zoukankan      html  css  js  c++  java
  • SpringBoot第十六篇:自定义starter

    作者:追梦1819
    原文:https://www.cnblogs.com/yanfei1819/p/11058502.html
    版权声明:本文为博主原创文章,转载请附上博文链接!

    前言

      这一段时间项目赶进度,故该系列博客更新没有之前那么频繁,望谅解。

      SpringBoot 用起来方便,它默认集成了 Java 的主流框架。这也是 SpringBoot 的一大特色,使用方便,需要什么框架或者技术,只需要引入对应的 starter 即可。目前官方已经集成的各大技术的启动器,可以查看 文档

      作者最开始考虑该话题的是曾经的一个面试题:如何自定义一个自定义启动器?

      本文将围绕该面试题进行讲解。


    自定义starter

      在自定义 starter 之前,我们先回顾 SpringBoot 官方已经集成的 starter 。我们使用时,只需引入对应的 spring-boot-starter-xxx 即可使用(即我们常说的开箱即用)。

      同时,还预先设置了默认值,如果需要修改这些默认值,只需要在 application.properties 或 application.yml 配置文件中修改。例如:SpringBoot 默认的端口号是 8080,如果需要修改该端口号,只需要在 application.properties 中添加属性 server.port=9090 即可。

      创建自定义启动器,需要创建以下两个组件:

    • 自动配置类以及自定义配置的属性类;
    • 对应的 maven 依赖。

    首先,创建自定义starter工程,并引入maven依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
        <version>2.1.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <version>2.0.0.RELEASE</version>
        <optional>true</optional>
    </dependency>
    

    然后,创建实体类。前缀加上字段名称可以在application.properties文件中创建属性的名称。

    package com.yanfei1819.springbootstarter.entity;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    /**
     * Created by 追梦1819 on 2019-05-10.
     */
    @ConfigurationProperties(prefix = "spring.person")
    public class PersonProperties {
        private String name;
        private int age;
        private double salary;
    	// set/get 省略
    }
    

    第三步,定义核心服务类,该类主要定义了 starter 的核心功能。

    package com.yanfei1819.springbootstarter.service;
    import com.yanfei1819.springbootstarter.entity.PersonProperties;
    
    /**
     * Created by 追梦1819 on 2019-05-10.
     */
    public class PersonService {
        private PersonProperties properties;
        public PersonService(PersonProperties properties) {
            this.properties = properties;
        }
        public PersonService() {
        }
        public void say() {
            System.out.println("hello,I am " + properties.getName() + ",and I am " + properties.getAge() +
                    ",and My salary " + properties.getSalary());
        }
    }
    

    第四步,自定义配置类。通常情况每个 starter 至少有一个配置类。命名规则也很明显,一般命名规则使用XxxAutoConfiguration, 例如 RedisAutoConfiguration 等。该类将核心功能注入到 SpringBoot 上下文中。

    package com.yanfei1819.springbootstarter.configuration;
    
    import com.yanfei1819.springbootstarter.entity.PersonProperties;
    import com.yanfei1819.springbootstarter.service.PersonService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * Created by 追梦1819 on 2019-05-10.
     */
    @Configuration
    @EnableConfigurationProperties(PersonProperties.class)
    @ConditionalOnClass(PersonService.class)
    @ConditionalOnProperty(prefix = "spring.person", value = "enabled", matchIfMissing = true)
    public class PersonServiceAutoConfiguration {
    
        @Autowired
        private PersonProperties properties;
    
        @Bean
        @ConditionalOnMissingBean(PersonService.class)  // 当容器中没有指定Bean的情况下,自动配置PersonService类
        public PersonService personService() {
            PersonService personService = new PersonService(properties);
            return personService;
        }
    }
    

    最后,创建 spring.factories 文件:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.yanfei1819.springbootstarter.configuration.PersonServiceAutoConfiguration
    

      当 SpringBoot 启动的时候,它会在类路径中查找 spring.factories 文件,此条件初始化由@ConditionalOnClass注释启用。此文件将名称映射到Spring Boot将尝试运行的不同配置类。因此,根据这个片段,Spring Boot将尝试运行RabbitMQ,Cassandra,MongoDB和Hibernate的所有配置类。

      @EnableAutoConfiguration 的关键功能是使用 SpringFactoriesLoader.loadFactoryNames 方法来扫描具有 META-INF/spring.factories 文件的 jar 包,这样我们的自动配置类才能生效,所以我们在 autoconfigure 模块的 resources 下创建 META-INF/spring.factories 文件。


    使用自定义starter

    新创建一个工程,引入自定义启动器的 maven 依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    
    <dependency>
        <groupId>com.yanfei1819</groupId>
        <artifactId>customize-spring-boot-starter</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    

      此处我们要注意一下命名规则,官方命名是spring-boot-starter-xxx , 自定义命名是 xxx-spring-boot-starter

    然后再配置文件中写入测试数据:

    spring.person.name=starter
    spring.person.age=26
    

    下面我们修改启动类做个简单的测试:

    package com.yanfei1819.customizestartertest;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class CustomizeStarterTestApplication implements CommandLineRunner {
        @Value("${spring.person.name}")
        private String name;
        @Value("${spring.person.age}")
        private int age;
        
        public static void main(String[] args) {
            SpringApplication.run(CustomizeStarterTestApplication.class, args);
        }
        @Override
        public void run(String... args) throws Exception {
            System.out.println("姓名是:"+name+",年龄是:"+age);
        }
    }
    

      上面的启动类实现了 CommandLineRunner 接口,并重写了 run 方法。Spring boot的CommandLineRunner接口主要用于实现在应用初始化后,去执行一段代码块逻辑,这段初始化代码在整个应用生命周期内只会执行一次。(该接口的作用可以参照官网说明)。

      此处是为了省去写测试类才实现了该接口。


    最后,启动项目,可以看到如下结果:


    总结

      根据以上的分析和示例,可以大概总结出 starter 的工作流程:

    1. SpringBoot 启动时寻找含 spring.factories 文件的JAR包;

    2. 读取spring.factories文件获取配置的自动配置类AutoConfiguration;

    3. 将自动配置类下满足条件(@ConditionalOnXxx)的@Bean放入到 Springoot 上下文中;

    4. 开发者直接使用。


    感悟

      SpringBoot 的自定义启动器极大的方便了独立功能 jar 的开发,消除了大量的配置工作。

      依葫芦画瓢,要写一个自定义的starter,其实很简单,注意几个注解就可以了。但是,我们真正要做的,是通过源码来理解自动配置原理,原理是灵魂,知其然,知其所以然,这样去自定义 starter 才会得心应手。后续我们将继续分享 SpringBoot 的自动配置原理。


    参考

    SpringBoot 官网


    ![](https://img2018.cnblogs.com/blog/1183871/201906/1183871-20190620141951608-719672168.png)
  • 相关阅读:
    treap模板
    Codeforces Round #446 (Div. 2)
    BZOJ 1001: [BeiJing2006]狼抓兔子 (最小割)
    NOIP2017总结
    Python 操作 Mysql 模块
    poj 3660 Cow Contest (传递闭包)
    poj 1964 Cow Cycling(dp)
    poj 3671 Dining Cows (Dp)
    cogs 线型网络(状压dp)
    codevs 2800 送外卖(状压dp)
  • 原文地址:https://www.cnblogs.com/yanfei1819/p/11058502.html
Copyright © 2011-2022 走看看