徒手使用SpringBoot自定义Starter启动器

前言

在使用SpringBoot框架时,SpringBoot 最强大的功能就是把我们常用的场景抽取封装成了一个个starter,将其称之为场景启动器。搭建项目时,通过引入SpringBoot为我提供的这些不同场景启动器,然后开发者再进行一些属性配置,便能够快速的开发出一个Java Web应用。究其原理,完全归结于SpringBoot封装的这些Starter为我们带来了众多的自动化配置,有了这些自动化配置,使得开发Java项目非常容易且节省了不少成本,出于对这些starter的好奇,于是亲自手把手自定义一个starter,只要你有SSM框架相关知识的基础,那么很容易实现这些自定义场景启动器。

Springboot官方文档关于starters的介绍: https://docs.spring.io/spring-boot/docs/2.2.2.RELEASE/reference/html/using-spring-boot.html#using-boot-starter

一、核心知识点

1.1 Starter的机制

SpringBoot中提供的starter是一种非常重要的机制,他抛弃了以前繁杂的配置,将其统一集成进一个个starter启动器,开发者只需要在maven中引入starter依赖,SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置。starter让我们摆脱了各种依赖库的处理,版本之间的兼容,需要配置各种配置信息的困扰。

SpringBoot会自动通过classpath路径下的类发现需要的Bean,并注册进IOC容器。SpringBoot提供了针对日常企业应用研发各种场景的spring-boot-starter依赖模块。所有这些依赖模块都遵循着约定成俗的默认配置,并允许我们调整这些配置,即遵循“约定大于配置”的理念。

1.2 为什么要自定义starter

默认情况下,SpringBoot官方为我们提供的starter完全足够应对日常开发工作,在了解了SpringBoot封装starter的原理之后,我们或许能思考到,日常开发中,经常会有一些独立于业务之外的配置模块,尤其是在Spring Cloud微服务架构中,一些复用性较高的配置类通常会被拷贝到不同的服务模块下,其实这些配置类代码一模一样,只是某个子服务需要用到,这样很冗余而且很麻烦。

此时,我们便可以将这些可独立于业务代码之外的功能配置模块封装成一个个starter,复用的时候只需要将其在pom中引用starter依赖即可,SpringBoot为我们完成自动装配,这样一来简直不要太爽。

1.3 自定义starter案例

  • 动态数据源切换

  • AOP切面日志拦截处理

  • 主从库读写分离数据源

  • Swagger接口文档配置

    。。。。。。。。等等场景

1.4 自定义starter的命名规则

官方命名规则

  • 前缀:spring-boot-starter-{name}
  • 模式:spring-boot-starter-模块名
  • 举例:spring-boot-starter-web、spring-boot-starter-thymeleaf

自定义命名规则

  • 后缀:{name}-spring-boot-starter
  • 模式:模块-spring-boot-starter
  • 举例:mybatis-spring-boot-starter

二、如何自定义starter

1.如何编写自动配置

想要自定义我们自己的starter,必然要先了解SpringBoot提供的自动配置,以常见的自动配置类WebMvcAutoConfiguration为例,学习一下编写自动配置类需要哪些步骤和注解。

摘自部分源码如下(示例):

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
  ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

 public static final String DEFAULT_PREFIX = "";

 public static final String DEFAULT_SUFFIX = "";

 private static final String[] SERVLET_LOCATIONS = { "/" };

 @Bean
 @ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
 @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
 public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
  return new OrderedHiddenHttpMethodFilter();
 }

 @Bean
 @ConditionalOnMissingBean(FormContentFilter.class)
 @ConditionalOnProperty(prefix = "spring.mvc.formcontent.filter", name = "enabled", matchIfMissing = true)
 public OrderedFormContentFilter formContentFilter() {
  return new OrderedFormContentFilter();
 }

在这个自动配置类当中,我们需要以下这些注解:徒手使用SpringBoot自定义Starter启动器对于上述注解,解释如下:

@Configuration  //指定这个类是一个配置类
@ConditionalOnXXX  //在指定条件成立的情况下自动配置类才生效
@AutoConfigureOrder //指定自动配置类的优先级
@AutoConfigureAfter  //指定自动配置类的顺序
@Bean  //给容器中添加组件

@ConfigurationPropertie //将application.properties文件中前缀为xx的属性来绑定映射到这个xxxProperties.java类对应的属性上
@EnableConfigurationProperties //让标注了@ConfigurationProperties(prefix = "xx")的配置类生效。

2. 如何让配置类加载生效

自动配置类要能加载生效,需要将启动就加载的自动配置类配置在配置在META‐INF/spring.factories中,在resources 下创建文件夹 META-INF 并在 META-INF 下创建文件 spring.factories。例如spring-boot-autoconfigure的spring.factories:

徒手使用SpringBoot自定义Starter启动器
在这里插入图片描述

3. 封装自定义starter模式

在SpringBoot定义的starter中,每一个的启动器starter只用来做依赖导入提供给外部项目需要时导入使用,而启动器starter又依赖于一个自动配置,因此专门来写一个自动配置模块给启动器starter依赖。

3.1 自定义Starter

  • 启动器只用来做依赖导入

  • 专门来写一个自动配置模块;

  • 启动器依赖自动配置模块,项目中引入相应的starter就会引入启动器的所有传递依赖徒手使用SpringBoot自定义Starter启动器


三. 定义自动配置类模块

徒手使用SpringBoot自定义Starter启动器

1. 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.wechat.boot</groupId>
    <artifactId>wechat-spring-boot-autoconfigure</artifactId>
    <version>1.0-SNAPSHOT</version>

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

    <dependencies>
        <!--引入spring‐boot‐starter;所有starter的基本配置-->
        <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>

        <!-- lombok简化代码 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

</project>

2. 定义配置类绑定属性

配置完成后,我们首先创建一个 WxProperties 类,用来接受 application.properties 中注入的属性值,绑定映射到这个类上。

/**
 * @desc:
 * @author: cao_wencao
 * @date: 2020-08-25 16:04
 */

@Data
@Component
@ConfigurationProperties(prefix = "wx.config")
public class WxProperties {

    private String isOpen;
    private String appid;
    private String appsecret;
    private String appkey;
    private String token;
}

3. 业务逻辑类

这个业务逻辑类主要用来调用WxProperties 中的属性,在项目启动后,可以使用@Autowired注入WxDemoService类,调用initLoader方法 测试自动配置是否成功生效。

/**
 * @desc:
 * @author: cao_wencao
 * @date: 2020-08-25 16:17
 */

@Slf4j
@Data
public class WxDemoService {

    private WxProperties wxProperties;

    public String initLoader(String msg) {
        String appId = wxProperties.getAppid();
        String appKey = wxProperties.getAppkey();
        String sercret = wxProperties.getAppsecret();
        String token = wxProperties.getToken();
        log.info("n【应用ID】: {} n【应用Key】: {} n【应用秘钥】: {} n【应用令牌】: {} ",appId,appKey,sercret,token);
        return msg + ",开始初始化:"+ appId + appKey+sercret +token;
    }
}

4. 定义自动配置类

/**
 *  //prefix为配置文件中的前缀,
 *  //name为配置的名字
 *  //havingValue是与配置的值对比值,当两个值相同返回true,配置类生效.
 * @desc:  微信自动配置类
 * @author: cao_wencao
 * @date: 2020-08-25 16:13
 */

@Configuration //表明这是一个配置类
//当存在配置文件中以wx.config为前缀的属性,属性名称为is-open,然后它的值为enabled时才会实例化一个类。matchIfMissing 表示缺少这个属性时是否加载,如果为true,没有该property属性也会正常加载,如果为false,没有该property属性则报错,默认值是false。
@ConditionalOnProperty(prefix = "wx.config", name = "is-open", havingValue = "enabled",matchIfMissing = false)
@ConditionalOnClass(WxDemoService.class)//当前项目下classpath下存在WxDemoService类时配置类生效(类加载器中存在指明的类)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableConfigurationProperties(WxProperties.class)//使我们之前配置的 @ConfigurationProperties生效,让配置的属性成功的进入Bean 中。
public class WxAutoConfiguration {
    @Autowired
    private WxProperties properties;

    @Bean
    public WxDemoService wxDemoService(){
        WxDemoService demoService = new WxDemoService();
        demoService.setWxProperties(properties);
        return demoService;
    }
}

关于上述自动配置类,注解解释如下:

  • @Configuration: 表明这是一个配置类。
  • @ConditionalOnProperty(prefix = “wx.config”, name = “is-open”, havingValue = “enabled”,matchIfMissing = false): 当存在配置文件中以wx.config为前缀的属性,属性名称为is-open,然后它的值为enabled时才会实例化一个类。matchIfMissing 表示缺少这个属性时是否加载,如果为true,没有该property属性也会正常加载,如果为false,没有该property属性则报错,默认值是false。
  • @ConditionalOnClass(WxDemoService.class): 当前项目下classpath下存在WxDemoService类时配置类生效(类加载器中存在指明的类)
  • @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET):web应用才生效
  • @EnableConfigurationProperties(WxProperties.class) : 使我们之前配置的 @ConfigurationProperties生效,让配置的属性成功的进入Bean 中。

5. @ConditionalOnProperty注解含义

@ConditionalOnProperty表示是否满足具备某个属性条件的条件注解,用来控制@Configuration的自动配置类是否生效。

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
    //数组,获取对应property名称的值,与name不可同时使用
 String[] value() default {};
 
    //配置属性名称的前缀,比如spring.http.encoding
 String prefix() default "";
 
    //数组,配置属性完整名称或部分名称
    //可与prefix组合使用,组成完整的配置属性名称,与value不可同时使用
 String[] name() default {};

    //可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置
 String havingValue() default "";

    //缺少该配置属性时是否可以加载。如果为true,没有该配置属性时也会正常加载;反之则不会生效
 boolean matchIfMissing() default false;
}
  • prefix:application.properties配置的前缀
  • name:属性是从application.properties配置文件中读取属性值
  • havingValue:配置读取的属性值跟havingValue做比较,如果一样则返回true;否则返回false。如果返回值为false,则该configuration不生效;为true则生效
  • matchIfMissing = true:表示如果没有在application.properties中设置该属性,不匹配,则默认为条件符合,没有该property属性也会正常加载项目。如果为matchIfMissing = false,则表示当没有该property属性时则启动报错。

例如:

@Configuration
public class WebConfig {
 
    @Bean
    @ConditionalOnProperty(prefix = "rest", name = "auth-open", havingValue = "true", matchIfMissing = true)
    public AuthFilter jwtAuthenticationTokenFilter() {
        return new AuthFilter();
    }

}

上面代码的意思是: 在application.properties文件中是否设置了rest.auth-open 属性并且设置值为rest.auth-open=true ,如果没有设置rest.auth-open 属性,或者设置了rest.auth-open=false ,我们就会通过matchIfMissing = true忽略掉这个属性,项目正常启动并加载jwt的的配置信息,忽略此属性不计,havingValue = "true"的值会跟rest.auth-open 属性的值做对比。

application.properties 配置如下:

rest.auth-open=true #jwt鉴权机制是否开启(true或者false)

6. 在resources文件夹下创建META-INF/spring.factories

在resources文件夹下创建META-INF/spring.factories ,项目启动时会从该文件中扫描通过类的完整路径读取自动配置类,从而实现SpringBoot的自动配置生效。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
cn.wechat.boot.autoconfigure.config.WxAutoConfiguration

四. 定义starter启动器模块

关于定义 starter,前面也说过,很简单,starter启动器只用来做依赖导入,启动器依赖自动配置模块,项目中引入相应的starter就会引入启动器的所有传递依赖。因此我们只需要新建一个空的maven项目,引入自动配置依赖即可。

1. 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.wechat.boot</groupId>
    <artifactId>wechat-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- 依赖自定义的自动配置类wechat-spring-boot-autoconfigure -->
        <dependency>
            <groupId>cn.wechat.boot</groupId>
            <artifactId>wechat-spring-boot-autoconfigure</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

五. 打包安装到仓库

在前面两步中,自动配置模块和启动器starter模块都定义配置完了,接下来我们只需要创建一个标准的SpringBoot项目,引入启动器starter模块的依赖即可,因为maven依赖具有传递性,引入启动器starter后,自动配置模块的依赖就自动传递依赖过去。此时我们需要将自动配置模块和启动器starter模块打包到本地Maven仓库即可。

  • 自动配置模块打包:徒手使用SpringBoot自定义Starter启动器

  • starter启动器打包:徒手使用SpringBoot自定义Starter启动器

六. web项目依赖starter

创建SpringBoot 项目,pom.xml文件引入我们自定义的starter启动器,然后在application.properties配置我们自定义自动配置类中所需要的属性。

1. 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.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.thinkingcao</groupId>
    <artifactId>springboot-starter-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-starter-test</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-web</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.wechat.boot</groupId>
            <artifactId>wechat-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2. 查看依赖情况

徒手使用SpringBoot自定义Starter启动器
在这里插入图片描述

3. application.properties

wx.config.is-open=enabled
wx.config.appid=111111
wx.config.appkey=222222
wx.config.appsecret=999999
wx.config.token=abcdefg

4. 创建Controller

/**
 * @desc:
 * @author: cao_wencao
 * @date: 2020-08-25 16:51
 */

@RestController
public class DemoController {

    @Autowired
    private WxDemoService wxDemoService;

    @RequestMapping("/hello")
    public String hello(){
        String msg = wxDemoService.initLoader("你好");
        return msg;
    }
}

5. 启动项目访问:http://localhost:8080/hello

浏览器显示如下结果:徒手使用SpringBoot自定义Starter启动器IDEA控制台日志:

2020-08-26 17:39:17.466  INFO 9108 --- [nio-8080-exec-6] c.w.b.a.service.WxDemoService            : 
【应用ID】: 111111 
【应用Key】: 222222 
【应用秘钥】: 999999 
【应用令牌】: abcdefg 

6. 自定义starter依赖关系图

徒手使用SpringBoot自定义Starter启动器



七、源码

1. 参考文档:Springboot官方文档关于starters的介绍

2. 源码:https://github.com/Thinkingcao/SpringBootLearning/tree/master/springboot-custom-starter

八、总结

以上就是今天要讲的内容,本文仅仅简单介绍了如何自定义一个我们自己的starter,并且正确的使用它,而SpringBoot为我们提供了大量的starter能使我们快速的应对日常各种web开发,那么除了SpringBoot官方提供的之外,我们可根据自身项目的场景,指定特定场景的starter启动器。




原文始发于微信公众号(Thinking曹):徒手使用SpringBoot自定义Starter启动器

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/26871.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!