你还不会手写SpringBoot启动器吗

Starter是什么 ?

Spring Boot 对比 Spring MVC 最大的优点就是使用简单,约定大于配置。不用 Spring MVC 的时候,时不时被 xml 配置文件搞的晕头转向,冷不防还因为 xml 配置上的一点疏忽,导致整个项目莫名其妙的不可用,顿感生无可恋。这要归功于Spring Boot 的各种各样的 starters,有官方提供以及第三方开源出来的。这些依赖模块都遵循着约定成俗的默认配置,并允许我们根据自身情况调整这些配置。基本上你打算用的功能都可以找到,如果没有找到,那就请再找一找。SpringBoot 会自动扫描需要加载的信息并启动相应的默认配置。

Starter 提供了以下功能:

  • 整合了模块需要的所有依赖,统一集合到 Starter 中。
  • 提供了默认配置,并允许我们调整这些默认配置。
  • 提供了自动配置类对模块内的 Bean 进行自动装配,注入 Spring 容器中。

Starter 命名规则

Spring 官方定义的 Starter 通常命名遵循的格式为 spring-boot-starter-{name},例如 spring-boot-starter-data-mongodb。Spring 官方建议,非官方 Starter 命名应遵循 {name}-spring-boot-starter 的格式,例如,myjson-spring-boot-starter。

Starter可以用来解决什么问题?

日常开发中有一些独立于业务系统之外的配置模块,它是可以在不同项目中进行复用的。如果在每个项目中都编写重复的模块代码,除了浪费时间和人力耦合性还高。将这些可独立于业务代码之外的功能配置模块封装成一个 Starter,在需要用到此功能模块的项目中,只需要在其 pom.xml 文件中引用依赖即可。

手写SpringBoot启动器的主要步骤

  1. 定义业务接口、需要的实体类等业务对象
  2. 定义配置属性类、
  3. 定义自动装配类、
  4. Resources目录下新建META-INF/spring.factories,加入自定义starter的配置项
  1. idea——new  project——spring initializr——填写group、artifact名称,选择java的jdk版本——next ——next——finish

pom文件依赖

<?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.4</version>

        <relativePath/> <!-- lookup parent from repository -->

    </parent>

    <groupId>com.example</groupId>

    <artifactId>my-starter</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <name>my-starter</name>

    <description>Demo project for Spring Boot</description>

    <properties>

        <java.version>1.8</java.version>

    </properties>

    <dependencies>

        <dependency>

            <groupId>com.example</groupId>

            <artifactId>myjson-spring-boot-starter-autoconfigurer</artifactId>

            <version>0.0.1-SNAPSHOT</version>

        </dependency>

    </dependencies>

 

    <build>

        <plugins>

            <plugin>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-maven-plugin</artifactId>

            </plugin>

        </plugins>

    </build>

 

</project>

 

  1. 在当前工程创建module——name为myjson,流程同上

<?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.4</version>

        <relativePath/> <!-- lookup parent from repository -->

    </parent>

    <groupId>com.example</groupId>

    <artifactId>myjson-spring-boot-starter-autoconfigurer</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <name>myjson-spring-boot-starter-autoconfigurer</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>

            <version>2.3.8.RELEASE</version>

            <optional>true</optional>

        </dependency>

        <dependency>

            <groupId>com.alibaba</groupId>

            <artifactId>fastjson</artifactId>

            <version>1.2.73</version>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-autoconfigure</artifactId>

            <version>2.3.8.RELEASE</version>

        </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>

                    <mainClass>com.example.myjson.MyjsonApplication</mainClass>

                    <skip>true</skip>

                </configuration>

            </plugin>

        </plugins>

    </build>

 

</project>

加红色的三个依赖,fastjson是我们需要的第三方依赖,其他的spring-boot-configuration-processor 依赖是可选的,加入此依赖主要是打包时,自动生成配置元信息文件 META-INF/spring-configuration-metadata.json,并放入到 jar 中。绿色的artifact是我们自定义starter的名称,蓝色的插件依赖是我们

  1. 手写starter
  1. 定义我们的service业务类:主要是对传入的对象转换为json字符串,同时在其首位做额外的拼接处理

public class MyJsonService {

    private String prefixName;

    private String suffixName;

 

    public String getPrefixName() {

        return prefixName;

    }

 

    public void setPrefixName(String prefixName) {

        this.prefixName = prefixName;

    }

 

    public String getSuffixName() {

        return suffixName;

    }

 

    public void setSuffixName(String suffixName) {

        this.suffixName = suffixName;

    }

 

    public String objectToMyJson(Object obj) {

        return prefixName + JSON.toJSONString(obj) + suffixName;

    }

}

 

  1. 定义控制的实体类对象

public class Person {

    private String name;

    private int age;

    private String address;

    public Person(String name, int age, String address) {

        super();

        this.name = name;

        this.age = age;

        this.address = address;

    }

 

    public String getName() {

        return name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

 

    public int getAge() {

        return age;

    }

 

    public void setAge(int age) {

        this.age = age;

    }

 

    public String getAddress() {

        return address;

    }

 

    public void setAddress(String address) {

        this.address = address;

    }

}

 

  1. 定义我们的属性配置类(也即yml配置文件中配置的属性)

/**

 * @author:cp

 * @time:2021-4-12

 * @Description: 配置类(类名一般为模块名+Properties) cp.json为Starter使用者通过yml配置文件动态修改属性值的变量名前缀

 */

@ConfigurationProperties(prefix = "cp.json")

public class MyJsonProperties {

    // Starter使用者没在配置文件中配置prefixName属性的值时的默认值

    public static final String DEFAULT_PREFIX_NAME = "@";

 

    // Starter使用者没在配置文件中配置suffixName属性的值时的默认值

    public static final String DEFAULT_SUFFIX_NAME = "#";

    private String prefixName = DEFAULT_PREFIX_NAME;

    private String suffixName = DEFAULT_SUFFIX_NAME;

 

    public String getPrefixName() {

        return prefixName;

    }

 

    public void setPrefixName(String prefixName) {

        this.prefixName = prefixName;

    }

 

    public String getSuffixName() {

        return suffixName;

    }

 

    public void setSuffixName(String suffixName) {

        this.suffixName = suffixName;

    }

}

 

  1. 定义自动装配类,让spring装配我们的service业务类,同时调用我们的属性配置类,让业务service类获取到属性配置类的值并进行处理

package com.example.myjson.config;

 

import com.example.myjson.service.MyJsonService;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;

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;

 

/**

 * @author:cp

 * @time:2021-4-12

 * @Description: 自动装配类

 */

@Configuration

@ConditionalOnClass(MyJsonService.class) // 表示只有指定的classclasspath上时才能被注册

@EnableConfigurationProperties(MyJsonProperties.class)

// 激活被@ConfigurationProperties注解修饰的类

public class MyJsonConfiguration {

    private MyJsonProperties myJsonProperties;

 

    // 自动注入配置类

    public MyJsonConfiguration(MyJsonProperties myJsonProperties) {

        this.myJsonProperties = myJsonProperties;

    }

 

    // 创建MyJsonService对象,注入到Spring容器中

    @Bean

    @ConditionalOnMissingBean(MyJsonService.class)  // 当容器没有此bean时,才注册

    public MyJsonService myJsonService() {

        MyJsonService myJsonService = new MyJsonService();

        myJsonService.setPrefixName(myJsonProperties.getPrefixName());

        myJsonService.setSuffixName(myJsonProperties.getSuffixName());

        return myJsonService;

    }

}

 

  1. 在我们的启动类模块的resources文件下创建META-INF/spring.factories 文件,增加如下内容,将自动装配的类配置上

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

com.example.myjson.config.MyJsonConfiguration

 

  1. 上述工作完成后,我们可以使用mvn install命令在本地仓库生成我们手写的starter的jar,可以提供给他其他工程依赖引用。也可以快捷的使用idea提供的快捷打包
  2. 新建一个工程,名称为kk,流程同(1)中,在pom文件中引入我们的starter依赖,

<?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.4</version>

        <relativePath/> <!-- lookup parent from repository -->

    </parent>

    <groupId>com.example</groupId>

    <artifactId>kk</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <name>kk</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>com.example</groupId>

            <artifactId>myjson-spring-boot-starter-autoconfigurer</artifactId>

            <version>0.0.1-SNAPSHOT</version>

        </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>

            </plugin>

        </plugins>

    </build>

 

</project>

 

  1. 更新maven依赖后,可以看我们的自定义starter已经被引入进来,在kk工程的resources文件下将application.properties文件改为application.yml并进行如下配置

cp:

  json:

    prefix-name: hello

    suffix-name: world

 

  1. 在kkApplication下新建controller

@RestController

@RequestMapping("demo")

public class DemoController {

    @Autowired

    private MyJsonService myJsonService;

 

    @GetMapping()

    public String test() {

        Person p = new Person(“千与千寻” , 18, "拉斯维加斯");

        // 调用服务方法

        return myJsonService.objectToMyJson(p);

    }

}

引入我们自定义启动器中的业务service类,启动springboot工程,访问http://localhost:8080/demo,验证我们的service类即可。

中间问题点:在打包自定义启动器类jar包本地仓库时,没有添加构建插件,导致生成包多了一个BOOT-INF的文件,无法被其他工程引用导入自定义service类:添加如下构建插件后问题解决:

<build>

        <plugins>

            <plugin>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-maven-plugin</artifactId>

                <configuration>

                    <mainClass>com.example.myjson.MyjsonApplication</mainClass>

                    <skip>true</skip>

                </configuration>

            </plugin>

        </plugins>

    </build>

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/419096.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ace教你一步一步做Android新闻客户端(三) JSON数据解析

对于服务器端来说&#xff0c;返回给客户端的数据格式一般分为html、xml和json这三种格式&#xff0c;现在给大家讲解一下json这个知识点&#xff0c; 1 如何通过json-lib和gson这两个json解析库来对解析我们的json数据&#xff0c; 2 以及如何在我们的Android客户端解析来自服…

前端学习(1732):前端系列javascript之插入内容

index.vue <template><view class"content"><view v-if"list.length!0" class"todo-header"><view class"todo-header_left"><text class"active-text">全部</text><text>10条&…

你会通过Docker部署war包吗

1、 使用Docker部署war&#xff0c;必须要用容器&#xff0c;我们就用tomcact容器&#xff0c;其实都是将war包丢到tomcat的webapps目录下&#xff0c;tomcat启动的情况下会自动解压war包。 一种是在Docker中安装tomcat容器的镜像&#xff0c;然后把war包丢到tomcat镜像下weba…

Yii中使用的简单方法

1. $this->render: 输出父模版的内容&#xff0c;将渲染的内容嵌入父模版。 2. $this->renderPartial: 不输出父模版的内容&#xff0c;只对本次渲染的布局内容进行输出。 3. PDostatement::bindValue: 把一个值绑定到一个参数。 PDostatement::bindParam: 绑定一个参数到…

前端学习(1732):前端系列javascript之状态切换

index.vue <template><view class"content"><view v-if"list.length!0" class"todo-header"><view class"todo-header_left"><text class"active-text">全部</text><text>10条&…

记一次若依ruoyi-ui(Vue2) 关闭tab页并打开新页面

网上教程很多&#xff0c;但是都是给前端代码段&#xff0c;都不知道怎么使用&#xff08;本人菜鸟一个&#xff09;&#xff0c;今天记一次完整的&#xff1a; 在你需要关闭的tab页面&#xff0c;加入以下代码&#xff1a; handleCommit()是我需要关闭页面的方法&#xff0c…

log4j自定义配置文件(SpringMVC项目)

问题来源 本周在实际项目中发现无法自定义的log4j-dev配置的error日志级别文件无法生效&#xff0c;项目启动后仍然采用默认的info级别日志进行打印。之所以自定义名称&#xff0c;是为了减少隔离不同环境的日志级别&#xff0c;比如开发dev环境使用debug、info级别&#xff0…

Redis的Java客户端Jedis的八种调用方式(事务、管道、分布式)介绍

一、普通同步方式二、事务方式(Transactions)三、管道(Pipelining)四、管道中调用事务五、分布式直连同步调用六、分布式直连异步调用七、分布式连接池同步调用八、分布式连接池异步调用九、需要注意的地方十、测试十一、完整的测试代码jedis是一个著名的key-value存储系统&…

23 | 二叉树基础(上):什么样的二叉树适合用数组来存储?

思考题 二叉树有哪几种存储方式&#xff1f;什么样的二叉树适合用数组来存储&#xff1f; 树&#xff08;Tree&#xff09; 根节点&#xff1a;没有父节点的节点叶子节点或者叶节点&#xff1a;没有子节点的节点叫做 树的高度、深度、层&#xff1a; 举例说明&#xff1a; 生…

HBase 手动 flush 机制梳理

对应 HBase 版本0.94.1&#xff0c;对照了开源的版本和工作使用的某发行版 问题&#xff1a;在 HBase shell 里面输入 flush table_or_region_name之后&#xff0c;发生了什么&#xff1f;具体的实现是怎么样的&#xff1f;对于现有的某个表&#xff0c;我如何在做操作之前估算…

寻找字符串中第一个仅仅出现一次打字符

寻找字符串中第一个仅仅出现一次打字符 代码例如以下&#xff1a; #include <iostream> #include <string>using namespace std;char findfirstoncechar(string &str) {int arr[255] {0};int i;for (i 0; i<str.size();i){arr[str[i]];}for(i 0; i < …

从0到1搞一波dubbo

1、为什么需要dubbo&#xff1f;&#xff08;为了解决什么问题&#xff1f;&#xff09; 架构演变 1 单一应用架构 2 应用和数据库单独部署 3 应用和数据库集群部署 4 数据库压力变大&#xff0c;读写分离 5 使用缓存技术加快速度 6 数据库分库分表 7 应用分为不同的类型拆分 …

前端学习(1734):前端系列javascript之添加动画

<template><view class"content"><!-- 状态栏 --><view v-if"list.length ! 0" class"todo-header"><!-- 状态栏的左侧 --><view class"todo-header__left"><text class"active-text&quo…

android146 360 病毒查杀

<?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:orientatio…

24 | 二叉树基础(下):有了如此高效的散列表,为什么还需要二叉树?

这节学习一种特殊的二叉树—二叉查找树。它最大的特点是支持动态数据集合的快速插入、删除、查找操作。但是散列表也是支持这些操作的&#xff0c;并且散列表的这些操作比二叉查找树更高效&#xff0c;时间复杂度是 O(1)。 问题引入 既然有高效的散列表&#xff0c;二叉树的地…

【可持久化线段树】【主席树】[HDU4417]Super Mario

Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory. Now the poor princess is in trouble again and Mario needs to save his lover. We regard the road to the boss’s castle as a line (the length is n), on ev…

25 | 红黑树(上):为什么工程中都用红黑树这种二叉树?

问题引入 二叉查找树在频繁的动态更新过程中&#xff0c;可能会出现树的高度远大于 log2n 的情况&#xff0c;从而导致各个操作的效率下降。极端情况下&#xff0c;二叉树会退化为链表&#xff0c;时间复杂度会退化到 O(n)。要解决这个复杂度退化的问题&#xff0c;需要设计一…