• 开发一个属于自己的Spring Boot Starter Vincent


    关于Starter

    Spring Boot秉承“约定大于配置”的开发方式,使得我们基于Spring Boot开发项目的效率变得十分高。

    相信使用过Spring Boot的小伙伴都会发现,当我们要用到某个Spring提供的组件时,只需要在pom.xml文件中添加该组件的starter依赖就能集成到项目中。

    例如,在pom.xml文件中添加spring-boot-starter-web依赖,就能让项目整合Spring MVC的功能。

    并且在最简使用下几乎不需要进行任何的配置,而以往想要集成Spring MVC,不仅要添加一堆类似于spring-webspring-webmvc等相关依赖包,以及完成许多繁杂的配置才能够实现集成。

    这是因为starter里已经帮我们整合了各种依赖包,避免了依赖包缺失或依赖包之间出现版本冲突等问题, 以及完成了许多基础配置和自动装配,让我们可以在最简使用下,跳过绝大部分的配置,从而达到开箱即用的效果。

    这也是Spring Boot实现“约定大于配置”的核心之一。

    动手开发一个Starter

    通过以上的描述,我们可以简单地将starter看作是对一个组件功能粒度较大的模块化封装,包括了所需依赖包的整合及基础配置和自动装配等。

    除了Spring官方提供的starter外,我们自己也可以根据业务开发一个starter。例如,当项目积累到一定程度时,我们可以将一些通用功能下沉为一个starter。

    开发一个starter也很简单,只需要以下步骤:

    1. 新建一个Maven项目,在pom.xml文件中定义好所需依赖;
    2. 新建配置类,写好配置项和默认值,使用@ConfigurationProperties指明配置项前缀;
    3. 新建自动装配类,使用@Configuration@Bean来进行自动装配;
    4. 新建spring.factories文件,用于指定自动装配类的路径;
    5. 将starter安装到maven仓库,让其他项目能够引用;

    接下来,以封装一个用于操作redis的starter为例,一步步展示这些步骤的具体实现过程。

    首先是第一步,新建一个maven项目,完整的pom.xml内容如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.0.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>spring-boot-starter-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>spring-boot-starter-demo</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
            <!-- jedis -->
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>3.1.0</version>
            </dependency>
    
            <!-- gson -->
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>2.8.6</version>
            </dependency>
        </dependencies>
    </project>

    第二步,新建一个属性配置类,写好配置项和默认值。并使用@ConfigurationProperties指明配置项前缀,用于加载配置文件对应的前缀配置项:

    package com.example.starter.demo.properties;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    /**
     * 属性配置类,用于加载配置文件对应的前缀配置项
     *
     * @author zero
     * @date 2019-11-04
     **/
    @Data
    @ConfigurationProperties("demo.redis")
    public class RedisProperties {
    
        private String host = "127.0.0.1";
    
        private int port = 6379;
    
        private int timeout = 2000;
    
        private int maxIdle = 5;
    
        private int maxTotal = 10;
    
        private long maxWaitMillis = 10000;
    
        private String password;
    }

    编写一个简单的redis操作工具,代码如下:

    package com.example.starter.demo.component;
    
    import com.google.gson.Gson;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    
    /**
     * redis 操作组件
     *
     * @author zero
     * @date 2019-11-04
     **/
    @Slf4j
    @RequiredArgsConstructor
    public class RedisComponent {
    
        private final JedisPool jedisPool;
    
        /**
         * get value with key
         */
        public <T> T get(String key, Class<T> clazz) {
            try (Jedis resource = jedisPool.getResource()) {
                String str = resource.get(key);
    
                return stringToBean(str, clazz);
            }
        }
    
        /**
         * set value with key
         */
        public <T> boolean set(String key, T value, int expireSeconds) {
            try (Jedis resource = jedisPool.getResource()) {
                String valueStr = beanToString(value);
                if (valueStr == null || valueStr.length() == 0) {
                    return false;
                }
    
                if (expireSeconds <= 0) {
                    resource.set(key, valueStr);
                } else {
                    resource.setex(key, expireSeconds, valueStr);
                }
    
                return true;
            }
        }
    
        private <T> T stringToBean(String str, Class<T> clazz) {
            Gson gson = new Gson();
            return gson.fromJson(str, clazz);
        }
    
        private <T> String beanToString(T value) {
            Gson gson = new Gson();
            return gson.toJson(value);
        }
    }

    第三步,新建自动装配类,使用@Configuration@Bean来实现对JedisPoolRedisComponent的自动装配;

    package com.example.starter.demo.configuration;
    
    import com.example.starter.demo.component.RedisComponent;
    import com.example.starter.demo.properties.RedisProperties;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    /**
     * 自动装配类
     *
     * @author zero
     * @date 2019-11-04
     **/
    @Slf4j
    @Configuration
    @RequiredArgsConstructor
    @EnableConfigurationProperties(RedisProperties.class)
    public class RedisConfiguration {
    
        private final RedisProperties properties;
    
        @Bean
        // 表示当Spring容器中没有JedisPool类的对象时,才调用该方法
        @ConditionalOnMissingBean(JedisPool.class)
        public JedisPool jedisPool() {
            log.info("redis connect string: {}:{}", properties.getHost(), properties.getPort());
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPoolConfig.setMaxIdle(properties.getMaxIdle());
            jedisPoolConfig.setMaxTotal(properties.getMaxTotal());
            jedisPoolConfig.setMaxWaitMillis(properties.getMaxWaitMillis());
    
            String password = properties.getPassword();
            if (password == null || password.length() == 0) {
                return new JedisPool(jedisPoolConfig, properties.getHost(),
                        properties.getPort(), properties.getTimeout());
            }
    
            return new JedisPool(jedisPoolConfig, properties.getHost(),
                    properties.getPort(), properties.getTimeout(), properties.getPassword());
        }
    
        @Bean
        @ConditionalOnMissingBean(RedisComponent.class)
        public RedisComponent redisComponent(JedisPool jedisPool){
            return new RedisComponent(jedisPool);
        }
    }

    第四步,在项目的resources目录下新建一个META-INF目录,并在该目录下新建spring.factories文件

    如下图所示:

     在spring.factories文件里指定自动装配类的路径:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.example.starter.demo.configuration.RedisConfiguration

    若需要指定多个自动装配类的路径,则使用逗号分隔。如下示例:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.example.starter.demo.configuration.DemoConfiguration,\
      com.example.starter.demo.configuration.RedisConfiguration

    Tips:spring.factories支持配置的key如下

    org.springframework.context.ApplicationContextInitializer
    org.springframework.context.ApplicationListener
    org.springframework.boot.autoconfigure.AutoConfigurationImportListener
    org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
    org.springframework.boot.autoconfigure.EnableAutoConfiguration
    org.springframework.boot.diagnostics.FailureAnalyzer
    org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider
    org.springframework.boot.env.EnvironmentPostProcessor
    org.springframework.boot.SpringApplicationRunListener
    org.springframework.boot.SpringBootExceptionReporter
    org.springframework.beans.BeanInfoFactory
    org.springframework.boot.env.PropertySourceLoader
    org.springframework.data.web.config.SpringDataJacksonModules
    org.springframework.data.repository.core.support.RepositoryFactorySupport

    最后install这个maven项目,命令如下:

    mvn clean install

    如果使用的开发工具是IDEA的话就比较简单,只需要双击一下install即可:

    使用Starter

    在任意一个Spring Boot项目的pom.xml文件中添加如下依赖:

    <dependency>
        <groupId>com.example</groupId>
        <artifactId>spring-boot-starter-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
     </dependency>

    在项目的application.yml中添加如下配置项来覆盖默认配置,若默认配置已符合需求则可以省略这一步:

    demo:
      redis:
        host: 192.168.11.130
        port: 6379
        timeout: 3000
        password: A8^MZ59qOr*gkhv51tSdifvb
        max-total: 10
        max-wait-millis: 10000
        max-idle: 10

    编写一个单元测试类进行测试,代码如下:

    package com.example.firstproject.starter;
    
    import com.example.starter.demo.component.RedisComponent;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    
    @Slf4j
    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class StarterTests {
    
        @Autowired
        private RedisComponent redisComponent;
    
        @Test
        public void redisTest() {
            String key = "redisTest";
            String value = "this is a test value";
            boolean success = redisComponent.set(key, value, 3600);
            log.info("set value to redis {}!", success ? "success" : "failed");
            String result = redisComponent.get(key, String.class);
            log.info("get value from redis: [{}]", result);
        }
    }

    运行结果如下:

     附代码仓库地址:

    https://gitee.com/demo_focus/Spring-Boot-Starter-Demo

    转发:https://cloud.tencent.com/developer/article/1702341

  • 相关阅读:
    面试8:找二叉树的下个结点
    面试8:找二叉树的下个结点
    面试题7:重建二叉树
    面试题7:重建二叉树
    Kenneth A.Lambert著的数据结构(用python语言描述)的第一章课后编程答案
    基础的Mapgis三维二次开发-插件式
    面试题6:从尾到头打印链表
    C语言中声明和定义详解(待看。。
    面试题5:替换空格
    面试题5:替换空格
  • 原文地址:https://www.cnblogs.com/Vincent-yuan/p/15734722.html
Copyright © 2020-2023  润新知