菜单

spring boot自动配置实现

2018年12月19日 - Java

于用了spring boot,都忘记spring
mvc中之xml配置是只什么东西了,再为回不错过。为底spring boot这么好用呢,
约定大于配置的统筹初衷,
让咱仅仅知敬爱好application.properties(或application.yml)文件就得了,大家当布局文件里好设置数据源参数,可以装服务端口,可以装redis的地点等等。我们平常会看有些分包starter名字的jar包,如spring-boot-starter-data-redis,引入那一个jar包,大家即使得简简单单急速布置了。那么我们团结支付了一个接口服务让外人调用,我们是免是可以管它们封装成一个starter
jar包呢?令人家当application.properties定义,实现自动配置为?答案是同意的,下边与自家联合写一个自动配置jar包。(本文的目标不是执教自动配置的原理,我们可以自动网上检索原理)。

SpringFactoriesLoader.loadFactoryNames方法会扫描具有META-INF/spring.factories文件的jar包,而我辈的spring-boot-autoconfigure.jar里面就生出一个那样的文件,此文件被讲明了实际有咋样自动配置:

  1. ### 环境音信

    开发工具:idea

    maven版本号:3.5.4

  2. ### jar包封装

    开创一个springboot项目

    图片 1

    填写坐标信息

    图片 2

    springboot版本2.0.4

    图片 3

    外默认,创立好后,目录如下

    图片 4

    连着下去创设我们的测试服务类TestService

     1 package com.tanghuachun.teststarter;
     2 
     3 public class TestService {
     4 
     5     private String ip;
     6     private int port;
     7 
     8     public TestService(String ip, int port){
     9         this.ip = ip;
    10         this.port = port;
    11     }
    12     
    13     public void printConfInfo(){
    14         System.out.println("骚年,你配置的IP为:" + ip + ",端口为:" + port);
    15     }
    16 }
    

    咱着想,外人利用我们那接口的下,将Ip和Port通过application.properties注入,接下我们创立属性配置实体类TestService(Service)Properties

     1 package com.tanghuachun.teststarter;
     2 
     3 import org.springframework.boot.context.properties.ConfigurationProperties;
     4 
     5 @ConfigurationProperties(prefix = "test-config")//取配置文件前缀为test-config配置
     6 public class TestServiceProperties {
     7     private String host;
     8     private int port;
     9 
    10     public String getHost() {
    11         return host;
    12     }
    13 
    14     public void setHost(String host) {
    15         this.host = host;
    16     }
    17 
    18     public int getPort() {
    19         return port;
    20     }
    21 
    22     public void setPort(int port) {
    23         this.port = port;
    24     }
    25 }
    

    咱发现在这仿佛写好后指示有荒唐

     图片 5

    每当pom文件加上依赖包,问题迎刃而解

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    

    新建一个机关配置类TestService(Service)AutoConfiguration

     1 package com.tanghuachun.teststarter;
     2 
     3 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
     4 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
     5 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
     6 import org.springframework.boot.context.properties.EnableConfigurationProperties;
     7 import org.springframework.context.annotation.Bean;
     8 import org.springframework.context.annotation.Configuration;
     9 
    10 @Configuration
    11 @ConditionalOnClass(TestService.class)// 存在TestService这个类才装配当前类
    12 @ConditionalOnProperty(name = "test-config.enabled", havingValue = "true", matchIfMissing = true)//配置文件存在这个test-config.enabled=true才启动,允许不存在该配置
    13 @EnableConfigurationProperties(TestServiceProperties.class)
    14 public class TestServiceAutoConfiguration {
    15     @Bean
    16     @ConditionalOnMissingBean   // 没有TestService这个类才进行装配
    17     public TestService testService(TestServiceProperties testServiceProperties) {
    18         return new TestService(testServiceProperties.getHost(), testServiceProperties.getPort());
    19     }
    20 }
    

    系声明含义在诠释中既认证,到那边,代码都写好了,我们希望坐声明的点子叫用户用,自定义一个注脚@EnableTestService

     1 package com.tanghuachun.teststarter;
     2 import org.springframework.context.annotation.Import;
     3 import java.lang.annotation.*;
     4 
     5 @Inherited
     6 @Documented
     7 @Target(ElementType.TYPE)
     8 @Retention(RetentionPolicy.RUNTIME)
     9 @Import(TestServiceAutoConfiguration.class)
    10 //相当于使用定义spring.factories完成Bean的自动装配
    11 public @interface EnableTestService {
    12 //@Import(TestServiceAutoConfiguration.class) 需要在调用者的Main 类加上该注解就能等效于spring.factories 文件配置
    13 }
    

    下一场注释掉pom文件被的maven插件,如下图

    图片 6

    下一场maven打包,就晤面变一个jar包,这一个jar包我们就算足以一向用了

    图片 7

     这里坐在地点环境测试,我们用编译好之jar安装到当地maven仓库,点击左边边的install按钮(你也可导入编译好的jar包一样的)

  3. ### jar包使用

    我们起首来测试我们封装好的jar,首先创立一个springboot项目(创立时共默认,你的包名也可以与我平,无所谓的)

    图片 8

    pom文件之因配置如下

    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.tanghuachun</groupId>
                <artifactId>test-starter</artifactId>
                <version>0.0.1-SNAPSHOT</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    </dependencies>
    

    以main入口类加上声明

    图片 9

    创设一个TestController类

     1 package com.tanghuachun.testmain;
     2 
     3 import com.tanghuachun.teststarter.TestService;
     4 import org.springframework.beans.factory.annotation.Autowired;
     5 import org.springframework.web.bind.annotation.GetMapping;
     6 import org.springframework.web.bind.annotation.RestController;
     7 
     8 @RestController
     9 public class TestController {
    10     @Autowired
    11     private TestService testService;
    12 
    13     @GetMapping(value = "/test")
    14     public String test(){
    15         testService.printConfInfo();
    16         return "OK";
    17     }
    18 }
    

    连通下当application.properties中配置参数

     图片 10

    启动该档,在地方栏输入 http://localhost:8080/test
    回车,看控制台打印的音信恰好是咱得的

    图片 11

    暨此大家便完成了一个活动配置的卷入。

    骚年们可研讨一下starter中注脚@ConditionalOnProperty对运用的震慑。

    后日的故事说了了。

        }

    ![](https://common.cnblogs.com/images/copycode.gif)

2.  )linux下的判定条件
    ![](https://common.cnblogs.com/images/copycode.gif)

        /**
         * 实现spring 的Condition接口,并且重写matches()方法,如果操作系统是linux就返回true
         *
         */
        public class LinuxCondition implements Condition{

            @Override
            public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

                return context.getEnvironment().getProperty("os.name").contains("Linux");
            }


        }

    ![](https://common.cnblogs.com/images/copycode.gif)

 

以SpringFactoriesLoader.loadFactoryNames()方法中,大家看来会询问META-INF/spring.factories这些布局文件

连接下我们看个源码的列子:

新建starter maven项目spring-boot-starter-hello

一、@Conditional小例子

手续如下:

老三、spring boot 自动配置源码分析

运用starter ,使用大家以此starter 需要新建一个要么采用既存的一个spring
boot工程(这里我为此底是既存的),然后

对拖欠工程开展mvn clean install,将jar推送至地点maven仓库,供后续利用。

图片 12

图片 13

图片 14

  1. 不同系统下的Bean的接近

    1. )接口

      public interface ListService {
      
          public String showListLine();
      }
      
    2. )windows下的Bean类
      图片 15

      public class WindowsListService implements ListService{
      
          @Override
          public String showListLine() {
              return "dir";
          }
      
      }
      

      图片 16

    3. )linux下的Bean的类
      图片 17

      public class LinuxListService implements ListService{
      
          @Override
          public String showListLine() {
              return "ls";
          }
      
      }
      

      图片 18

  2. 配置类
    图片 19

    @Configuration
    public class ConditionConfig {
    
        /**
         * 通过@Conditional 注解,符合windows条件就返回WindowsListService实例
         * 
         */
        @Bean
        @Conditional(WindowsCondition.class)
        public ListService windonwsListService() {
            return new WindowsListService();
        }
    
        /**
         * 通过@Conditional 注解,符合linux条件就返回LinuxListService实例
         * 
         */
        @Bean
        @Conditional(LinuxCondition.class)
        public ListService linuxListService() {
            return new LinuxListService();
        }
    }
    

    图片 20

  3. 测试类
    图片 21

    public class ConditionTest {
    
        public static void main(String[] args) {
    
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionConfig.class);
            ListService listService = context.getBean(ListService.class);
            System.out
                    .println(context.getEnvironment().getProperty("os.name") + " 系统下的列表命令为: " + listService.showListLine());
        }
    }
    

    图片 22

  4. 运行测试类,由于自家的凡windows7 系统,由此结果是

    Windows 7 系统下的列表命令为: dir
    

    如您的是linux系统,则结果虽会是

    Linux 系统下的列表命令为: ls
    

图片 23

登记配置,需要到META-INF/spring.factories文件中注册改成自动配置类:在src/main/source目录下新建改文件,然后举行部署。

紧接下去,我们就来描写一个简的spring boot starter pom。

图片 24

(注:由于自家本地的spring
boot版本不是行的,这里的EnableAutoConfigurationImportSelector已经休提出采用了,新本子可能已经更换成了此外类,不过未影响大家看代码)

 季、编写好的spring boot starter pom

 

  1. @Conditional小例子,来验证条件化配置的落实模式
  2. spring boot 的条件化配置详解
  3. spring boot 自动配置源码分析
  4. 好出手实现spring boot starter pom

跻身父类,里面来只艺术selectImports()调用了法getCandidateConfigurations(),进而调用了SpringFactoriesLoader.loadFactoryNames()方法

这个是是怎落实的也罢?原因即便在其利用了Spring的条件化配置,条件化配置允许配置有吃下被,不过于满意某些特定条件前会忽略这一个配置。

机关配置类

每当spring boot项目面临会存在一个称呼也spring-boot-autoconfigure的jar包

修改pom文件

@Configuration
@EnableConfigurationProperties(HelloServiceProperties.class)
@ConditionalOnClass(HelloService.class)
@ConditionalOnProperty(prefix = "hello", matchIfMissing = true, value = "enabled")
public class HelloServiceAutoConfiguration {

    @Autowired
    HelloServiceProperties helloServiceProperties;

    @Bean
    @ConditionalOnMissingBean(HelloService.class)
    public HelloService helloService() {
        HelloService service = new HelloService();
        service.setMsg(helloServiceProperties.getMsg());
        return service;
    }
}

我们位置提到的JdbcTemplateAutoConfiguration自动配置类似即当中。

  1. )修改pom,引入上述的赖

    <dependency>
                <groupId>com.sam</groupId>
                <artifactId>spring-boot-starter-hello</artifactId>
                <version>0.0.1-SNAPSHOT</version>
            </dependency>
    
  2. )实现controller
    图片 25

    @RestController
    public class HelloController {
      //代码中没有配置这个helloService Bean,但是自动配置能够帮忙实例化,因此可以直接注入
        @Autowired
        HelloService helloService;
    
        @RequestMapping(value="/helloService")
        public String sayHello() {
            return helloService.sayHello();
        }
    }
    

    图片 26

  3. )页面访问/helloService接口图片 27

     

  4. )在application.properties里面配备hello.msg=sam,然后再次访问/hello瑟维斯(Service)接口

  1. 认清标准定义

    1. )windows下的判断条件
      图片 28

      /**
       * 实现spring 的Condition接口,并且重写matches()方法,如果操作系统是windows就返回true
       *
       */
      public class WindowsCondition implements Condition{
      
          @Override
          public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
      
              return context.getEnvironment().getProperty("os.name").contains("Windows");
          }
      

图片 29

属性配置

/**
 * 后面的代码会依据此类是否存在,来决定是否生产对应的Bean
 *
 */
public class HelloService {

    private String msg;

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public String sayHello() {
        return "hello " + msg;
    }
}

图片 30

认清按照类

以JdbcTemplateAutoConfiguration为条例,它其中有立段代码:

图片 31

图片 32

唯有当不存在JdbcOperations(假如查阅JdbcTemplate的源码,你会发觉JdbcTemplate类实现了JdbcOperations接口)实例的上,才会面开首化一个JdbcTemplate
的Bean。

依照HelloService(Service)Properties提供的参数,并经@ConditionalOnClass(Hello瑟维斯(Service).class)判定Hello瑟维斯(Service)这多少个看似以Classpath中是否是,存在而还不曾对应之Bean,就别对应之hello瑟维斯(Service)(Service)Bean

       图片 33

次、spring boot 的条件化配置

图片 34

图片 35

图片 36

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sam.spring_boot_starter_hello.HelloServiceAutoConfiguration

spring
boot项目标启动类用的讲明–@SpringBootApplication是一个组合声明,其中@EnableAutoConfiguration是自动配置相关的。图片 37

一旦此@EnableAutoConfiguration注明里面有个@Import讲明导入了EnableAutoConfigurationImportSelector用来兑现具体的效应

图片 38

条件化配置就是以是jar里面实现之,它使用了如下的条件化阐明,这么些注脚都是为@ConditionalOn最先的,他们依然运用了@Conditional的三结合阐明:

假定实现条件化配置大家若用到@Conditional条件化注脚。

俺们知晓,spring
boot自动配置效益可按照不同境况来支配spring配置当据此哪个,不该为此什么人,举个例证:

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sam</groupId>
    <artifactId>spring-boot-starter-hello</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <!-- 这里需要引入spring boot的自动配置作为依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>1.5.1.RELEASE</version>
        </dependency>


    </dependencies>
</project>

图片 39

/**
 * @ConfigurationProperties
 * 自动匹配application.properties文件中hello.msg的值,然后赋值给类属性msg,这里的msg默认值为“spring boot”
 *
 */
@ConfigurationProperties(prefix="hello")
public class HelloServiceProperties {

    private static final String MSG = "spring boot";

    private String msg = MSG;

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }


}

我们清楚在windows下显得列表的命令是dir,而于linux系统下显得列表的指令是ls,基于条件配置,我们可兑现以不同的操作系统下重返不同的价。

依照上述内容,大家虽雅观活动配置相关的源码了。

本篇随便说起如下三独点拓展拓展:

 这些近乎继承了AutoConfigurationImportSelector

图片 40

图片 41

@Bean
    @Primary
    @ConditionalOnMissingBean(JdbcOperations.class)
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(this.dataSource);
    }

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图