SpringBoot自定義Starter

1. 建立本身的Starterjava

一個完整的Spring Boot Starter可能包含如下組件:程序員

  • autoconfigure模塊:包含自動配置的代碼
  • starter模塊:提供對autoconfigure模塊的依賴,以及一些其它的依賴

(PS:若是你不須要區分這兩個概念的話,也能夠將自動配置代碼模塊與依賴管理模塊合併成一個模塊)web

簡而言之,starter應該提供使用該庫所需的一切spring

1.1. 命名sql

  • 模塊名稱不能以spring-boot開頭
  • 若是你的starter提供了配置keys,那麼請確保它們有惟一的命名空間。並且,不要用Spring Boot用到的命名空間(好比:servermanagementspring 等等)

舉個例子,假設你爲「acme」建立了一個starter,那麼你的auto-configure模塊能夠命名爲acme-spring-boot-autoconfigure,starter模塊能夠命名爲acme-spring-boot-starter。若是你只有一個模塊包含這兩部分,那麼你能夠命名爲acme-spring-boot-starterapache

1.2. autoconfigure模塊json

建議在autoconfigure模塊中包含下列依賴:bash

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. 工程結構


歡迎工做一到五年的Java工程師朋友們加入Java程序員開發: 721575865

羣內提供免費的Java架構學習資料(裏面有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!

相關文章
相關標籤/搜索