其实springboot的自动装配流程的关键点是通过@Import注解和对应的ImportSelector接口实现,加入相关需要导入的配置文件properties,配置文件中默认配置了需要导入的类,结合过滤规则和项目中是否导入相应的jar来确定是否需要导入相关的类。
首先介绍下@Import和ImportSelector接口的使用。注意:我这里是搭建的源码环境:
普通的pojo:
package mytest.person; public class Person2 { public Person2() { System.out.println("person2..."); } private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
实现接口ImportSelector:
package mytest.person; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; public class Person2Selector implements ImportSelector { public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 这里写上述的pojo Person2的全类名 return new String[]{"mytest.person.Person2"}; } private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
config的配置类:
package mytest; import mytest.person.Person2Selector; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @Import(Person2Selector.class) public class Config { }
测试客户端:
package mytest; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Client { public static void main(String[] args) { AnnotationConfigApplicationContext ap = new AnnotationConfigApplicationContext(Config.class); } }
执行后,打印出输出结果:
person2...
可以发现上述的步骤中,有一个非常灵活的地方:
public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 这里写上述的pojo Person2的全类名 return new String[]{"mytest.person.Person2"}; }
这里可以返回多个导入需要的实例的,只需要逗号隔开就行。所以可以通过配置文件读取的形式来读入。
简易版本的springboot的自动装配
创建一个autoconfig.properties文件:我这里只是加入需要导入的三个类:
mytest.MyAutoConfig=mytest.person.Person2,mytest.person.Person,mytest.person.Person3
加一个自动装配的注解:
package mytest; import org.springframework.context.annotation.Import; import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(MyAutoConfigurationImportSelector.class) public @interface MyAutoConfig { }
ImportSelector的具体实现:
package mytest; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.StringUtils; import java.io.FileInputStream; import java.util.Properties; public class MyAutoConfigurationImportSelector implements ImportSelector { public String[] selectImports(AnnotationMetadata importingClassMetadata) { Properties properties = new Properties(); try { // 读取对应的properties文件 properties.load(new FileInputStream("D:\\jeffchan\\other-meterial\\spring-framework\\spring-my-test\\src\\main\\resources\\META-INF\\autoconfig.properties")); }catch (Exception e){ System.out.println(e.getMessage()); } // 获取对应要装配的类配置信息 String objClass = (String)properties.get(MyAutoConfig.class.getName()); // 直接返回相应的结果。(其实就是将需要导入的类的全类名通过String数组的方式返回) return StringUtils.isEmpty(objClass) ? new String[0] : objClass.split(","); } }
相应的配置类需要修改一个:
package mytest; import org.springframework.context.annotation.Configuration; @Configuration @MyAutoConfig // 换成需要自动装配的注解 public class Config { }
还是执行打印结果:
发现需要导入的类都导入了:这里面最灵活的地方还是在ImportSelector接口的实现方法中,比如这里可以加入需要过滤的规则等一些限制。
上述的本质流程其实都是在产生beanDefination的流程。就是将需要导入的类放入到工厂类的beanDefinationMap中,然后在容器刷新的时候,会创建对应的实例放入到单例缓存池中。那么后续需要时候可以直接注入对应的实例。spring源码中还提供了对bean定义和bean实例修改的位置。比如BeanFactoryProcessor和BeanPostProcessor等接口可以灵活的修改bean定义和bean实例。