一、前言

到现在,我们可以看出来,如果我们想用一些功能,基本上都是通过添加spring-boot-starter的方式来使用的,因为各种各样的功能都被封装成了starter,然后把相关服务注入到容器中去,那么如果我们想用一下自己的某些功能呢,那就也要编写一个自定义的starter,所以今天我们就来看看,怎么编写自己的starter,来实现在springboot中实现自己想要的一些功能。

二、原理

回想一下, 当我们使用一个官方的starter的时候,只需要将相应的starter依赖包添加到pom.xml中去就可以了,然后maven自动下载依赖包,并且springboot在启动后会自动配置相关的服务,然后注入的到容器中去。那么springboot是如何知道要去调用哪些方法,加载哪些配置,并注入哪些bean呢?

基本步骤如下:

1.首先,springboot在启动的时候会去找starter包下resources/META-INF/spring.factories文件,然后根据文件中配置的自动配置类运行,加载autoconfigure类。

2.根据@Conditional注解的条件,决定Bean是否要注入的容器中去。

3.然后我们一般用的就是这个Bean,来使用其中的一些功能。

三、编写自己的Starter

3.1 创建maven项目

首先我们创建一个maven项目,按照如下名字起名,其实starter说白了也只是一个jar:

一般的,官方的starter命名方式是spring-boot-start-xxx的形式,为了区分,我们自定义的starter用xxx-spring-boot-starter的命名方式。

1.添加springboot的依赖,因为要成为一个spring-boot-starter是需要依赖springboot的这套流程的,所以我们引入以下依赖:

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. 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.  
  6. <groupId>com.example.demo</groupId>
  7. <artifactId>person-spring-boot-starter</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <packaging>jar</packaging>
  10.  
  11. <dependencies>
  12. <dependency>
  13. <groupId>org.springframework.boot</groupId>
  14. <artifactId>spring-boot-autoconfigure</artifactId>
  15. <version>2.0.0.RELEASE</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-configuration-processor</artifactId>
  20. <version>2.0.0.RELEASE</version>
  21. <optional>true</optional>
  22. </dependency>
  23. </dependencies>
  24.  
  25. </project>

2.添加配置类PersonProperties,主要来从配置文件中读取配置绑定到这个类上:

  1. package com.example.demo;
  2.  
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4.  
  5. @ConfigurationProperties(prefix = "spring.person")
  6. public class PersonProperties {
  7. private String name;
  8.  
  9. private Integer age;
  10.  
  11. public String getName() {
  12. return name;
  13. }
  14.  
  15. public void setName(String name) {
  16. this.name = name;
  17. }
  18.  
  19. public Integer getAge() {
  20. return age;
  21. }
  22.  
  23. public void setAge(Integer age) {
  24. this.age = age;
  25. }
  26. }

3.添加我们的核心服务类,这里边是我们主要提供的功能,假设我们提供了一个sayHello方法,返回一段话:

  1. package com.example.demo;
  2.  
  3. public class PersonService {
  4. private PersonProperties personProperties;
  5.  
  6. public PersonService(PersonProperties personProperties) {
  7. this.personProperties = personProperties;
  8. }
  9. public String sayHello() {
  10. return "大家好,我叫: " + personProperties.getName() + ", 今年" + personProperties.getAge() + "岁";
  11. }
  12. }

4.添加自动配置类,这个类主要是给springboot来用的,springboot会用这个类来启动,激活我们的服务,注册都容器中去:

  1. @Configuration
  2. @EnableConfigurationProperties(PersonProperties.class)
  3. @ConditionalOnClass(PersonService.class)
  4. @ConditionalOnProperty(prefix = "spring.person", value = "enabled", matchIfMissing = true)
  5. public class PersonServiceAutoConfiguration {
  6. @Autowired
  7. private PersonProperties personProperties;
  8.  
  9. @Bean
  10. public PersonService personService(){
  11. return new PersonService(personProperties);
  12. }
  13. }

解释一下代码中用到的几个注解:

  • @ConditionalOnClass,当classpath下发现该类的情况下进行自动配置。
  • @ConditionalOnMissingBean,当Spring Context中不存在该Bean时。
  • @ConditionalOnProperty(prefix = "srping.person",value = "enabled",havingValue = "true"),当配置文件中spring.perons.enabled=true时。如果没有,使用matchIfMissing的值,也为true。
  • @Bean,把返回的PersonService对象添加的spring容器中去,这样我们再使用的使用就可以直接装配了。

其他注解:

  • @ConditionalOnBean:当容器中有指定的Bean的条件下
  • @ConditionalOnClass:当类路径下有指定的类的条件下
  • @ConditionalOnExpression:基于SpEL表达式作为判断条件
  • @ConditionalOnJava:基于JVM版本作为判断条件
  • @ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
  • @ConditionalOnMissingBean:当容器中没有指定Bean的情况下
  • @ConditionalOnMissingClass:当类路径下没有指定的类的条件下
  • @ConditionalOnNotWebApplication:当前项目不是Web项目的条件下
  • @ConditionalOnProperty:指定的属性是否有指定的值
  • @ConditionalOnResource:类路径下是否有指定的资源
  • @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者在有多个Bean的情况下,用来指定首选的Bean
  • @ConditionalOnWebApplication:当前项目是Web项目的条件下

5. 在resources下添加spring.factories文件,目录结构如下/resources/META-INF/spring.factories,springboot会自动识别这个文件,加载运行我们的PersonServiceAutoConfiguration类。

6.编译运行,用maven install安装jar,默认会安装的maven的仓库里边,用于后来我们使用。

3.2 创建springboot项目测试

这一步简单就不说了,直接按模板创建一个springboot的web项目就行了,然后我们在pom.xml中添加我们刚才写好的person-spring-boot-starter:

  1. <dependency>
  2. <groupId>com.example.demo</groupId>
  3. <artifactId>person-spring-boot-starter</artifactId>
  4. <version>1.0-SNAPSHOT</version>
  5. </dependency>

配置文件中添加starter的配置项:

  1. spring.person.age=12
  2. spring.person.name=songlin

添加一个控制器测试:

  1. @RestController
  2. public class HelloController {
  3.  
  4. @Autowired
  5. PersonService personService;
  6.  
  7. @GetMapping("/hello")
  8. public String hello(){
  9. String s = personService.sayHello();
  10. return s;
  11. }
  12. }

访问http://localhost:8080/hello,显示如下:

四、总结

当我们想往springboot集成一个功能的时候,就可以用这种方式了,关于starter的写法,大家还可以参看源码,理解了源码我们就能写出更好用的starter了。springboot的基本入门系列就先到这了,往后我们就开始学习springboot集成高级功能了。

SpringBoot起飞系列-自定义starter(十)的更多相关文章

  1. SpringBoot起飞系列-Web开发(四)

    一.前言 从今天你开始我们就开始进行我们的web开发,之前的一篇用SpringBoot起飞系列-使用idea搭建环境(二)已经说明了我们如何进行开发,当然这是搭建起步,接下来我们就开始进行详细的开发, ...

  2. SpringBoot起飞系列-国际化(六)

    一.前言 国际化这个功能可能我们不常用,但是在有需要的地方还是必须要上的,今天我们就来看一下怎么在我们的web开发中配置国际化,让我们的网站可以根据语言来展示不同的形式.本文接续上一篇SpringBo ...

  3. SpringBoot起飞系列-配置文件(三)

    一.SpringBoot中的配置文件 说起到配置文件,大家并不陌生,早在springboot之前,我们用ssh,ssm框架开发的时候整天都要接触配置文件,那时候的配置文件基本上都是.propertie ...

  4. SpringBoot起飞系列-入门(一)

    一.SpringBoot简介 1.1 什么是SpringBoot 说到spring系列,可能大家都很熟悉,spring.springmvc,美之名曰:spring全家桶,那么springboot其实也 ...

  5. SpringBoot起飞系列-数据访问(九)

    一.前言 前边我们已经学些了开发的基本流程,最重要的一步来了,怎么样和数据库交互才是最重要的,毕竟没有数据那就相当于什么也没做,本文我们来学习使用springboot整合jdbc.mybatis.jp ...

  6. SpringBoot起飞系列-配置嵌入式Servlet容器(八)

    一.前言 springboot中默认使用的是tomcat容器,也叫做嵌入式的servlet容器.因为它和我们平常使用的tomcat容器不一样,这个tomcat直接嵌入到的springboot,平常我们 ...

  7. SpringBoot起飞系列-拦截器和统一错误处理(七)

    一.前言 在前边部分我们已经学会了基本的web开发流程,在web开发中,我们通常会对请求做统一处理,比如未登录的用户要拦截掉相关请求,报错页面统一显示等等,这些都需要配置,可以大大简化我们的代码,实现 ...

  8. SpringBoot学习(2) - 自定义starter

    自己开发一个spring boot starter的步骤1.新建一个项目(全部都基于maven),比如新建一个spring-boot-starter-redis的maven项目 pom.xml: &l ...

  9. SpringBoot起飞系列-日志使用(四)

    一.SpringBoot中的日志组件 日志是一个系统中不可缺少的组件.在项目中,我们常用的日志组件有JUL.JCL.Jboss-logging.logback.log4j.log4j2.slf4j.. ...

随机推荐

  1. ros中同时订阅两个topic(2张图像)合并成一个topic(1张图像)

    2019-12-06 15:42:39 先暂时做个资料保存 要同时用两个红外相机,但是没有做硬件上的 时间戳同步,就是笔记本上同时插着两个相机. 两个topic发布各自相机的图像,然后要有个节点同时订 ...

  2. SSO 断点登陆

    1. 摘要 ( 注意:请仔细看下摘要,留心此文是否是您的菜,若浪费宝贵时间,深感歉意!!!) SSO这一概念由来已久,网络上对应不同场景的成熟SSO解决方案比比皆是,从简单到复杂,各式各样应有尽有!开 ...

  3. .NetCore 读取配置文件

    1.创建config.json配置,并设置成始终复制 2.需要安装 nuget 包 Microsoft.Extensions.Configuration .Microsoft.Extensions.C ...

  4. 7.13 T2 Shit 题(shit)

    [题目描述] 某一天,小

  5. Codeforces 940 E.Cashback (单调队列,dp)

    Codeforces 940 E.Cashback 题意:一组数,要分为若干个区间,每个区间长度为ki(1<=ki<=n),并且对于每个区间删去前ki/c(向下取整)个小的数(即对区间升序 ...

  6. Java基础线程系列大纲

    ## Java 多线程之 线程创建 ## Java 多线程之 Sleep ## Java 多线程之 Join ## Java 多线程之 生命周期 ## Java 多线程之 wait, notify a ...

  7. POJ 3177 (Redundant Paths) —— (有重边,边双联通,无向图缩点)

    做到这里以后,总算是觉得tarjan算法已经有点入门了. 这题的题意是,给出若干个点和若干条边连接他们,在这个无向图中,问至少增加多少条边可以使得这个图变成边双联通图(即任意两点间都有至少两条没有重复 ...

  8. CSS子元素在父元素中水平垂直居中的几种方法

    1. 水平居中(margin: auto;)子父元素宽度固定,子元素上设置 margin: auto; 子元素不能设置浮动,否则居中失效. #div1{ width: 300px; height: 3 ...

  9. Redis 延迟指标监控

    Redis 延迟监控框架 Redis 2.8.13 引入了Latency Monitoring的一个新功能,可以帮助我们检查和排查引起延迟的原因. Latecny Monitoring 由如下组成: ...

  10. break语句与continue语句

    break:终止该层循环: continue:跳过该层循环 注: ①:若这两个语句离开应用范围,存在是没有意义的. ②:这个两个语句后面都不能有语句,因为执行不到. ③:continue语句是跳过本次 ...