SpringBoot 自动配置的原理

1.一切起源之@SpringBootApplication

SpringBoot全局配置文件application.properties或application.yml可以配置哪些属性 参考文档

作为SpringBoot项目的入口,@SpringBootApplication起到了关键性的作用,另外需要注意的是SpringBoot所有关于自动配置的源码都在spring-boot-autoconfigure-x.x.x.x.jar里面

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration  //声明这是一个SpringBoot应用配置类
@EnableAutoConfiguration  //开启自动配置,尝试去自动配置你可能需要的的Beans
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) //开启包扫描,并且指定扫描的范围
public @interface SpringBootApplication {

我们可以看到上面SpringBoot开启了自动配置,使用了@EnableAutoConfiguration注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	/**
	 * Environment property that can be used to override when auto-configuration is
	 * enabled.
	 */
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

@EnableAutoConfiguration内部又通过@Import注解引入了EnableAutoConfigurationImportSelector资源,看名字EnableAutoConfigurationImportSelector大概是开启了自动配置引入选择器,那它到底是在选择什么呢?

public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
    public EnableAutoConfigurationImportSelector() {
    }

    protected boolean isEnabled(AnnotationMetadata metadata) {
        return this.getClass().equals(EnableAutoConfigurationImportSelector.class) ? (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true) : true;
    }
}

 点击进入后发现它继承自AutoConfigurationImportSelector选择器

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    private static final String[] NO_IMPORTS = new String[0];
    private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
    private ConfigurableListableBeanFactory beanFactory;
    private Environment environment;
    private ClassLoader beanClassLoader;
    private ResourceLoader resourceLoader;

    public AutoConfigurationImportSelector() {
    }

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            try {
                //加载自动配置元数据,即加载自动配置所有组件相关的基础类
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); 
                AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                //获取候选的配置,即哪些自动配置需要生效
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); 
                //移除重复的配置
                //内部代码使用一个ArrayList去构建一个LinkedHashSet,再将LinkedHashSet转为ArrayList就实现了去重功能,因为LinkedHashSet元素唯一
                configurations = this.removeDuplicates(configurations); 
                configurations = this.sort(configurations, autoConfigurationMetadata);
                //移除配置,spring.auto.exclude='some component exclude'
                Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                this.checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = this.filter(configurations, autoConfigurationMetadata);
                this.fireAutoConfigurationImportEvents(configurations, exclusions);
                return (String[])configurations.toArray(new String[configurations.size()]);
            } catch (IOException var6) {
                throw new IllegalStateException(var6);
            }
        }
    }
     //加载候选的配置
     protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        //所有候选的自动配置都通过SpringFactoriesLoader加载
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
    }

而在AutoConfigurationImportSelector类中我们发现存在selectImports方法,看名字我们大概可以猜到这里回去动态的选择导某些资源。而我们查看方法内部发现,

//加载器
public abstract class SpringFactoriesLoader {
    private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
    //资源加载位置,当前org.springframework.boot.autoconfigure项目下的META-INF/spring.factories
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
   
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();

        try {
            //加载META-INF/spring.factories的所有配置
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            ArrayList result = new ArrayList();

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                String factoryClassNames = properties.getProperty(factoryClassName);
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }

            return result;
        } catch (IOException var8) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
        }
    }

spring.factories文件内部定义了所有的XXXAutoConfiguration配置类,因此加载过程会将所有的配置类加载到Spring容器中

此时对于spring.factories里面的所有配置,为了更好的讲解。我们一模板引擎Thymeleaf继续向大家分析Springboot自动配置。

spring.factories里面存在如下内容:

org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\

我们继续进入ThymeleafAutoConfiguration查看内容

@Configuration //声明这是个配置类
@EnableConfigurationProperties({ThymeleafProperties.class}) //引入ThymeleafProperties类里面的配置
@ConditionalOnClass({SpringTemplateEngine.class})
@AutoConfigureAfter({WebMvcAutoConfiguration.class}) //在WebMvcAutoConfiguration加载之后再加载此类
public class ThymeleafAutoConfiguration {
    public ThymeleafAutoConfiguration() {
    }

    @Configuration
    @ConditionalOnWebApplication //web项目条件下启用
    protected static class ThymeleafResourceHandlingConfig {
        protected ThymeleafResourceHandlingConfig() {
        }

        @Bean
        @ConditionalOnMissingBean
        @ConditionalOnEnabledResourceChain
        public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
            return new ResourceUrlEncodingFilter();
        }
    }

    @Configuration
    @ConditionalOnJava(JavaVersion.EIGHT)
    @ConditionalOnClass({Java8TimeDialect.class})
    protected static class ThymeleafJava8TimeDialect {
        protected ThymeleafJava8TimeDialect() {
        }

        @Bean
        @ConditionalOnMissingBean
        public Java8TimeDialect java8TimeDialect() {
            return new Java8TimeDialect();
        }
    }

@ConfigurationProperties(prefix = "spring.thymeleaf" )//定义自动以配置前缀
public class ThymeleafProperties {
    private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8");
    private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html");
    public static final String DEFAULT_PREFIX = "classpath:/templates/"; //页面资源默认路径
    public static final String DEFAULT_SUFFIX = ".html"; //页面资源默认后缀名
    private boolean checkTemplate = true;
    private boolean checkTemplateLocation = true;
    private String prefix = "classpath:/templates/";
    private String suffix = ".html";
    private String mode = "HTML5";
    private Charset encoding;
    private MimeType contentType;
    private boolean cache;
    private Integer templateResolverOrder;
    private String[] viewNames;
    private String[] excludedViewNames;
    private boolean enabled;

至此,SpringBoot自动配置结束,最后上一张自动配置工作原理图

how-spring-boot-autoconfigure-works

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值