SpringBoot自定义Starter

1. 创建自己的Starter
一个完整的Spring Boot Starter可能包含以下组件:
autoconfigure模块:包含自动配置的代码
starter模块:提供对autoconfigure模块的依赖,以及一些其它的依赖
(PS:如果你不需要区分这两个概念的话,也可以将自动配置代码模块与依赖管理模块合并成一个模块)
简而言之,starter应该提供使用该库所需的一切
1.1. 命名
模块名称不能以spring-boot开头
如果你的starter提供了配置keys,那么请确保它们有唯一的命名空间。而且,不要用Spring Boot用到的命名空间(比如:servermanagementspring等等)
举个例子,假设你为“acme”创建了一个starter,那么你的auto-configure模块可以命名为acme-spring-boot-autoconfigure,starter模块可以命名为acme-spring-boot-starter。如果你只有一个模块包含这两部分,那么你可以命名为acme-spring-boot-starter
1.2.autoconfigure模块
建议在autoconfigure模块中包含下列依赖:
1
2 org.springframework.boot
3 spring-boot-autoconfigure-processor
4 true
5

1.3. starter模块
事实上,starter是一个空jar。它唯一的目的是提供这个库所必须的依赖。
你的starter必须直接或间接引用核心的Spring Boot starter(spring-boot-starter)
2. Hello Starter
接下来,作为入门,写一个Spring Boot版的Hello World
2.1.hello-spring-boot-starter-autoconfigure
新建一个Maven项目命名为hello-spring-boot-starter-autoconfigure
pom.xml
1
2 3xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
44.0.0
5
6org.springframework.boot
7spring-boot-starter-parent
82.1.5.RELEASE
9
10
11com.example
12hello-spring-boot-starter-autoconfigure
130.0.1-SNAPSHOT
14hello-spring-boot-starter-autoconfigure
15Demo project for Spring Boot
16
17
181.8
19
20
21
22
23org.springframework.boot
24spring-boot-starter
25

26
27org.springframework.boot
28spring-boot-autoconfigure
29

30
31
32org.springframework.boot
33spring-boot-configuration-processor
34true
35

36

37
38
HelloProperties.java
1 package com.example.hello;
2
3 import org.springframework.boot.context.properties.ConfigurationProperties;
4
5 /**
6* @author ChengJianSheng
7* @date 2019-05-26
8*/
9 @ConfigurationProperties("my.hello")
10 public class HelloProperties {
11
12/**
13* 姓名
14*/
15private String name;
16
17/**
18* 年龄
19*/
20private Integer age;
21
22/**
23* 家乡
24*/
25private String hometown;
26
27public String getName() {
28return name;
29}
30
31public void setName(String name) {
32this.name = name;
33}
34
35public Integer getAge() {
36return age;
37}
38
39public void setAge(Integer age) {
40this.age = age;
41}
42
43public String getHometown() {
44return hometown;
45}
46
47public void setHometown(String hometown) {
48this.hometown = hometown;
49}
50
51@Override
52public String toString() {
53return "HelloProperties{" +
54"name='" + name + '\'' +
55", age=" + age +
56", hometown='" + hometown + '\'' +
57'}';
58}
59 }
HelloService.java
1 package com.example.hello;
2
3 /**
4* @author ChengJianSheng
5* @date 2019-05-26
6*/
7 public class HelloService {
8
9/**
10* 姓名
11*/
12private String name;
13
14/**
15* 年龄
16*/
17private Integer age;
18
19/**
20* 家乡
21*/
22private String hometown;
23
24public HelloService(String name, Integer age, String hometown) {
25this.name = name;
26this.age = age;
27this.hometown = hometown;
28}
29
30public String sayHello(String name) {
31return "Hello, " + name;
32}
33
34public String helloWorld() {
35return String.format("[name=%s, age=%d, hometown=%s]", this.name, this.age, this.hometown);
36}
37
38 }
HelloServiceAutoConfiguration.java
1 package com.example.hello;
2
3 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
4 import org.springframework.boot.context.properties.EnableConfigurationProperties;
5 import org.springframework.context.annotation.Bean;
6 import org.springframework.context.annotation.Configuration;
7
8 /**
9* @author ChengJianSheng
10* @date 2019-05-26
11*/
12 @Configuration
13 @EnableConfigurationProperties(HelloProperties.class)
14 public class HelloServiceAutoConfiguration {
15
16private final HelloProperties helloProperties;
17
18public HelloServiceAutoConfiguration(HelloProperties helloProperties) {
19this.helloProperties = helloProperties;
20}
21
22@Bean
23@ConditionalOnMissingBean
24public HelloService helloService() {
25return new HelloService(this.helloProperties.getName(),
26this.helloProperties.getAge(),
27this.helloProperties.getHometown());
28}
29 }
META-INF/spring.factories
1 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2com.example.hello.HelloServiceAutoConfiguration
mvn clean install
2.2.hello-spring-boot-starter
在hello-spring-boot-starter中引用该autoconfigure模块
1
2 3xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
44.0.0
5com.example
6hello-spring-boot-starter
70.0.1-SNAPSHOT
8hello-spring-boot-starter
9
10
111.8
12
13
14
15
16com.example
17hello-spring-boot-starter-autoconfigure
180.0.1-SNAPSHOT
19

20

21
22
至此,我们的hello-spring-boot-starter开发完了
接下来,我们在demo中引用它
2.3. demo
1
2 3xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
44.0.0
5
6org.springframework.boot
7spring-boot-starter-parent
82.1.5.RELEASE
9
10
11com.example
12demo
130.0.1-SNAPSHOT
14demo
15
16
171.8
18UTF-8
19
20
21
22
23org.springframework.boot
24spring-boot-starter-web
25

26
27
28com.example
29hello-spring-boot-starter
300.0.1-SNAPSHOT
31

32
33
34org.springframework.boot
35spring-boot-starter-test
36test
37

38

39
40
41
42
43org.springframework.boot
44spring-boot-maven-plugin
45
46
47

48
49
application.properties
1 my.hello.name=程同学
2 my.hello.age=28
3 my.hello.hometown=湖北省随州市
DemoController.java
1 package com.example.demo.controller;
2
3 import com.example.hello.HelloService;
4 import org.springframework.web.bind.annotation.GetMapping;
5 import org.springframework.web.bind.annotation.PathVariable;
6 import org.springframework.web.bind.annotation.RequestMapping;
7 import org.springframework.web.bind.annotation.RestController;
8
9 import javax.annotation.Resource;
10
11 /**
12* @author ChengJianSheng
13* @date 2019-05-26
14*/
15 @RestController
16 @RequestMapping("/demo")
17 public class DemoController {
18
19@Resource
20private HelloService helloService;
21
22@GetMapping("/hello/{name}")
23public String hello(@PathVariable("name") String name) {
24return helloService.sayHello(name);
25}
26
27@GetMapping("/info")
28public String info() {
29return helloService.helloWorld();
30}
31
32 }
3. 升级版的Hello World
上面的例子中演示了我们引入自定义的starter,然后调用其提供的HelloService服务。感觉好像并没有什么用,下面在此基础上做个升级,再写一个记录日志的服务。
3.1.hello-spring-boot-starter-autoconfigure
pom.xml
1
2 3xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
44.0.0
5
6org.springframework.boot
7spring-boot-starter-parent
82.1.5.RELEASE
9
10
11com.example
12hello-spring-boot-starter-autoconfigure
130.0.1-SNAPSHOT
14hello-spring-boot-starter-autoconfigure
15Demo project for Spring Boot
16
17
181.8
19
20
21
22
23org.springframework.boot
24spring-boot-starter
25

26
27org.springframework.boot
28spring-boot-autoconfigure
29

30
31org.springframework.boot
32spring-boot-starter-web
33true
34

35
36org.projectlombok
37lombok
38true
39

40
41com.alibaba
42fastjson
431.2.58
44true
45

46
47org.springframework.boot
48spring-boot-configuration-processor
49true
50

51

52
53
MyLog.java
1 package com.example.log;
2
3 import java.lang.annotation.ElementType;
4 import java.lang.annotation.Retention;
5 import java.lang.annotation.RetentionPolicy;
6 import java.lang.annotation.Target;
7
8 /**
9* @author ChengJianSheng
10* @date 2019-05-26
11*/
12 @Target(ElementType.METHOD)
13 @Retention(RetentionPolicy.RUNTIME)
14 public @interface MyLog {
15
16/**
17* 方法描述
18*/
19String desc() default "";
20 }
MyLogInterceptor.java
1 package com.example.log;
2
3 import com.alibaba.fastjson.JSON;
4 import lombok.extern.slf4j.Slf4j;
5 import org.springframework.web.method.HandlerMethod;
6 import org.springframework.web.servlet.ModelAndView;
7 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
8
9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11 import java.lang.reflect.Method;
12
13 /**
14* @author ChengJianSheng
15* @date 2019-05-26
16*/
17 @Slf4j
18 public class MyLogInterceptor extends HandlerInterceptorAdapter {
19
20private static final ThreadLocal startTimeThreadLocal = new ThreadLocal<>();
21
22@Override
23public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
24HandlerMethod handlerMethod = (HandlerMethod) handler;
25Method method = handlerMethod.getMethod();
26MyLog myLog = method.getAnnotation(MyLog.class);
27if (null != myLog) {
28//设置开始时间
29long startTime = System.currentTimeMillis();
30startTimeThreadLocal.set(startTime);
31}
32return true;
33}
34
35@Override
36public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
37HandlerMethod handlerMethod = (HandlerMethod) handler;
38Method method = handlerMethod.getMethod();
39MyLog myLog = method.getAnnotation(MyLog.class);
40if (null != myLog) {
41//获取开始时间
42long startTime = startTimeThreadLocal.get();
43long endTime = System.currentTimeMillis();
44long expendTime = endTime - startTime;
45
46//打印参数
47String requestUri = request.getRequestURI();
48String methodName = method.getDeclaringClass().getName() + "#" + method.getName();
49String methodDesc = myLog.desc();
50String parameters = JSON.toJSONString(request.getParameterMap());
51
52log.info("\n描述:{}\n路径: {}\n方法: {}\n参数:{}\n耗时:{}", methodDesc, requestUri, methodName, parameters, expendTime);
53}
54}
55 }
MyLogAutoConfiguration.java
1 package com.example.log;
2
3 import org.springframework.context.annotation.Configuration;
4 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
5 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
6
7 /**
8* @author ChengJianSheng
9* @date 2019-05-26
10*/
11 @Configuration
12 public class MyLogAutoConfiguration implements WebMvcConfigurer {
13
14@Override
15public void addInterceptors(InterceptorRegistry registry) {
16registry.addInterceptor(new MyLogInterceptor());
17}
18 }
META-INF/spring.factories
1 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2com.example.hello.HelloServiceAutoConfiguration,\
3com.example.log.MyLogAutoConfiguration
3.2. demo
ProductController.java
1 package com.example.demo.controller;
2
3 import com.example.demo.domain.ProductVO;
4 import com.example.log.MyLog;
5 import org.springframework.web.bind.annotation.*;
6
7 /**
8* @author ChengJianSheng
9* @date 2019-05-26
10*/
11 @RestController
12 @RequestMapping("/product")
13 public class ProductController {
14
15@MyLog(desc = "查询商品")
16@GetMapping("/list")
17public String list() {
18System.out.println("查询商品");
19return "ok";
20}
21
22@MyLog(desc = "添加商品")
23@PostMapping("/save")
24public String save(@RequestBody ProductVO productVO) {
25System.out.println("添加商品");
26return "ok";
27}
28
29@MyLog(desc = "删除商品")
30@GetMapping("/delete")
31public String delete(@RequestParam("productId") Long productId) {
32System.out.println("删除商品");
33return "ok";
34}
35
36@MyLog(desc = "获取商品详情")
37@GetMapping("/detail/{productId}")
38public String detail(@PathVariable("productId") Long productId) {
39System.out.println("商品详情");
40return "ok";
41}
42
43 }
查看控制台
SpringBoot自定义Starter
文章图片


(PS:这里就打印日志这个功能没有使用AOP,因为这意味着在自动配置的代码中就要定义切面、切点这些东西,而且在项目启动的时候还要扫描切面,感觉比较麻烦)
4. 工程结构
SpringBoot自定义Starter
文章图片


SpringBoot自定义Starter
文章图片


欢迎工作一到五年的Java工程师朋友们加入Java程序员开发: 721575865
【SpringBoot自定义Starter】群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

    推荐阅读