转载

springboot自动配置原理

1.java configuration及WebApplicationInitializer

The following example of the Java configuration registers and initializes the DispatcherServlet, which is auto-detected by the Servlet container (see Servlet Config)-- 来自spring官网

//首先实现WebApplicationInitializer接口中onStartUp()方法,容器启动时候会调用该方法
public class MyWebApplicationInitializer implements WebApplicationInitializer {

    //容器为什么会调用onStartup()方法
    //servlet 3.0 提出了spi规范,一个项目自定义的WebApplicationInitializer如果想被容器调用(tomcat),
    //只需要在项目中meta-inf/service目录下定义一个文件,
    //文件名必须为javax.servlet.ServletContainerInitializer,
    //该名字为一个接口,文件内容为实现该接口的类,
    //Spring实现该接口的类的名称为org.springframework.web.SpringServletContainerInitializer,spring项目中该文件内容也为该实现类的全路径。
    //该接口为servlet中定义的,容器实现该接口,Spring也实现该接口,以此达到容器(tomcat)启动,
    //能够调用到自定义的WebApplicationInitializer
    

    @Override
    public void onStartup(ServletContext servletCxt) {
        
        // Load Spring web application configuration
        //spring 容器对象
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        //AppConfig类为配置
        ac.register(AppConfig.class);
        ac.refresh();
        //spring容器配置完成
        

        // Create and register the DispatcherServlet
        //设置spring mvc
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        //设置为优先启动
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}

spring实现servlet3.0规范中,meta-inf/services目录下的文件

springboot自动配置原理

实现的接口

springboot自动配置原理

spring 对servlet3.0规范中onStartup()方法的实现

@HandlesTypes(WebApplicationInitializer.class)
//该注解将保证容器(tomcat)首先扫描WebApplicationInitializer接口(该接口可以自定义,spring使用的是WebApplicationInitializer接口)的所有实现类,然后赋值到onStartup()方法参数的webAppInitializerClasses set集合中。
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
            throws ServletException {

        List<WebApplicationInitializer> initializers = new LinkedList<>();
        //存放new 出来的对象

        if (webAppInitializerClasses != null) {
            for (Class<?> waiClass : webAppInitializerClasses) {
                // Be defensive: Some servlet containers provide us with invalid classes,
                // no matter what @HandlesTypes says...
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                        WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer)
                                ReflectionUtils.accessibleConstructor(waiClass).newInstance());
                    }
                    catch (Throwable ex) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                    }
                }
            }
        }

        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
            return;
        }

        servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
        AnnotationAwareOrderComparator.sort(initializers);
        
        //for循环,依次调用new 出来的对象,调用其onStartup()方法
        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
    }

}

2.Springboot自动配置原理

Springboot项目启动application

package com.xiayu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication//Springboot项目启动注解
@EnableConfigurationProperties
public class XiayuRoutingDatasourceApplication {
    public static void main(String[] args) {
        SpringApplication.run(XiayuRoutingDatasourceApplication.class);
    }
}

@SpringbootApplication注解

@Target(ElementType.TYPE)//定义该注解作用于什么上面,是类,接口,方法,方法参数等等上面,ElementType.TYPE定义在类和接口上面
@Retention(RetentionPolicy.RUNTIME)//注解在运行时生效
@Documented 声明该注解会被在javadoc文档信息中展示
@Inherited 该注解保证注解声明在一个父类上后,子类也会有相同的属性
//上述都是java自带的注解
@SpringBootConfiguration//该注解仅仅声明配置类
@EnableAutoConfiguration//Springboot的核心配置注解
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

@EnableAutoConfiguration注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
//配置自动扫描包,扫描当前注解所在的目录,如果当前配置在com.xiayu包下,就相当于
//配置了@ComponentScan("com.xiayu")
//这也是为什么Springboot项目启动类与各个模块同级
@Import(AutoConfigurationImportSelector.class)
//真正的核心自动配置类,加载AutoConfigurationImportSelector类到spring容器中,并作为bean管理
public @interface EnableAutoConfiguration {

典型的Springboot项目目录结构

springboot自动配置原理

@Import加载的类:AutoConfigurationImportSelector,其实现了DeferredImportSelector接口

public class AutoConfigurationImportSelector
        implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
        BeanFactoryAware, EnvironmentAware, Ordered {

DeferredImportSelector接口

package org.springframework.context.annotation;

public interface DeferredImportSelector extends ImportSelector {
}

package org.springframework.context.annotation;
-------------------------------------------------------
import org.springframework.core.type.AnnotationMetadata;

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
    //该接口在AutoConfigurationImportSelector类中有具体的实现
}

AutoConfigurationImportSelector类中 selectImports方法的具体实现

//返回自动配置类的路径,交由spring管理,完成整个bean生命周期
@Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        try {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AnnotationAttributes attributes = getAttributes(annotationMetadata);
            //从meta-inf/spring.factories文件中加载已经定义的配置类的全路径
            List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);
            configurations = removeDuplicates(configurations);
            configurations = sort(configurations, autoConfigurationMetadata);
            Set<String> exclusions = getExclusions(annotationMetadata, attributes);
            checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = filter(configurations, autoConfigurationMetadata);
            fireAutoConfigurationImportEvents(configurations, exclusions);
            return StringUtils.toStringArray(configurations);
        }
        catch (IOException ex) {
            throw new IllegalStateException(ex);
        }
    }

getCandidateConfigurations方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
                getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
                //返回List<String>,list中包含自动配置类的全路径
        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;
    }

在SpringFactoriesLoader类中定义了自动配置类全路径保存的文件

public abstract class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
    private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();

meta-inf/spring.factories文件,如果存在多个该文件,也会被扫描到,扫描到后就会将这些类通过@Import注解加载到spring容器中,被Spring进行管理。

springboot自动配置原理

原文  https://segmentfault.com/a/1190000021962377
正文到此结束
Loading...