spring boot自动配置原理

前言

阅读本文最好懂得spring源码,springmvc源码知识,以及零配置mvc+内嵌tomcat,servlet的spi机制
本文springboot源码版本说明:2.0.2.RELEASE
2.2.4.RELEASE的版本笔者也阅读过,相较于2.0.2来说更合理,更清晰,但是为了入门简单,还是选择早期版本

自动配置原理

@SpringBootApplication -->
@EnableAutoConfiguration -->
@Import({AutoConfigurationImportSelector.class})  -->
class AutoConfigurationImportSelector implements DeferredImportSelector{
	//读过spring源码的都知道ImportSelector会把selectImports返回的这些类都放到spring容器中
	//annotationMetadata是你带有@SpringBootApplication的类的注解描述
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
        	AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        	//exclude,excludeName
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //从META-INF/spring.factories文件读取
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            
            //去重
            configurations = this.removeDuplicates(configurations);
            
            //看看你的@SpringBootApplication的类有没有exclusions 
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            //这里过滤掉一些
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return StringUtils.toStringArray(configurations);
    }
    
}

总结一下原理就是从spring.factories文件读取自动配置类放到spring容器中,下面我们以一个自动配置类的实际例子看看

DispatcherServletAutoConfiguration

该类可以从spring.factories文件找到,
DispatcherServlet 是如何交给tomcat的,以及是如何与spring发生联系的,都在这里完成

//省略
@AutoConfigureAfter({ServletWebServerFactoryAutoConfiguration.class})
@EnableConfigurationProperties({ServerProperties.class})
//上面注解实质上只有@AutoConfigureAfter是boot新增的,其他注解都是spring本来有的或者在spring基础上做的扩展
public class DispatcherServletAutoConfiguration {
	
	/*******DispatcherServlet如何与spring发生联系********/
	//省略注解
    protected static class DispatcherServletConfiguration {
    	
        private final WebMvcProperties webMvcProperties;
        private final ServerProperties serverProperties;
		//DispatcherServletConfiguration 类只有一个有参构造器,在装配模式NO的情况下,determinCandidateConstructors的时候就可以确定该构造器,并且spring会把两个参数也给这个构造器
        public DispatcherServletConfiguration(WebMvcProperties webMvcProperties, ServerProperties serverProperties) {
            this.webMvcProperties = webMvcProperties;
            this.serverProperties = serverProperties;
        }
		
		//把DispatcherServlet 放入spring容器
		//在mvc零配置的时候我们是把appContext当作DispatcherServlet 的构造器入参传给DispatcherServlet 
		//boot中DispatcherServlet 是如何使用到appContext的呢?
		//其实是DispatcherServlet 的父类FrameworkServlet实现了ApplicationContextAware接口,spring在初始化的时候会回调此接口把自己的上下文环境传给他
        @Bean(name = {"dispatcherServlet"})
        public DispatcherServlet dispatcherServlet() {
            DispatcherServlet dispatcherServlet = new DispatcherServlet();
            //省略
            return dispatcherServlet;		
		}
		
		/*******DispatcherServlet如何与spring发生联系********/


		/*******DispatcherServlet 是如何交给tomcat的********/
	//省略
    protected static class DispatcherServletRegistrationConfiguration {
        //构造器原理同上
        
		//省略
		@Bean(name = {"dispatcherServletRegistration"})
		//方法参数是上面这个内部类@Bean放到spring容器的
        public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
        	//把dispatcherServlet和ServerProperties(其实是我们yml文件配置的server.address,server.port等属性)通过构造器给ServletRegistrationBean
            ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean(dispatcherServlet, new String[]{this.serverProperties.getServlet().getServletMapping()});
            //省略
            return registration;
        }
    }
    //到现在为止我们还不知道dispatcherServlet是如何交给tomcat的,注意ServletRegistrationBean这个类
    //他的顶级父类如下,一看ServletContextInitializer的onStartup我就懂了,servlet的spi机制而已
	public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
    public final void onStartup(ServletContext servletContext) throws ServletException {
        String description = this.getDescription();
        this.register(description, servletContext);
    }
    
    this.register(description, servletContext);//这行代码一直点下去会发现如下熟悉的代码
    {
     	String name = this.getServletName();
        return servletContext.addServlet(name, this.servlet);
    }
   
}
		/*******DispatcherServlet 是如何交给tomcat的********/
		
    }
	
}

@AutoConfigureAfter注解

DispatcherServletAutoConfiguration 要在ServletWebServerFactoryAutoConfiguration之后被spring实例化, @AutoConfigureAfter是如何控制这些类的顺序的

AutoConfigurationImportSelector类的如下代码

	//selectImports方法是由spring的初始化过程的invokeBeanFactoryPostProcessors方法回调的
	//该方法在这里就是给传过来的类排个序
	public Iterable<Entry> selectImports() {
            return (Iterable)this.sortAutoConfigurations().stream().map((importClassName) -> {
                return new Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);
            }).collect(Collectors.toList());
        }

        private List<String> sortAutoConfigurations() {
        		//省略
                return (new AutoConfigurationSorter(this.getMetadataReaderFactory(), autoConfigurationMetadata)).getInPriorityOrder(autoConfigurations);
            }
        }
        
public List<String> getInPriorityOrder(Collection<String> classNames) {
       	//省略
        //这里处理@AutoConfigureAfter注解
        List<String> orderedClassNames = this.sortByAnnotation(classes, orderedClassNames);
        return orderedClassNames;
    }

@EnableConfigurationProperties注解

@EnableConfigurationProperties({ServerProperties.class})  --->

@Import({EnableConfigurationPropertiesImportSelector.class})
public @interface EnableConfigurationProperties {
    Class<?>[] value() default {};
}


class EnableConfigurationPropertiesImportSelector implements ImportSelector {
    private static final String[] IMPORTS = new String[]{EnableConfigurationPropertiesImportSelector.ConfigurationPropertiesBeanRegistrar.class.getName()};

    public String[] selectImports(AnnotationMetadata metadata) {
        return IMPORTS;
    }
	
	//该类被EnableConfigurationPropertiesImportSelector import了
	//并且该类是ImportBeanDefinitionRegistrar ,会回调registerBeanDefinitions函数
    public static class ConfigurationPropertiesBeanRegistrar implements ImportBeanDefinitionRegistrar {
        public ConfigurationPropertiesBeanRegistrar() {
        }
		//metadata在这里是DispatcherServletAutoConfiguration的注解信息
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        	//拿到配置集合遍历
            this.getTypes(metadata).forEach((type) -> {
            	//为每一个配置文件注册bd
                this.register(registry, (ConfigurableListableBeanFactory)registry, type);
            });
        }
		
        private List<Class<?>> getTypes(AnnotationMetadata metadata) {
        	//只关心DispatcherServletAutoConfiguration的EnableConfigurationProperties的注解信息
            MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(EnableConfigurationProperties.class.getName(), false);
            //返回@EnableConfigurationProperties里的怕配置文件集合
            return this.collectClasses(attributes != null ? (List)attributes.get("value") : Collections.emptyList());
        }
    }
}


总结

自动配置原理:spring.factories 用ImportSelector放入spring容器
DispatcherServlet关联spring:@Bean将DS放入spring容器,ApplicationContextAware接口将spring的appContext给DS
DispatcherServlet关联tomcat:servlet3.0 SPI扩展机制