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 <dependency>
2 	<groupId>org.springframework.boot</groupId>
3 	<artifactId>spring-boot-autoconfigure-processor</artifactId>
4 	<optional>true</optional>
5 </dependency>

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 <?xml version="1.0" encoding="UTF-8"?>
 2 <project 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     <modelVersion>4.0.0</modelVersion>
 5     <parent>
 6         <groupId>org.springframework.boot</groupId>
 7         <artifactId>spring-boot-starter-parent</artifactId>
 8         <version>2.1.5.RELEASE</version>
 9         <relativePath/> <!-- lookup parent from repository -->
10     </parent>
11     <groupId>com.example</groupId>
12     <artifactId>hello-spring-boot-starter-autoconfigure</artifactId>
13     <version>0.0.1-SNAPSHOT</version>
14     <name>hello-spring-boot-starter-autoconfigure</name>
15     <description>Demo project for Spring Boot</description>
16 
17     <properties>
18         <java.version>1.8</java.version>
19     </properties>
20 
21     <dependencies>
22         <dependency>
23             <groupId>org.springframework.boot</groupId>
24             <artifactId>spring-boot-starter</artifactId>
25         </dependency>
26         <dependency>
27             <groupId>org.springframework.boot</groupId>
28             <artifactId>spring-boot-autoconfigure</artifactId>
29         </dependency>
30 
31         <dependency>
32             <groupId>org.springframework.boot</groupId>
33             <artifactId>spring-boot-configuration-processor</artifactId>
34             <optional>true</optional>
35         </dependency>
36     </dependencies>
37 
38 </project> 

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 <?xml version="1.0" encoding="UTF-8"?>
 2 <project 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     <modelVersion>4.0.0</modelVersion>
 5     <groupId>com.example</groupId>
 6     <artifactId>hello-spring-boot-starter</artifactId>
 7     <version>0.0.1-SNAPSHOT</version>
 8     <name>hello-spring-boot-starter</name>
 9 
10     <properties>
11         <java.version>1.8</java.version>
12     </properties>
13 
14     <dependencies>
15         <dependency>
16             <groupId>com.example</groupId>
17             <artifactId>hello-spring-boot-starter-autoconfigure</artifactId>
18             <version>0.0.1-SNAPSHOT</version>
19         </dependency>
20     </dependencies>
21 
22 </project> 

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

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

2.3.  demo

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project 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     <modelVersion>4.0.0</modelVersion>
 5     <parent>
 6         <groupId>org.springframework.boot</groupId>
 7         <artifactId>spring-boot-starter-parent</artifactId>
 8         <version>2.1.5.RELEASE</version>
 9         <relativePath/> <!-- lookup parent from repository -->
10     </parent>
11     <groupId>com.example</groupId>
12     <artifactId>demo</artifactId>
13     <version>0.0.1-SNAPSHOT</version>
14     <name>demo</name>
15 
16     <properties>
17         <java.version>1.8</java.version>
18         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
19     </properties>
20 
21     <dependencies>
22         <dependency>
23             <groupId>org.springframework.boot</groupId>
24             <artifactId>spring-boot-starter-web</artifactId>
25         </dependency>
26 
27         <dependency>
28             <groupId>com.example</groupId>
29             <artifactId>hello-spring-boot-starter</artifactId>
30             <version>0.0.1-SNAPSHOT</version>
31         </dependency>
32 
33         <dependency>
34             <groupId>org.springframework.boot</groupId>
35             <artifactId>spring-boot-starter-test</artifactId>
36             <scope>test</scope>
37         </dependency>
38     </dependencies>
39 
40     <build>
41         <plugins>
42             <plugin>
43                 <groupId>org.springframework.boot</groupId>
44                 <artifactId>spring-boot-maven-plugin</artifactId>
45             </plugin>
46         </plugins>
47     </build>
48 
49 </project>

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 <?xml version="1.0" encoding="UTF-8"?>
 2 <project 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     <modelVersion>4.0.0</modelVersion>
 5     <parent>
 6         <groupId>org.springframework.boot</groupId>
 7         <artifactId>spring-boot-starter-parent</artifactId>
 8         <version>2.1.5.RELEASE</version>
 9         <relativePath/> <!-- lookup parent from repository -->
10     </parent>
11     <groupId>com.example</groupId>
12     <artifactId>hello-spring-boot-starter-autoconfigure</artifactId>
13     <version>0.0.1-SNAPSHOT</version>
14     <name>hello-spring-boot-starter-autoconfigure</name>
15     <description>Demo project for Spring Boot</description>
16 
17     <properties>
18         <java.version>1.8</java.version>
19     </properties>
20 
21     <dependencies>
22         <dependency>
23             <groupId>org.springframework.boot</groupId>
24             <artifactId>spring-boot-starter</artifactId>
25         </dependency>
26         <dependency>
27             <groupId>org.springframework.boot</groupId>
28             <artifactId>spring-boot-autoconfigure</artifactId>
29         </dependency>
30         <dependency>
31             <groupId>org.springframework.boot</groupId>
32             <artifactId>spring-boot-starter-web</artifactId>
33             <optional>true</optional>
34         </dependency>
35         <dependency>
36             <groupId>org.projectlombok</groupId>
37             <artifactId>lombok</artifactId>
38             <optional>true</optional>
39         </dependency>
40         <dependency>
41             <groupId>com.alibaba</groupId>
42             <artifactId>fastjson</artifactId>
43             <version>1.2.58</version>
44             <optional>true</optional>
45         </dependency>
46         <dependency>
47             <groupId>org.springframework.boot</groupId>
48             <artifactId>spring-boot-configuration-processor</artifactId>
49             <optional>true</optional>
50         </dependency>
51     </dependencies>
52 
53 </project>

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.  工程结构

5.  源码

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

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

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

 

posted @ 2019-05-27 09:34 废物大师兄 阅读(...) 评论(...) 编辑 收藏