上篇文章:
目录
2.2 @SpringBootConfiguration注解
2.4 @EnableAutoConfiguration注解
1 Spring加载Bean
在使用Spring以及SpringBoot时,会发现很多时候并没有声明某个类需要Spring来管理,就可以直接注入到程序中使用,这是因为Spring和SpringBoot为我们已经做好了一些Bean的加载:
通常Spring会识别到@SpringBootApplication注解所在的目录下的包和类以及对应的注解,但是如果不是这个路径下有一些代码(第三方jar包会在类加载时期合并到src/main/java路径下),如果不做一些配置,Spring就无法识别到这些路径下的代码。在Spring中有两种方式可以实现加载Bean时的路径扫描:
1.1 @ComponentScan注解
使用方法在@SpringBootApplication所在的启动类添加@ComponentScan("全路径的包名"),可以让Spring扫描该路径下的所有内容。
缺点:扫描范围太大,会扫描大量不需要管理的依赖项。
1.2 @Import注解
使用方法在@SpringBootApplication所在的启动类添加@Import("类.class"),可以让Spring扫描该类。
缺点:扫描范围太小,多个类需要管理就需要写多个类名。
上述两种方式都有缺点,并且程序员也不知道第三方依赖哪些类需要被管理。但是如果第三方依赖可以提供一个需要管理的清单,把两个注解结合一下,在通过一个文件管理需要扫描的路径,由@Import来导入这个文件,就可以更灵活方便的完成Bean的扫描和加载了。SpringBoot就是采用这种方式:
2 SpringBoot自动装配
2.1 @SpringBootApplication注解
SpringBoot对Spring进行了封装,在@SpringBootApplication注解的源码中:
@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 {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "nameGenerator"
)
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
其中,@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan注解最关键。
2.2 @SpringBootConfiguration注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
@SpringBootConfiguration的元注解是@Configuration和@Indexed注解(索引,加速启动),即让启动类被Spring扫描到。
2.3 @ComponentScan注解
@ComponentScan注解用来进行路径过滤和自定义的路径扫描,可以通过basePackageClasses或basePackages来定义要扫描的特定包,如果没有定义特定的包,将从声明该注解的类的包开始扫描,这也是为什么SpringBoot项目声明的注解类必须要在启动类的目录下。
2.4 @EnableAutoConfiguration注解
@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 {};
}
最关键的注解是@EnableAutoConfiguration,负责第三方依赖的配置类(@Bean注解的方法获得第三方依赖的对象)和组件(比如MyBatis的@Mapper注解不属于Spring)的扫描,其中包含两个关键注解:@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
@AutoConfigurationPackage注解把启动类所在的包下面所有的组件都注入到Spring容器中。
而@Import注解负责把AutoConfigurationImportSelector类导入进来,在该类中的方法:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
该方法通过Spring工厂的SpringFactoriesLoader.loadFactoryNames方法来自动加载配置类,扫描路径META-INF/spring.factories,该文件会给出第三方依赖需要加载的Bean的路径,但是需要按@ConditionalOnClass注解(需要注入)和@ConditionalOnMissingBean注解(忽略该Bean)来选择注入或者忽略。