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  xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     4.0.0
 5     
 6         org.springframework.boot
 7         spring-boot-starter-parent
 8         2.1.5.RELEASE
 9          
10     
11     com.example
12     hello-spring-boot-starter-autoconfigure
13     0.0.1-SNAPSHOT
14     hello-spring-boot-starter-autoconfigure
15     Demo project for Spring Boot
16 
17     
18         1.8
19     
20 
21     
22         
23             org.springframework.boot
24             spring-boot-starter
25         
26         
27             org.springframework.boot
28             spring-boot-autoconfigure
29         
30 
31         
32             org.springframework.boot
33             spring-boot-configuration-processor
34             true
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      */
15     private String name;
16 
17     /**
18      * 年龄
19      */
20     private Integer age;
21 
22     /**
23      * 家乡
24      */
25     private String hometown;
26 
27     public String getName() {
28         return name;
29     }
30 
31     public void setName(String name) {
32         this.name = name;
33     }
34 
35     public Integer getAge() {
36         return age;
37     }
38 
39     public void setAge(Integer age) {
40         this.age = age;
41     }
42 
43     public String getHometown() {
44         return hometown;
45     }
46 
47     public void setHometown(String hometown) {
48         this.hometown = hometown;
49     }
50 
51     @Override
52     public String toString() {
53         return "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      */
12     private String name;
13 
14     /**
15      * 年龄
16      */
17     private Integer age;
18 
19     /**
20      * 家乡
21      */
22     private String hometown;
23 
24     public HelloService(String name, Integer age, String hometown) {
25         this.name = name;
26         this.age = age;
27         this.hometown = hometown;
28     }
29 
30     public String sayHello(String name) {
31         return "Hello, " + name;
32     }
33 
34     public String helloWorld() {
35         return 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 
16     private final HelloProperties helloProperties;
17 
18     public HelloServiceAutoConfiguration(HelloProperties helloProperties) {
19         this.helloProperties = helloProperties;
20     }
21 
22     @Bean
23     @ConditionalOnMissingBean
24     public HelloService helloService() {
25         return new HelloService(this.helloProperties.getName(),
26                 this.helloProperties.getAge(),
27                 this.helloProperties.getHometown());
28     }
29 }

META-INF/spring.factories

1 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2   com.example.hello.HelloServiceAutoConfiguration 

mvn clean install

2.2.  hello-spring-boot-starter

在hello-spring-boot-starter中引用该autoconfigure模块

 1 
 2  xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     4.0.0
 5     com.example
 6     hello-spring-boot-starter
 7     0.0.1-SNAPSHOT
 8     hello-spring-boot-starter
 9 
10     
11         1.8
12     
13 
14     
15         
16             com.example
17             hello-spring-boot-starter-autoconfigure
18             0.0.1-SNAPSHOT
19         
20     
21 
22  

至此,我们的hello-spring-boot-starter开发完了

接下来,我们在demo中引用它

2.3.  demo

 1 
 2  xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     4.0.0
 5     
 6         org.springframework.boot
 7         spring-boot-starter-parent
 8         2.1.5.RELEASE
 9          
10     
11     com.example
12     demo
13     0.0.1-SNAPSHOT
14     demo
15 
16     
17         1.8
18         UTF-8
19     
20 
21     
22         
23             org.springframework.boot
24             spring-boot-starter-web
25         
26 
27         
28             com.example
29             hello-spring-boot-starter
30             0.0.1-SNAPSHOT
31         
32 
33         
34             org.springframework.boot
35             spring-boot-starter-test
36             test
37         
38     
39 
40     
41         
42             
43                 org.springframework.boot
44                 spring-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
20     private HelloService helloService;
21 
22     @GetMapping("/hello/{name}")
23     public String hello(@PathVariable("name") String name) {
24         return helloService.sayHello(name);
25     }
26 
27     @GetMapping("/info")
28     public String info() {
29         return helloService.helloWorld();
30     }
31 
32 }

3.  升级版的Hello World 

上面的例子中演示了我们引入自定义的starter,然后调用其提供的HelloService服务。感觉好像并没有什么用,下面在此基础上做个升级,再写一个记录日志的服务。 

3.1.  hello-spring-boot-starter-autoconfigure

pom.xml

 1 
 2  xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     4.0.0
 5     
 6         org.springframework.boot
 7         spring-boot-starter-parent
 8         2.1.5.RELEASE
 9          
10     
11     com.example
12     hello-spring-boot-starter-autoconfigure
13     0.0.1-SNAPSHOT
14     hello-spring-boot-starter-autoconfigure
15     Demo project for Spring Boot
16 
17     
18         1.8
19     
20 
21     
22         
23             org.springframework.boot
24             spring-boot-starter
25         
26         
27             org.springframework.boot
28             spring-boot-autoconfigure
29         
30         
31             org.springframework.boot
32             spring-boot-starter-web
33             true
34         
35         
36             org.projectlombok
37             lombok
38             true
39         
40         
41             com.alibaba
42             fastjson
43             1.2.58
44             true
45         
46         
47             org.springframework.boot
48             spring-boot-configuration-processor
49             true
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      */
19     String 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 
20     private static final ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();
21 
22     @Override
23     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
24         HandlerMethod handlerMethod = (HandlerMethod) handler;
25         Method method = handlerMethod.getMethod();
26         MyLog myLog = method.getAnnotation(MyLog.class);
27         if (null != myLog) {
28             //  设置开始时间
29             long startTime = System.currentTimeMillis();
30             startTimeThreadLocal.set(startTime);
31         }
32         return true;
33     }
34 
35     @Override
36     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
37         HandlerMethod handlerMethod = (HandlerMethod) handler;
38         Method method = handlerMethod.getMethod();
39         MyLog myLog = method.getAnnotation(MyLog.class);
40         if (null != myLog) {
41             //  获取开始时间
42             long startTime = startTimeThreadLocal.get();
43             long endTime = System.currentTimeMillis();
44             long expendTime = endTime - startTime;
45 
46             //  打印参数
47             String requestUri = request.getRequestURI();
48             String methodName = method.getDeclaringClass().getName() + "#" + method.getName();
49             String methodDesc = myLog.desc();
50             String parameters = JSON.toJSONString(request.getParameterMap());
51 
52             log.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
15     public void addInterceptors(InterceptorRegistry registry) {
16         registry.addInterceptor(new MyLogInterceptor());
17     }
18 }

META-INF/spring.factories

1 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2   com.example.hello.HelloServiceAutoConfiguration,\
3   com.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")
17     public String list() {
18         System.out.println("查询商品");
19         return "ok";
20     }
21 
22     @MyLog(desc = "添加商品")
23     @PostMapping("/save")
24     public String save(@RequestBody ProductVO productVO) {
25         System.out.println("添加商品");
26         return "ok";
27     }
28 
29     @MyLog(desc = "删除商品")
30     @GetMapping("/delete")
31     public String delete(@RequestParam("productId") Long productId) {
32         System.out.println("删除商品");
33         return "ok";
34     }
35 
36     @MyLog(desc = "获取商品详情")
37     @GetMapping("/detail/{productId}")
38     public String detail(@PathVariable("productId") Long productId) {
39         System.out.println("商品详情");
40         return "ok";
41     }
42 
43 } 

查看控制台

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

4.  工程结构

SpringBoot自定义Starter_第1张图片

5.  源码

https://github.com/chengjiansheng?tab=repositories

https://github.com/chengjiansheng/hello-spring-boot-starter-autoconfigure.git 

https://github.com/chengjiansheng/starter-demo.git 

 

你可能感兴趣的:(SpringBoot自定义Starter)