近期由于有些面试将平时零散的知识进行梳理下并做记录,欢迎大家一起交流
简介
SpringBoot一个最核心思想是:约定大于配置,这种虽然降低了配置的灵活性
但是却简化了应用的搭建与开发,因为这种约定从实现角度看就是SpringBoot提供了大量的默认配置参数本次我们来聊聊 @EnableAutoConfiguration的作用。
@EnableAutoConfiguration是启用Spring的自动装配的关键注解
@EnableAutoConfiguration 注解实现原理
通过源码会发现该注解会@Import(AutoConfigurationImportSelector.class),AutoConfigurationImportSelector这个类是实现自动装配的关键
根据@Import装配类的特性,并且 AutoConfigurationImportSelector 实现自 ImportSelector 类,所以知道它可以根据 AutoConfigurationImportSelector 的实现在 selectImports 方法返回的数组(类的全类名)去自动装配bean到Spring IOC容器中。此注解相当于xml配置中 import 标签
@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 {};
}
AutoConfigurationImportSelector 分析
进入AutoConfigurationImportSelector类通过读取META-INF/spring.factories文件中key为EnableAutoConfiguration完成目标类加载,以mybatis为例应该为
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
至此默认配置已经加载到spring中,而用户只需要根据实际情况修改对应的组件的配置参数,以mybatis为例其参数均有@EnableConfigurationProperties({MybatisProperties.class}) 提供
@Import 注解何时被解析
而@Import 注解被容器解析时才会真正调用实现了ImportSelector接口的类,这个过程正是在refresh方法的invokeBeanFactoryPostProcessors中完成
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
....省略部分代码有兴趣的可以直接查看源码
}
}
继续分析invokeBeanFactoryPostProcessors该方法主要通过beanfactory来加载一些处理器类,通过PostProcessorRegistrationDelegate工具类调用invokeBeanFactoryPostProcessors
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean("loadTimeWeaver")) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
进入invokeBeanFactoryPostProcessors发现该方法将会加载实现了BeanDefinitionRegistryPostProcessor注册处理器的实现类其中处理import注解的处理器为ConfigurationClassPostProcessor,此处理器会处理四个注解:@PropertySource, @ComponentScan, @Import, @ImportResource
接下来分析下ConfigurationClassPostProcessor中processConfigBeanDefinitions方法其中ConfigurationClassParser类将会完成上述四个注解的解析工作
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
....省略部分代码
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates);
HashSet alreadyParsed = new HashSet(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
....省略部分代码
}
}
最后我们看下ConfigurationClassParser类中doProcessConfigurationClass方法,代码注释中已经明确指明了每一个注解的解析逻辑,这里不再展开分析
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
}
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotation
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurs
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
总结
EnableAutoConfiguration与starter配合完美的实现了自动装配,大大提升了搭建与开发的效率