自动装配可以理解为:通过注解或者一些简单的配置就能在SpringBoot的帮助下实现某块功能。
SpringBoot是如何实现自动装配的呢?
SpringBoot 的核心注解 SpringBootApplication。
// 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 {
XXXXXX
}
// SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //SpringBootConfiguration实际上是一个配置类
public @interface SpringBootConfiguration {
}
@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 {};
}
SpringBootApplication可以看作是Configuration,EnableAutoConfiguration,ComponentScan的集合。根据 SpringBoot 官网,这三个注解的作用分别是:
- Configuration:允许在上下文中注册额外的 bean 或导入其他配置类
- EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
- ComponentScan:扫描被@Component (@Service、@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 。同时特可以自定义不扫描某些 bean,如下图所示,容器中将排除TypeExcludeFilter和AutoConfigurationExcludeFilter
@EnableAutoConfiguration 是实现自动装配的重要注解,我们以这个注解入手。
@EnableAutoConfiguration:实现自动装配的核心注解
EnableAutoConfiguration只是一个组合注解,自动装配的核心功能的实现类其实是AutoConfigurationImportSelector类。
··
问:@Import注解有什么作用?(知识补充,不想了解的可以跳过!)
在Spring框架中到处都可以见到这个注解。那么这个注解的作用到底是什么呢?
-
@Import 注解提供了类似 @Bean 注解的功能。
-
从代码的注释中可以看到:Import可以配合 Configuration, ImportSelector, ImportBeanDefinitionRegistrar 来使用,也可以用于一个普通类的导入。
-
使用方式有三种:
普通类注入
public class NorMal {
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
import作用于某个类上
@Configuration
@Import(NorMal.class)
public class MyConfigure {
}
这样NorMal就相当于注入到Spring容器中了。
实现了ImportSelector接口
/**
* 批量导入对象到容器的类
*/
public class MySelectImport implements ImportSelector {
//返回需要导入Spring容器中的所有对象全限定名称
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
String[] strings = new String[]{
"com.zhong.pojo.NorMal",
"com.zhong.pojo.User"
};
return strings;
}
}
@SpringBootApplication
@Import(MySelectImport.class)
public class Springboot08ImportAnnoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Springboot08ImportAnnoApplication.class, args);
NorMal normal = context.getBean(NorMal.class);
System.out.println(normal);
}
}
实现了ImportBeanDefinitionRegistrar
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(NorMal.class);
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
registry.registerBeanDefinition("normal", beanDefinition);
}
//或者 使用如下的方法也可以,自动生成beanName
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(NorMal.class);
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
String beanName = importBeanNameGenerator.generateBeanName(beanDefinition, registry);
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
@Import就不再详细介绍了,感兴趣的可以看看这位大佬的文章。
AutoConfigurationImportSelector:加载自动装配类
可以看出AutoConfigurationImportSelector实现了DeferredImportSelector接口,而DeferredImportSelector接口继承ImportSelector类。也就是说AutoConfigurationImportSelector也实现了selectImports方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类都会被加载到IOC容器中。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
}
public interface DeferredImportSelector extends ImportSelector {
}
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
在AutoConfigurationImportSelector中可以看到selectImports()
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 判断自动装配是否开启
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 获取所有需要装配的bean
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
重点关注getAutoConfigurationEntry(),这个方法主要负责加载自动配置类的。
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
//1
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 2
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 3
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 4
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
1、判断自动装配是否开启。默认spring.boot.enableautoconfiguration=true,可在 application.properties 或 application.yml中设置
2、用于获取EnableAutoConfiguration注解中的 exclude 和 excludeName。(获取需要排除的类)
3、读取需要自动装配的所有配置类,读取META-INF/spring.factories
不光是这个依赖下的META-INF/spring.factories被读取到,所有 Spring Boot Starter 下的META-INF/spring.factories都会被读取到。
4、“spring.factories中这么多配置,每次启动都要全部加载么?”。
很明显,这是不现实的。我们 debug 到后面你会发现,configurations 的值变小了。
因为,这一步有经历了一遍筛选,@ConditionalOnXXX 中的所有条件都满足,该类才会生效
@Configuration
// 检查相关的类:RabbitTemplate 和 Channel是否存在
// 存在才会加载
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
}
总结
Spring Boot 通过@EnableAutoConfiguration开启自动装配,通过 SpringFactoriesLoader 最终加载META-INF/spring.factories中的自动配置类实现自动装配,自动配置类其实就是通过@Conditional按需加载的配置类,想要其生效必须引入spring-boot-starter-xxx包实现起步依赖