spring Boot自动装配2
简介
springboot的自动装配实际上就是为了从spring.factories中获取到对应需要进行自动装配的类,并生成相应的Bean对象,然后将它们交给spring容器来处理
@SpringBootApplication
这个注解是springboot启动类上的一个注解,是一个组合注解,也就是由其他注解组合起来,它的主要作用就是标记说明这个类是springboot的主配置类,springboot应该运行这个类里面的main()方法来启动程序
这个注解主要由三个子注解构成
- @springbootConfiguration
- @EnableAotuConfiguration
- @ComponentScan
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
@SpringBootConfiguration
这个注解包含了@Configuration,@Configuration里面又包含了一个@Component注解,也就是说,这个朱姐标注再哪个类上,就表示当前这个类是一个配置类,而配置类也是spring容器中的组件
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
@EnableAutoConfiguration
这个注解是开启自动配置的功能,里面包含了两个注解
- @AutoConfigurationPackage
- @Import(AutoConfigurationImportSelector.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage
这个注解的作用就是将主配置类所在包里面所有的组件扫描并加载到spring容器中,这也就是为什么我嗯在利用springboot进行开发的时候,无论controller还是service的路径都是主配置类同级或者次级的原因
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
@Import(AutoConfigurationImportSelector.class)
上一个注解我们把所有组件都加载到了容器里面,这个注解就是将需要自动装配的类以全类名的方式返回
@ComponentScan
这个注解的作用是扫描当前包以及子包
@ComponentScan
这个注解的作用就是扫描当前包及子包的注解
springboot自动装配的流程
- 1、在springboot启动的时候会创建一个SpringApplication对象,在对象的构造方法里面会进行一些参数的初始化工作,最主要的是判断当前应用程序的类型以及设置初始化器以及监听器,并在这个过程中会加载整个应用程序的spring.factories文件,将文件中的内容放到缓存当中,方便后续获取;
2、SpringApplication对象创建完成之后会执行run()方法来完成整个应用程序的启动,启动的过程中有两个最主要的方法prepareContext()和refreshContext(),在这两个方法中完成了自动装配的核心功能,在run()方法里还执行了一些包括上下文对象的创建,打印banner图,异常报告期的准备等各个准备工作,方便后续进行调用;
3、在prepareContext()中主要完成的是对上下文对象的初始化操作,包括属性的设置,比如设置环境变量。在整个过程中有一个load()方法,它主要是完成一件事,那就是将启动类作为一个beanDefinition注册到registry,方便后续在进行BeanFactoryPostProcessor调用执行的时候,可以找到对应执行的主类,来完成对@SpringBootApplication、@EnableAutoConfiguration等注解的解析工作;
4、在refreshContext()方法中会进行整个容器的刷新过程,会调用spring中的refresh()方法,refresh()方法中有13个非常关键的方法,来完成整个应用程序的启动。而在自动装配过程中,会调用的关键的一个方法就是invokeBeanFactoryPostProcessors()方法,在这个方法中主要是对ConfigurationClassPostProcessor类的处理,这个类是BFPP(BeanFactoryPostProcessor)的子类,因为实现了BDRPP(BeanDefinitionRegistryPostProcessor)接口,在调用的时候会先调用BDRPP中的postProcessBeanDefinitionRegistry()方法,然后再调用BFPP中的postProcessBeanFactory()方法,在执行postProcessBeanDefinitionRegistry()方法的时候会解析处理各种的注解,包含@PropertySource、@ComponentScan、@Bean、@Import等注解,最主要的是对@Import注解的解析;
5、在解析@Import注解的时候,会有一个getImport()方法,从主类开始递归解析注解,把所有包含@Import的注解都解析到,然后在processImport()方法中对import的类进行分类,例如AutoConfigurationImportSelect归属于ImportSelect的子类,在后续的过程中会调用DeferredImportSelectorHandler类里面的process方法,来完成整个EnableAutoConfiguration的加载。
总结
在springboot应用里,只需要在启动上@SpringbootApplication就可以实现自动装配
@springbootApplication是一个复核注解,真正实现自动装配功能的是@EnableAotuConfiguration注解,自动装配的实现主要依靠三个核心的关键技术
第一个
引入stater启动依赖组件的时候,这个组件里面必须要包含@Configuration配置类,这个配置类里面需要通过@bean注解去声明,需要装配到IOC容器里面的bean对象,
第二个
这个配置类是放在第三方的jar包里面,然后通过springboot中约定优于配置的这样一个理念,去怕这个配置类的全路径放在classpath:/META-INF/spring.factories
文件里,这样springboot就可以知道,第三方jar包里这个配置类的位置,这个步骤主要是用到了spring里面的SrpingFactoryLoader来完成的.
第三个
springboot拿到所有第三方jar包里面声明的配置类以后,再通过spring提供的importSelector这样一个接口来实现对这些配置类的动态加载
他可以让开发人员把更多注意力放在对业务代码的编写上而不需要关心和业务无关的配置
其实自动装配的思想,在springframework3.X里面的@Enable注解就已经有了实现的雏形,@Enable注解是一个模块驱动的意思,也就是说我们只需要增加@Eable注解就能自动打开某个功能,而不需要针对这个功能去做bean的配置