spring boot自動配置原理

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擴展機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章