如何手写一个SpringBoot Starter

制作starter

我们通过手写一个starter,实现一个这样的功能:如果存在FastJson包则将对象以json形式输出,否则以对象的toString()输出。另外引入这个starter后我们不希望有任何的配置(自动配置),开箱即用。此文并不探究starter的原理,只希望读者可以初步了解一个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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lqb</groupId>
    <artifactId>demo-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <version>2.2.1.RELEASE</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.56</version>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

定义接口

定义一个转化方法的接口Formater ,然后分别用两个类来实现它

public interface Formater {
    String format(Object o);
}
/**
 * 将对象toString形式输出
 */
public class StringFormater implements Formater {
    public String format(Object o) {
        return "String: " + o.toString();
    }
}
import com.alibaba.fastjson.JSON;

/**
 * 将对象Json形式输出
 */
public class JsonFormater implements Formater{
    public String format(Object o) {
        return "JSON: " + JSON.toJSONString(o);
    }
}

依赖注入Formater

接着我们将Formater 纳入Spring的IOC进行管理

import com.lqb.demo.starter.formater.Formater;
import com.lqb.demo.starter.formater.JsonFormater;
import com.lqb.demo.starter.formater.StringFormater;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class FormaterConfig {

	//如果不存在“JSON”这个类那么我们实例化StringFormater
    @ConditionalOnMissingClass("com.alibaba.fastjson.JSON")
    @Bean
    @Primary
    public Formater stringFormater() {
        return new StringFormater();
    }

	//如果存在“JSON”这个类那么我们实例化JsonFormater
    @ConditionalOnClass(name = "com.alibaba.fastjson.JSON")
    @Bean
    public Formater jsonFormater() {
        return new JsonFormater();
    }
}

定义配置类

定义一个配置类,里面定义我们需要的配置,并初始化默认值。

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

//指定配置前缀
@ConfigurationProperties(prefix="demo.starter")
public class FormaterProperties {

	//性别
    private String sex;

	//手机号码
    private String mobile;

    //指定默认值
    public FormaterProperties() {
        this.sex = "boy";
        this.mobile = "110";
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }
}

通过Template暴露API

接着我们来写一个Template来对外提供API

public class DemoTemplate {

    private Formater formater;
    private FormaterProperties properties;

    public DemoTemplate(Formater formater, FormaterProperties properties) {
        this.formater = formater;
        this.properties = properties;
    }

    public String format(Object o) {
        StringBuilder sb = new StringBuilder();
        //调用formater序列化对象
        sb.append(formater.format(o)).append("\n")
        		//输出默认配置                
        		.append("我的性别是:" + properties.getSex()).append("\n")
                .append("我的手机是:" + properties.getMobile());
        return sb.toString();
    }
}

依赖注入Template

同样的,要想Template能被调用者时刻依赖注入,依然需要将它纳入Spring的管理

import com.lqb.demo.starter.DemoTemplate;
import com.lqb.demo.starter.formater.Formater;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//导入FormaterConfig
@Import(FormaterConfig.class)
//导入我们配置类FormaterProperties
@EnableConfigurationProperties(FormaterProperties.class)
//将DemoAutoConfig纳入Spring的管理
@Configuration
public class DemoAutoConfig {

    @Autowired
    private Formater formater;
    @Autowired
    private FormaterProperties properties;

    @Bean
    public DemoTemplate template() {
        return new DemoTemplate(formater, properties);
    }

}

配置spring.factories

如果读者对JDK的SPI机制熟悉的话,对下面的配置应该感到很熟悉。接下来我们需要在resources目录下创建META-INF文件夹,并在这个文件夹中新建一个名叫spring.factories的文件,如下图红框中所示。
在这里插入图片描述
spring.factories中我们指定需要自动配置的配置类DemoAutoConfig,Spring在启动时会自动加载这个配置文件,找到我们的配置类,注意配置的是全类名。细心的同学可能会问,FormaterConfig也需要自动配置呀,怎么这里怎么只需要配置DemoAutoConfig呢,因为我们在DemoAutoConfig中用到了@Import注解将它导入进来啦。

# key是固定的,约定好的,不能修改。value是DemoAutoConfig的全类名
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.lqb.demo.starter.autoconfig.DemoAutoConfig

到这里我们starter的代码就全部完成了,运行命令mvn clean install将我们的工程发布到本地仓库。

使用starter

引入starter

下面我们新建一个SpringBoot工程的项目导入我们写好的这个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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.lqb</groupId>
	<artifactId>spring-boot-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-boot-demo</name>
	<description>Demo project for Spring Boot</description>

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

	<dependencies>
		<!-- 核心模块,包括自动配置支持、日志和YAML -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		
		<!--导入我们前面写好的starter-->
		<dependency>
            <groupId>com.lqb</groupId>
            <artifactId>demo-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>

定义pojo

Template需要序列化输出一个类,现在我们就来定义一个类,并重写它的toString方法

public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

	//TODO 这里省略了get和set方法

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}

对象toString形式输出

下面就可以启动SpringBoot,并使用Template来序列化输出User了。

import com.lqb.demo.starter.DemoTemplate;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class AutoConfigApplication {
    public static void main(String[] args) {
    	//获取ApplicationContext
        ConfigurableApplicationContext context = SpringApplication.run(AutoConfigApplication.class, args);
        //从ApplicationContext中拿到template
        DemoTemplate template = context.getBean(DemoTemplate.class);
        //调用template的format方法序列化User
        System.out.println(template.format(new User("jack", 123)));
    }
}

因为我们pom中没有引入fastjson包,所以这个时候DemoTemplate注入的实例是StringFormater,因此最终运行main函数的输出会如下图所示。
在这里插入图片描述

对象json形式输出

接着我们在pom中导入fastjson包

<?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>com.lqb</groupId>
    <artifactId>spring-boot-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-demo</name>
    <description>Demo project for Spring Boot</description>

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

    <dependencies>
        <!-- 核心模块,包括自动配置支持、日志和YAML -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!--导入我们前面写好的starter-->
        <dependency>
            <groupId>com.lqb</groupId>
            <artifactId>demo-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

		<!--导入fastjson包-->
		<dependency>
	        <groupId>com.alibaba</groupId>
	        <artifactId>fastjson</artifactId>
	        <version>1.2.56</version>
    	</dependency>
    </dependencies>

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

</project>

再次运行AutoConfigApplication,发现确实是User被json的形式输出了。
在这里插入图片描述
为了使用这个starter,我们只是在pom中进行了引入,并没有进行任何的配置,这就是SpringBoot自动配置了。

修改默认配置

有时候我们需要修改默认的配置,这该如何实现呢?在application.properties中修改默认配置,demo.starter是我们在starter中的FormaterProperties 定义好的前缀,sex和mobile我们从原来默认的"boy"和"110"替换成"girl"和"520"。

demo.starter.sex="girl"
demo.starter.mobile="520"

再次运行,发现输出的配置已经被改变了。
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值