精华内容
下载资源
问答
  • SpringBoot自动配置的原理及实现

    万次阅读 多人点赞 2018-11-13 14:22:34
    SpringBoot自动配置的实现原理 SpringBoot的核心就是自动配置,自动配置又是基于条件判断来配置Bean。关于自动配置的源码在spring-boot-autoconfigure-2.0.3.RELEASE.jar 回顾配置属性 在通常需要我们在property中...

    整理日志

    2018-11-13 写blog
    2020-11-13 参考其他blog添加流程图
    2021-3-17 评论区说跨域的包没引入,这是我当时写脚手架时加的,对于当前blog没什么用,我加上了注释 //,免得被喷

    SpringBoot自动配置的实现原理

    SpringBoot的核心就是自动配置,自动配置又是基于条件判断来配置Bean。关于自动配置的源码在spring-boot-autoconfigure-2.0.3.RELEASE.jar

    在这里插入图片描述

    回顾配置属性

    在通常需要我们在property中配置信息时,通常使用@ConfigurationProperties(pefix=“前缀”)注解的方式从配置文件中获取配置,如下:

    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    @RestController
    @ConfigurationProperties(prefix = "test")
    //@Component //如果这里添加了注解那么在自动配置类的时候就不用添加@enableConfigurationProperties(HelloProperties.class)注解.
    public class Demo {
    
     
    	
        private String msg="default";//现在我们在配置文件写hello.msg=world,因为简单就不再展示;如果那么默认为default.
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
    	}
    	
    	
    	@RequestMapping("/msg")
    	public Object index(){
    		return this.msg;
    	}
    
    }
    
    

    application.yml中配置信息

    test:
      msg: bamboo
    

    访问url获取配置信息返回的值
    http://localhost:8080/msg

    如果把application.yml中的配置信息注释掉则默认使用default值,否则使用配置信息中的值,以上便是普通配置方式

    解析

    SpringBoot运行原理
    先看@SpringBootApplication

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = {
            @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
            @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
    ...
    }
    

    主要关注的几个注解如下
    @SpringBootConfiguration:标记当前类为配置类
    @EnableAutoConfiguration:开启自动配置
    @ComponentScan:扫描主类所在的同级包以及下级包里的Bean
    关键是@EnableAutoConfiguration

    @Target(ElementType.TYPE)
    @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 {};
    }
    
    

    最关键的要属@Import(EnableAutoConfigurationImportSelector.class),借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器:通过@Import(AutoConfigurationImportSelector.class)导入的配置功能,
    AutoConfigurationImportSelector中的方法getCandidateConfigurations,得到待配置的class的类名集合,这个集合就是所有需要进行自动配置的类,而是是否配置的关键在于META-INF/spring.factories文件中是否存在该配置信息

      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;
        }
    

    打开,如下图可以看到所有需要配置的类全路径都在文件中,每行一个配置,多个类名逗号分隔,而\表示忽略换行
    在这里插入图片描述

    在这里插入图片描述
    整个流程如上图所示

    样例讲解

    以SpringApplicationAdminJmxAutoConfiguration类来看其主要构成部分

    @Configuration
    @AutoConfigureAfter({JmxAutoConfiguration.class}) //配置完JmxAutoConfiguration后再配置当前类型
    // spring.application.admin为前缀,属性为enabled,有值时为true,没有匹配到则为false:以上条件为true则实例化,否则不是实例化
    @ConditionalOnProperty( prefix = "spring.application.admin", value = {"enabled"}, havingValue = "true",  matchIfMissing = false)
    public class SpringApplicationAdminJmxAutoConfiguration 
    
    

    都能看到各种各样的条件判断注解,满足条件时就加载这个Bean并实例化
    此类的条件注解是:@ConditionalOnProperty

    @ConditionalOnBean:当容器里有指定Bean的条件下
    @ConditionalOnClass:当类路径下有指定的类的条件下
    @ConditionalOnExpression:基于SpEL表达式为true的时候作为判断条件才去实例化
    @ConditionalOnJava:基于JVM版本作为判断条件
    @ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
    @ConditionalOnMissingBean:当容器里没有指定Bean的情况下
    @ConditionalOnMissingClass:当容器里没有指定类的情况下
    @ConditionalOnWebApplication:当前项目时Web项目的条件下
    @ConditionalOnNotWebApplication:当前项目不是Web项目的条件下
    @ConditionalOnProperty:指定的属性是否有指定的值
    @ConditionalOnResource:类路径是否有指定的值
    @ConditionalOnOnSingleCandidate:当指定Bean在容器中只有一个,或者有多个但是指定首选的Bean
    这些注解都组合了@Conditional注解,只是使用了不同的条件组合最后为true时才会去实例化需要实例化的类,否则忽略
    这种spring4.X带来的动态组合很容易后期配置,从而避免了硬编码,使配置信息更加灵活多变,同时也避免了不必要的意外异常报错。使用的人只要知道配置的条件即可也不用去阅读源码,方便快捷,这也是sprignboot快捷方式带来的好处

    参考HttpEncodingAutoConfiguration配置信息如下

    @Configuration
    @EnableConfigurationProperties(HttpEncodingProperties.class)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    @ConditionalOnClass(CharacterEncodingFilter.class)
    @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
    public class HttpEncodingAutoConfiguration {
    

    @Configuration:标明为配置类
    @EnableConfigurationProperties(HttpEncodingProperties.class)声明开启属性注入
    @ConditionalOnClass(CharacterEncodingFilter.class)当CharacterEncodingFilter在类路径的条件下
    @ConditionalOnProperty(prefix = “spring.http.encoding”, value = “enabled”, matchIfMissing = true)当spring.http.encoding=enabled的情况下,如果没有设置则默认为true,即条件符合
    @ConditionalOnMissingBean当容器中没有这个Bean时新建Bean

    案例扩展

    
    /**
     * @author wuweifeng wrote on 2017/11/25.
     * 根据部署环境动态决定是否启用eureka
     线上的环境开启eureka,就在application-prod.yml里配上open.eureka=true,
     其他的yml什么也不写就行了。这样本地启动时就相当于没有开启EnableDiscoveryClient
     */
    @Component
    @ConditionalOnProperty(value = "open.eureka")
    @EnableDiscoveryClient
    public class JudgeEnableDiscoveryClient 
    

    自己实现一个自己的自动配置

    项目

    xm-common:普通jar项目
    - src/main
        java
            BambooServer.java  需要被实例化的服务类
            BambooServerProperties.java 配置信息属性类
            BmbooServiceAutoConfiguration.java 自动配置类
        resources
            META-INF/spring.factories 配置自动配置的属性文件
    demo:普通springboot-web项目
    

    需要实例化的服务类

    public class BambooServer {
        private String name;
    
        public String sayServerName(){
            return "I'm " + name + "! ";
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    }
    

    配置信息对应的属性映射类,需要pom中加入spring-boot-starter依赖

    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    @ConfigurationProperties(prefix = "bamboo")
    public class BambooServerProperties {
    
        private static final String NAME = "bamboo_server0";
    
        private String name = NAME;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    自动配置文件

    /**
     * Author: bamboo
     * Time: 2018/11/25/025
     * Describe: 自动配置类
     * 根据条件判断是否要自动配置,创建Bean
     */
    @Configuration
    @EnableConfigurationProperties(BambooServerProperties.class)
    @ConditionalOnClass(BambooServer.class)//判断BambooServer这个类在类路径中是否存在
    @ConditionalOnProperty(prefix = "bamboo",value = "enabled",matchIfMissing = true)
    public class BmbooServiceAutoConfiguration {
    
        @Autowired
        private BambooServerProperties mistraServiceProperties;
    
        @Bean(name = "bambooServer")
        @ConditionalOnMissingBean(BambooServer.class)//当容器中没有这个Bean时(BambooServer)就自动配置这个Bean,Bean的参数来自于BambooServerProperties
        public BambooServer mistraService(){
            BambooServer mistraService = new BambooServer();
            mistraService.setName(mistraServiceProperties.getName());
            return mistraService;
        }
    }
    

    在创建如下路径文件src/main/resources/META-INF/spring.factories

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.bamboo.common.autoconfigure.bamboo.BmbooServiceAutoConfiguration
    

    必须是自动配置类的全路径

    mvn install 该项目

    创建一个springboot-mvc项目pom依赖上面的jar

    @SpringBootApplication
    @RestController
    //@Import(value = {CorsConfig.class, LogFilter.class}) //跨域,接口访问请求日志
    public class DemoApplication {
    	@Autowired
    	private BambooServer bmbooService;
    
    	public static void main(String[] args) {
    		SpringApplication.run(DemoApplication.class, args);
    	}
    
    	@RequestMapping("/")
    	public Object index(){
    		return "helll demo"+bmbooService.getName()+DateUtils.getDate();
    	}
    }
    

    http://localhost:8080/则返回当前服务的默认值

    在applicaton.yml中加,重启刷新则会更新为如下信息

    bamboo:
      name: 测试服务
    

    总结图

    在这里插入图片描述
    SpringBoot自动化配置关键组件关系图
    mybatis-spring-boot-starter、spring-boot-starter-web等组件的META-INF文件下均含有spring.factories文件,自动配置模块中,SpringFactoriesLoader收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的bean。

    参考blog

    https://www.cnblogs.com/xiaoxi/p/7999885.html

    展开全文
  • 文章目录SpringBoot自动配置的原理自动配置的即插即用原理自动配置的约定优先的原理 SpringBoot自动配置的原理 自动配置都是从@SpringBootApplication引入的,而他又包含了三个非常重要的注解 @Configuration @...

    SpringBoot自动配置的原理

    自动配置都是从@SpringBootApplication引入的,而他又包含了三个非常重要的注解

    @Configuration 
    @EnableAutoConfiguration //启用自动配置,导入一类自动配置的类定义
    @ComponentScan //扫描和加载应用中的一些自定义的类
    

    自动配置的即插即用原理

    注解EnableAutoConfiguration最终会导入一个自动配置的类列表.列表中的自动配置类很多,这些配置类大都将被导入,并处于备用状态中,这如同电器中准备了一些插槽一样,实现了即插即用的原理.


    这样,当项目中引入了相关的包时候,相关的==插槽==(功能)将被启用 > 比如说:
    > 如果在maven中配置了redis的话,那么redis的功能将被启用,这时候启动应用,程序将尝试==读==取有关redis的==配置信息==

    自动配置的约定优先的原理

    概念:

    想要加载一个类时候
    若存在
    不存在
    开始
    读取项目中的配置
    启用相关配置
    启用配置的默认值
    展开全文
  • 关于自动配置的源码在spring-boot-autoconfigure-2.0.0.RELEASE.jar 在讲springBoot自动配置之前我们应该思考,为什么要有自动配置,有什么好处呢? 为什么要有springboot自动配置? 直接文字说明可能不直观,...

    SpringBoot的核心就是自动配置,自动配置又是基于条件判断来配置Bean。关于自动配置的源码在spring-boot-autoconfigure-2.0.0.RELEASE.jar

    在讲springBoot自动配置之前我们应该思考,为什么要有自动配置,有什么好处呢?

    为什么要有springboot自动配置?

    直接文字说明可能不直观,理解起来也吃力,我们用例子来证明体现。

    Spring搭建一个项目

    如果我们用传统的spring来搭建一个SSM框架:

    核心配置类:

    spring-web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
        <!-- 配置SpringMVC -->
        <!-- 1.开启SpringMVC注解模式 -->
        <!-- 简化配置: (1)自动注册DefaultAnootationHandlerMapping,AnotationMethodHandlerAdapter
            (2)提供一些列:数据绑定,数字和日期的format @NumberFormat, @DateTimeFormat, xml,json默认读写支持 -->
        <mvc:annotation-driven />
    
        <!-- 2.静态资源默认servlet配置 (1)加入对静态资源的处理:js,gif,png (2)允许使用"/"做整体映射 -->
        <mvc:resources mapping="/resources/**" location="/resources/" />
        <mvc:default-servlet-handler />
    
        <!-- 3.定义视图解析器 -->
        <bean id="viewResolver"
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/html/"></property>
            <property name="suffix" value=".html"></property>
        </bean>
        <!-- 在spring-mvc.xml文件中加入这段配置后,spring返回给页面的都是utf-8编码了 -->
        <bean
            class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
            <property name="messageConverters">
                <list>
                    <bean
                        class="org.springframework.http.converter.StringHttpMessageConverter">
                        <property name="supportedMediaTypes">
                            <list>
                                <value>text/html;charset=UTF-8</value>
                            </list>
                        </property>
                    </bean>
                </list>
            </property>
        </bean>
        <!-- 4.扫描web相关的bean -->
        <context:component-scan base-package="com.xiateng.web" />
    </beans>

    web.xml

    <servlet>
          <servlet-name>spring-dispatcher</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath:spring/spring-*.xml</param-value>
          </init-param>
      </servlet>
      <servlet-mapping>
          <servlet-name>spring-dispatcher</servlet-name>
          <!-- 默认匹配所有请求 -->
          <url-pattern>/</url-pattern>
      </servlet-mapping>

    可以看到,这里用到两个配置文件,如果要集成更多第三方技术配置会更复杂、繁重。

    SpringBoot搭建一个项目pring

    相对于Spring,新建一个spingBoot项目无需任何配置就可启动一个项目。

    具体新建流程就不说了,有兴趣可以看看这篇:https://blog.csdn.net/qq_43037478/article/details/90031380

    我们会看到,创建好的项目的pom文件里自动引入了几个jar包,我们先看下面这个父依赖

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.0.RELEASE</version>
        </parent>

    点进去会发现里面会有个dependencies,里面其实就是很多jar包的版本号(由于文件比较长,这里只截图部分。)

     这里我们可以得到一个结论,就是我们引入依赖的时候不需要知道版本,因为SpringBoot的父依赖里已经帮我们维护了一套版本

     

    父依赖里继续往下翻,会看到里面帮我们写好了资源库,不用我们自己去配置

            <resources>
                <resource>
                    <filtering>true</filtering>
                    <directory>${basedir}/src/main/resources</directory>
                    <includes>
                        <include>**/application*.yml</include>
                        <include>**/application*.yaml</include>
                        <include>**/application*.properties</include>
                    </includes>
                </resource>
                <resource>
                    <directory>${basedir}/src/main/resources</directory>
                    <excludes>
                        <exclude>**/application*.yml</exclude>
                        <exclude>**/application*.yaml</exclude>
                        <exclude>**/application*.properties</exclude>
                    </excludes>
                </resource>
            </resources>

    启动器,我这里用的是下面这个跟mybatis整合的

            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.1</version>
            </dependency>

    引入这个依赖的同时会自动引入jdbc,autoconfigure,starter等相关依赖,这里就不细说了

     

    不难看出SpringBoot相比于Spring创建项目的优势:

    • 遵循"约定优于配置"的原则,使用springboot的时候只需要少量配置即可,大部分时候我们可以使用默认配置。
    • 项目搭建速度非常快,没有繁重的配置,而且不容易出错。
    • 无需配置就可整合第三方框架
    • 内嵌Servlet如:Tomcat日期,应用可以直接jar包运行

     

    下面我们着重讲下SpringBoot的自动配置

    首先我们找到入口:启动类(该启动类的核心就是@SpringBootApplication注解)

    // exclude = {DataSourceAutoConfiguration.class} 排除主动注入数据源
    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
    public class Application extends WebMvcConfigurerAdapter{
        public static void main(String[] args) {
            SpringApplication.run(Application.class,args);
        }
    }

    @SpringBootApplication其实是个组合注解,我们只需要关心其中的@SpringBootConfiguration注解和@EnableAutoConfiguration注解

    @SpringBootConfiguration   // 核心
    @EnableAutoConfiguration   // 核心
    @ComponentScan(excludeFilters = 
    {@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}
    ), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}
    )})
    public @interface SpringBootApplication {
    }

    @SpringBootConfiguration注解内容

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Configuration // 核心
    public @interface SpringBootConfiguration {
    }

    可以看到,SpringBootConfiguration其实就携带了一个@Configuration注解,这个注解详细大家都很熟悉了,他代表自己是一个spring的配置类。

    @EnableAutoConfiguration注解内容

    @AutoConfigurationPackage
    @Import({AutoConfigurationImportSelector.class}) // 核心
    public @interface EnableAutoConfiguration {
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        Class<?>[] exclude() default {};
    
        String[] excludeName() default {};
    }

    可以看到,在@EnableAutoConfiguration注解内使用到了@import注解来完成导入配置的功能,而EnableAutoConfigurationImportSelector内部则是使用了SpringFactoriesLoader.loadFactoryNames方法进行扫描具有META-INF/spring.factories文件的jar包。下面是2.0.0.RELEASE实现源码:

    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);
                    //扫描具有META-INF/spring.factories文件的jar包
                    List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                    //去重
                    configurations = this.removeDuplicates(configurations);
                    // 排序
                    configurations = this.sort(configurations, autoConfigurationMetadata);
                    // 删除需要排除的类,如:启动类上的@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
                    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);
                } catch (IOException var6) {
                    throw new IllegalStateException(var6);
                }
            }
        }

     加载spring.factories文件的类

    所有的配置都存放在configurations中,   而这些配置都从getCandidateConfiguration中获取,   这个方法是用来获取候选的配置。

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            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;
        }

    实际上它返回了一个List,这个List是由loadFactoryNames()方法返回的,其中传入了一个getSpringFactoriesLoaderFactoryClass(),我们可以看看这个方法的内容。 

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
            return EnableAutoConfiguration.class;
        }

    发现没有?它实际上就是返回所有标注了这个类的所有包,而标注了这个类的包不就是@SpringBootApplication

    从而我们可以得出结论:饶了一圈其实就是为了将启动类所需的所有资源导入。

     

    spring.factories内容:一个key对应一个集合类(注:自动配置只会读取key为EnableAutoConfiguration下的集合类

    总结

    SpringBoot所有自动配置类都是在启动的时候进行扫描并加载,通过spring.factories可以找到自动配置类的路径,但是不是所有存在于spring,factories中的配置都进行加载,而是通过@ConditionalOnClass注解进行判断条件是否成立(只要导入相应的stater,条件就能成立),如果条件成立则加载配置类,否则不加载该配置类。

     

    展开全文
  • 1.一切起源之@SpringBootApplication SpringBoot全局配置文件...作为SpringBoot项目的入口,@SpringBootApplication起到了关键性的作用,另外需要注意的是SpringBoot所有关于自动配置的源码都在spring-boot-autocon...

    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
    @EnableAutoConfiguration
    @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({EnableAutoConfigurationImportSelector.class}) //引入EnableAutoConfigurationImportSelector资源
    public @interface EnableAutoConfiguration {
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        Class<?>[] exclude() default {};
    
        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

    展开全文
  • ## Spring Boot系列技术文章和开源项目等 引言: 不论在工作中,亦或是求职面试...当然,作为Spring Boot精髓,自动配置原理的工作过程往往只有在“面试”时候才能用得上,但是如果在工作中你能够深入理解Spring
  • 在spring程序main方法中 添加@SpringBootApplication或者@EnableAutoConfiguration  会自动去maven中读取每个starter中spring.factories文件 该文件里配置了所有需要被创建spring容器中bean...
  • SpringBoot自动配置原理源码解读1.@SpringBootApplication:通过这个注解,不仅仅标记这是一个SpringBoot应用,而且还开启自动配置的功能主要注解EnableAutoConfiguration@Target1.程序启动入口SpringApplication启动...
  • SpringBoot系列教程12--SpringBoot自动配置原理探析作者:一一哥一.SpringBoot自动配置概述1.概述在Spring 4.x之后,提供了一个按条件...当然这个自动配置的原理,一般也就只有在面试的时候才用得上,但是我们学习过...
  • SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConfigurationProperties 或者 @ConfigurationProperties 等几个注解来进行自动配置完成。@EnableAutoConfiguration 开启自动配置,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,117
精华内容 1,246
关键字:

springboot自动配置的原理

spring 订阅