目录
制作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"
再次运行,发现输出的配置已经被改变了。