SpringBoot自动装配
@SpringBootApplication
在我们开发SpringBoot项目时,项目的启动类往往被@SpringBootApplication 注解所修饰,那么这个注解到底有什么含义?
@SpringBootApplication 本质上是由三个注解组成的复合注解。
这三个注解分别是:@SpringBootConfiguration(本质是@Configuration)、@EnableAutoConfiguration、@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 {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
@Configuration
我们知道,目前 Spring bean 支持两种配置方式,一种是基于 xml 文件方式,另一种就是 JavaConfig 方式。
任何一个标注了@Configuration 的 Java 类都是一个 JavaConfig 配置类,而在配置类中,任何一个标注了@Bean 的方法,其返回值都会作为 Bean 定义注册到 Spring IOC 容器中,方法名默认为这个 bean 的 id。
@ComponentScan
@ComponentScan 相当于 xml 配置文件中的
用于标识类需要装配的注解有:@Component、@Service、@Controller、@Repository。
如果在@ComponentScan注解中没有指定具体路径,默认会扫描当前类所在的包。
@EnableAutoConfiguration
@EnableAutoConfiguration 同样是一个复合注解。
@AutoConfigurationPackage 的作用是:该注解修饰的类所在的 package 会作为自动装配的 package 管理。
@Import 可以把多个分开的配置合并到一个配置中。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
@Import
@Import 可以把多个分开的配置合并到一个配置中。
它可以配置三种不同的 class:
1、被@Configuration 修饰的 JavaConfig 类,将配置中被@Bean修饰的 bean 注入。
@Import(OtherConfig.class)
@Configuration
public class FirstConfig {
@Bean
public FirstBean firstBean() {
return new FirstBean();
}
}
@Configuration
public class OtherConfig {
@Bean
public OtherBean otherBean() {
return new OtherBean();
}
}
2、对实现了 ImportSelector 接口的class进行动态注入。
例如在前面提到的@EnableAutoConfiguration 注解中导入了 AutoConfigurationImportSelector 类,AutoConfigurationImportSelector 实现了 ImportSelector 接口的 selectImports 方法,在 selectImports 方法中可以按照自己的逻辑来装配bean,显然比第一种方式更加灵活了。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
......
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// annotationMetadata 注解的元信息
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 自动装配元数据:基于SPI机制,加载META-INF/spring.factories配置文件中需要自动装配的配置
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
// 返回需要装配的类的全路径名的数组
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
......
}
注:这里自动装配是基于Spring 的 SPI 机制,加载的是 META-INF/spring.factories 中配置的,且 key 为当前注解的全限定名(org.springframework.boot.autoconfigure.EnableAutoConfiguration)的所有 value,value 是需要加载的配置类,需要被 @Configuration 修饰,可以是多个,用逗号分隔。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
......
3、对实现了 ImportBeanDefinitionRegistrar 接口的 class 进行动态注入。
与上面的 AutoConfigurationImportSelector 相似,通过实现 ImportBeanDefinitionRegistrar 接口的 registerBeanDefinitions 方法来自定义注入 bean。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(FirstBean.class);
// bean 名称
String beanName = StringUtils.uncapitalize(FirstBean.class.getSimpleName());
// 注册一个 rootBeanDefinition
beanDefinitionRegistry.registerBeanDefinition(beanName, rootBeanDefinition);
}
}
还没有评论,来说两句吧...