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

欢迎工作一到五年的Java工程师朋友们加入Java程序员开发: 721575865

群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容