27、springboot --> 自定义第三方框架 和 Starter组件 及其测试 详解

springboot自定义第三方框架和Starter组件及其测试


自定义自动配置 ==> 自动配置类+注册

所谓的自动配置,就是通过一个配置类,然后这个配置类在我们容器中定义了大量的bean,然后这些bean也不是直接定义,它是结合了条件注解,只有在某些特定的条件下,才会生效,这样我们的自动配置就可以根据我们的环境的配置(如yml配置文件),根据我们这个应用程序所使用的环境来决定这些bean的配置是否要生效。


自定义自动配置分为2步:


1、使用@Configuration和条件注解定义自动配置类。

使用条件注解和@Bean注解在容器中定义整合框架所需要的组件(Bean)。

比如程序要整合MyCustomFrame框架,MyCustomFrame框架所需要核心组件就是WriterTemplate,

因此该自动配置就是负责在容器中自动配置WriterTemplate


2、在META-INF/spring.factories中注册自动配置类。

使用如下META-INF/spring.factories文件来注册自动配置类:

      org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
          cn.ljh.mycustomstarter.FrameAutoConfiguration

开发完自己的自动配置及Starter后,并不需要运行该项目,而是应该将Starter安装到本地资源库、甚至公司的中央资源库。

在Spring的Java配置类中,方法名默认将作为bean的 id,因此不要让两个方法的方法名相同,这样会造成ID冲突。


自定义Starter组件

官方推荐,一个完整的 Spring Boot starter 应该包含以下两个组件:


auto-configure模块,包含自动配置类和META-INF\spring-factories文件。推荐名:xxx-spring-boot-autoconfigure


Starter模块:负责管理自动配置模块及其他第三方的依赖,简而言之,添加本Starter就能开始使用该自动配置。

推荐名:xxx-spring-boot-starter

Spring Boot官方Starter名叫: spring-boot-stater-xxx

Starter并不包含任何class文件,它只负责管理依赖。

Starter JAR包下只包含xxx-starter.pom文件,该文件指定该Starter负责管理的自动依赖模块和第三方依赖。


【注意】:使用Maven开发Spring Boot的Starter组件时,不要添加spring Boot的Maven插件。

——否则该Maven插件总以为你是一个Spring Boot项目,它总会尝试帮你找程序的主类。


需求分析总结:

创建一个自定义的第三方框架 MyCustomFrame , 创建一个自定义的 Starter 组件 MyCustomStarter,创建一个普通项目 CustomFrameTest 来添加 自定义的 starter 组件,进行测试。

测试:根据普通项目的配置,决定信息输出到数据库还是文件。

关系:
第三方框架 MyCustomFrame 作用:提供一个 WriterTemplate 工具类,用来实现一个功能。
功能:如果我们引入这个第三方框架的项目有连接数据库,那么就把要输出的信息输出到数据库,如果没有连接数据库,那么就把要输出的信息输出到指定的文件

自定义 Starter 组件的作用:核心就是提供一个 FrameAutoConfiguration 配置类,用来整合第三方框架 MyCustomStarter 时,需要的一些配置。
简单来说,这个 FrameAutoConfiguration 配置类 提供两个 Bean,根据我们的普通项目CustomFrameTest 是否有连接数据库,进行分析,有连接就返回输出到数据库的Bean,没有连接就返回输出到文件的bean。

文字大概这么描述,具体还得看截图一并分析。


代码截图分析:

开发第三方框架:MyCustomFrame

1、第一步:先创建一个maven项目,作为第三方框架,项目名叫:MyCustomFrame,

这个项目里面只有一个核心的类,WriterTemplate,作用是引入这个第三方框架的项目是否有连接数据库,有就把要输出的信息输出到数据库,如果没有连接数据库,那么就把要输出的信息输出到指定的文件 的功能。

然后把这个框架部署到本地资源库,双击 Ctrl 键,用 mvn install 操作把框架打包并且安装到我们的本地资源库

具体代码看文章最后:

在这里插入图片描述


代码:
在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


然后把这个第三方框架 通过 双击 Ctrl 键,输入 mvn install 命令 , 把这个框架打包成jar包,安装到本地的 maven 库里面。

可以理解成我们在项目中添加依赖一样。添加的依赖就会保存在 maven 仓库里面。

这样一个简单的第三方框架就可以了

在这里插入图片描述


开发 starter 组件:mycustomstarter

2、第二步:演示如何使用 spring 通过 自动配置来开发一个自动的 starter 组件,让我们的项目可以很简单的通过starter 来整合我这个自定义的第三方框架(MyCustomFrame)

现在开发starter组件
这个starter组件主要就是这两个类:属性处理类和配置类,还有一个注册配置类的文件。
在这里插入图片描述


详细:
FrameProperties 属性处理类:
在starter组件添加这个属性处理类,后面这个starter组件被引入到项目后,就可以获取到该项目指定前缀的一些配置文件的属性
在这里插入图片描述


FrameAutoConfiguration 配置类:

在这里插入图片描述
在这里插入图片描述


spring.factories 用来注册自动配置类FrameAutoConfiguration :

在这里插入图片描述


pom.xml 文件

在这里插入图片描述


到这里 starter组件就开发完成,现在就需要把这个组件也打包成jar包,安装到 maven 仓库里面。

开发完自己的自动配置(MyCustomFrame)及Starter(MyCustomStarter)后,并不需要运行该项目,而是应该将Starter安装到本地资源库、甚至公司的中央资源库。

双击 Ctrl 键,然后输入 mvn install 命令就可以了,就会自动保存到本地的maven仓库

如果修改了这个starter组件,要重新打包的话,我选择先删除target包,再重新编译下,再打包成jar包。

在这里插入图片描述


【注意】:使用Maven开发Spring Boot的Starter组件时,不要添加spring Boot的Maven插件

——否则该Maven插件总以为你是一个Spring Boot项目,它总会尝试帮你找程序的主类。

starter 组件不需要 这个 maven 插件,把这个删除掉,不然打包失败

在这里插入图片描述


针对自定义starter组件中一些代码的理解:

@Bean 表明在spring 容器中配置这个bean

1、@EnableConfigurationProperties

这里创建了属性处理类,这个类需要用 @EnableConfigurationProperties(value = FrameProperties.class) 这个注解来启用它,不然就会爆红,如图:
在这里插入图片描述


还需要配合这个注解,用来解析配置文件的属性
在这里插入图片描述


2、@ConditionalOnClass(WriterTemplate.class)
这是一个条件注解,被这个注解修饰的这个 FrameAutoConfiguration 配置类,如果要生效,项目中就必须有WriterTemplate这个类存在,不然就不生效。
因为这个WriterTemplate类是存在第三方框架MyCustomFrame里面的,所以需要引入第三方框架的依赖
在这里插入图片描述


3、@AutoConfigureAfter(DataSourceAutoConfiguration.class)
因为这个配置类添加的writerTemplate1这个bean需要Datasource作为参数,但是在加载的时候,可能加载 writerTemplate1 这个bean的时机 比加载 DataSourceAutoConfiguration这个类要快,所以需要添加这个条件注解@AutoConfigureAfter。
作用就是FrameAutoConfiguration这个配置类要在DataSourceAutoConfiguration类加载存在后才能加载。


@ConditionalOnMissingBean:
只有当项目中 没有 WriterTemplate 这个bean时,才自动为项目配置这个Bean,因为如果项目中有开发人员自己自定义开发 WriterTemplate 这个bean,那我们肯定不需要再额外自动配置 WriterTemplate 这个bean


@ConditionalOnSingleCandidate:
另外,Bean上面的这个@ConditionalOnSingleCandidate(DataSource.class)注解,表示只有当spring容器中有且仅有一个 DataSource Bean 的时候,下面这个bean配置方法才生效。
原因:因为下面这个配置必须给它依赖注入唯一的 DataSource ,有多个DataSource 就不行。
在这里插入图片描述


4、两个bean的执行顺序。
WriterTemplate1 这个bean是把数据输出数据库表中去
WriterTemplate2 这个bean是把数据输出到指定文件中去
在这里插入图片描述


5、WriterTemplate1 这个bean是把数据输出数据库表中去
这个starter组件中的处理类中的配置的WriterTemplate1 这个bean,传入的 DataSource ,应该是 引入这个starter组件的 CustomFrameTest 项目中的yml配置文件中配置的数据库连接。
因为这个数据库连接在项目中只有这一个,应该就算是唯一的。
这个bean的dataSource有且仅有一个,还理解的不够透彻,先把想法记下来。
在这里插入图片描述


6、WriterTemplate2 这个bean 的数据的获取,这个bean获取到引入这个starter组件的项目的yml配置文件中的要输出到指定文件中的dest和charset属性。
在这里插入图片描述


CustomFrameTest:普通测试项目

3、第3步:
创建一个项目,来使用这个 starter 组件,进行测试。
在这里插入图片描述


在启动类中通过类型获取 WriterTemplate 这个bean ,然后调用 write 方法,把要输出的信息作为参数传递过去,然后是输出到数据库还是输出到文件,
就是通过 starter组件+MyCustomFrame框架里面的逻辑来实现了。

在这里插入图片描述


从pom文件中可以看出,这个项目引入了starter组件,然后这个starter里面又包含了自定义的第三方框架MyCustomFrame
在这里插入图片描述

可以看出这个项目只有启动类和yml配置类,没有写任何逻辑代码,测试是输出到文件还是数据库的代码,都是通过引入 starter 组件来实现的。而starter组件是不需要运行的,是安装在maven库的。


测试:


输出到文件:

这个项目没有连接到数据库,所以是把信息输出到指定文件中去
在这里插入图片描述


在这里插入图片描述


输出到数据库:

如果要输出到数据库,那么就要有数据库的连接。需要创建对应的 customframe 数据库,然后再pom.xml 文件中添加数据库的连接的依赖。
在这里插入图片描述


可以看出因为添加了连接数据库的 jdbc 依赖,所以就是走输出信息到数据库逻辑去了。

在这里插入图片描述


打印的文字出自第三方框架这个类
在这里插入图片描述


具体代码:

MyCustomFrame框架

自定义第三方框架MyCustomFrame 里面的 WriterTemplate 工具类代码

package cn.ljh.myCustomFrame;

import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;

/*
 * 这个类的作用:
 * 如果我们这个项目有连到数据库,那就把我们要输出的信息输出到数据库,
 * 如果没有连数据库,那么就把信息输出到文件
 *
 * 是一个工具类
 */

@Slf4j
public class WriterTemplate
{
    private final DataSource dataSource;
    private Connection conn;
    private final File dest;
    private final Charset charset;
    private RandomAccessFile raf;

    public WriterTemplate(DataSource dataSource) throws SQLException
    {
        this.dataSource = dataSource;
        this.dest = null;
        this.charset = null;
        if (Objects.nonNull(this.dataSource))
        {
            log.debug("==========获取数据库连接==========");
            this.conn = dataSource.getConnection();
        }
    }
    public WriterTemplate(File dest, Charset charset) throws FileNotFoundException
    {
        this.dest = dest;
        this.charset = charset;
        this.dataSource = null;
        this.raf = new RandomAccessFile(this.dest, "rw");
    }

    public void write(String message) throws IOException, SQLException
    {
        if (Objects.nonNull(this.conn))
        {
            // 查询当前数据库的customFrame_message表是否存在
            ResultSet rs = conn.getMetaData().getTables(conn.getCatalog(), null,
                    "customFrame_message", null);
            //  如果customFrame_message表不存在
            if (!rs.next())
            {
                log.debug("~~~~~~创建customFrame_message表~~~~~~");
                conn.createStatement().execute("create table customFrame_message " +
                        "(id int primary key auto_increment, message_text text)");
                rs.close();
            }
            log.debug("~~~~~~输出到数据表~~~~~~");
            // 插入要输出的字符串
            conn.createStatement().executeUpdate("insert into " +
                    "customFrame_message values (null, '" + message + "')");
        }
        else
        {
            log.debug("~~~~~~输出到文件~~~~~~");
            // 输出到文件
            raf.seek(this.dest.length());
            raf.write((message + "\n").getBytes(this.charset));
        }
    }
    // 关闭资源
    public void close() throws SQLException, IOException
    {
        if (this.conn != null)
        {
            this.conn.close();
        }
        if (this.raf != null)
        {
            this.raf.close();
        }
    }
}

pom.xml 文件

自定义第三方框架MyCustomFrame 里面的 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.ljh.customframe</groupId>
    <artifactId>MyCustomFrame</artifactId>
    <version>1.0.0</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.6</version>
        </dependency>
    </dependencies>
</project>

mycustomstarter 组件

mycustomstarter 组件的 FrameAutoConfiguration 配置类代码

package cn.ljh.mycustomstarter;


import cn.ljh.myCustomFrame.WriterTemplate;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


import javax.sql.DataSource;
import java.io.File;
import java.io.FileNotFoundException;
import java.nio.charset.Charset;
import java.sql.SQLException;


//这个配置类的作用就是用来整合第三方框架MyCustomFrame时,所需要的一些配置
// 用 @Configuration 修饰的类就是配置类
@Configuration
//启用这个 FrameProperties 属性处理类,不然属性处理类的@ConfigurationProperties注解会爆红
@EnableConfigurationProperties(value = FrameProperties.class)
//WriterTemplate类 代表了要整合的框架(WriterTemplate)的核心API,
//这个配置类要想生效,需要有这个WriterTemplate类存在
//这个WriterTemplate类是存在第三方框架MyCustomFrame里面的,所以需要引入第三方框架的依赖
//因为有了WriterTemplate这个类,所以 这个 FrameAutoConfiguration 配置类就能生效了,这就是这个类的生效过程
@ConditionalOnClass(WriterTemplate.class)
//指定这个自动配置类需要位于DataSourceAutoConfiguration之后生效
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class FrameAutoConfiguration {


    /*
     * 使用 条件注解 和 @Bean 注解在容器中定义整合第三方框架(WriterTemplate)所需要的组件
     * 项目要整合第三方框架(MyCustomFrame),MyCustomFrame框架所需要的核心组件就是 WriterTemplate
     * 因此这个自动配置类就是负责在容器中自动配置 WriterTemplate
     */

    //FrameProperties 属性处理类,该属性处理类负责读取整合 MyCustomFrame 框架相关的配置信息
    private final FrameProperties frameProperties;
    //构造器
    public FrameAutoConfiguration(FrameProperties frameProperties) {
        this.frameProperties = frameProperties;
    }

    //配置Bean
    //此处需要传入 DataSource 来构建这个 writerTemplate,因此这个自动配置类需要在 DataSource 创建出来之后运行,
    //因此应该让这个自动配置类位于 DataSourceAutoConfiguration 之后生效

    //这个bean是如果引入starter组件的项目有连接数据库的配置,那么就会返回这个bean
    @Bean
    //只有当项目中 没有 WriterTemplate 这个bean时,才自动为项目配置这个Bean,
    //因为如果项目中有开发人员自己自定义开发 WriterTemplate 这个bean,那我们肯定不需要再额外自动配置 WriterTemplate 这个bean
    @ConditionalOnMissingBean
    //只有当spring容器中有且仅有一个 DataSource Bean 的时候,下面这个配置才生效
    @ConditionalOnSingleCandidate(DataSource.class)
    public WriterTemplate writerTemplate1(DataSource dataSource) throws SQLException {

        return new WriterTemplate(dataSource);
    }

    //这个bean是如果引入starter组件的项目没有连接数据库的配置,那么就会返回这个bean
    //这个bean主要是获取到引入该starter组件的项目里面的配置文件里面的dest属性和charset属性
    //具体是如何输出到数据库或是文件的逻辑,是在第三方框架的WriterTemplate类实现的
    @Bean
    @ConditionalOnMissingBean
    public WriterTemplate writerTemplate2() throws FileNotFoundException {
        //创建文件
        File file = new File(frameProperties.getDest());
        //指定字符
        Charset charset = Charset.forName(frameProperties.getCharset());

        return new WriterTemplate(file, charset);
    }
}

mycustomstarter 组件的 FrameProperties 属性处理类代码

package cn.ljh.mycustomstarter;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

//@ConfigurationProperties 这个注解修饰的类就是属性处理类
//指定该属性处理类只读取配置文件中,以 cn.ljh.frame 开头的配置属性
@ConfigurationProperties(prefix = FrameProperties.FRAME_PREFIX)
@Data
public class FrameProperties {

    public static final String FRAME_PREFIX = "cn.ljh.frame";

    //定义一些常用信息
    private String dest;
    private String charset;

}

mycustomstarter 组件的 spring.factories 配置文件

# 注册自动配置类 FrameAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  cn.ljh.mycustomstarter.FrameAutoConfiguration

mycustomstarter 组件的 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.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <!--  这个名称是符合springboot官方定义starter名的规格  -->
    <groupId>cn.ljh.mycustomstarter</groupId>
    <artifactId>mycustomframe-spring-boot-starter</artifactId>
    <version>1.0.0</version>

    <name>mycustomstarter</name>

    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <!--
            因为 mycustomstarter 这个 starter 组件有添加这个 第三方框架MyCustomFrame 的依赖,
            因此在以后的开发中,只需要在项目中添加这个 mycustomstarter 组件 ,
            这个starter组件就会帮我们添加被整合的框架 ->第三方框架 MyCustomFrame
            -->
        <dependency>
            <groupId>cn.ljh.customframe</groupId>
            <artifactId>MyCustomFrame</artifactId>
            <version>1.0.0</version>
        </dependency>

        <!--  属性处理类需要的依赖,用来解析配置文件的属性  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <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>
        </dependency>
    </dependencies>
</project>

CustomFrameTest

CustomFrameTest 项目里面的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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>cn.ljh</groupId>
    <artifactId>CustomFrameTest</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>

        <!--   添加mysql的驱动     -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--  添加自定义的starter组件  -->
        <dependency>
            <groupId>cn.ljh.mycustomstarter</groupId>
            <artifactId>mycustomframe-spring-boot-starter</artifactId>
            <version>1.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <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>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>


</project>

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_L_J_H_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值