专栏首页程序猿的大杂烩开发一个属于自己的Spring Boot Starter

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

关于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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring的一些零碎知识点整理

    要想在Web工程中配置Spring,首先需要在工程加入spring-web包,我这里使用的是maven的web工程,pom.xml配置文件配置的依赖如下:

    端碗吹水
  • Spring Batch快速入门

    Spring Batch简单来说就是一个轻量级的批处理框架,从名字就可以知道它是Spring 的子项目。我们在企业开发中可能会面临到一些需要处理较大数据量的场景...

    端碗吹水
  • Spring Cloud 集成 RabbitMQ

    前言:我们现在有一个用微服务架构模式开发的系统,系统里有一个商品服务和订单服务,且它们都是同步通信的。

    端碗吹水
  • Hystrix图形化界面

    一个新的模块,起名为cloud-consumer-hystrix-dashboard9001

    乐心湖
  • import提升导致Fundebug报错:“请配置apikey”

    部分Fundebug用户使用import来导入js文件时,出现了"请配置apikey"的报错,这是由于import提升导致的,下面我会详细解释一下这一点。

    Fundebug
  • Android菜单的定义及ActionBar的实现

    当用户点击menu键时,onCreateOptionMenu()方法会被触发,因此如果需要有自己的实现,就必须重写该方法。该方法会传入一个menu对象,利用该对...

    砸漏
  • 安卓开发_浅谈OptionsMenus(选项菜单)

    听着music睡
  • hibernate与spring的初步整合

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework....

    yawn
  • mongo实现自增id

    Mongo 的free schema,提供了灵活的数据结构,和快速开发的能力,但是也造成了松散的数据组织形式。比如说有些字段不允许为null的,需要符合一定格式...

    方丈的寺院
  • 加速Python循环

    py3study

扫码关注云+社区

领取腾讯云代金券