本文摘自于《Spring Cloud微服务 入门 实战与进阶》一书。  作者:尹吉欢

  Spring Boot的方便体现在简化了很多繁琐的配置,对开发人员来说是一个福音,通过引入各种Spring Boot Starter包可以快速的搭建出一个项目的脚手架。

  目前提供的Spring Boot Starter包有:

  • spring-boot-starter-web:快速构建基于Spring MVC的Web项目,使用Tomcat做默认嵌入式容器。
  • spring-boot-starter-data-redis:操作Redis。
  • spring-boot-starter-data-mongodb:操作Mongodb。
  • spring-boot-starter-data-jpa:操作Mysql。
  • spring-boot-starter-activemq:操作Activemq。
  • 等等......

  自动配置非常方便,当我们要操作Mongodb的时候,只需要引入spring-boot-starter-data-mongodb的依赖,然后配置Mongodb的链接信息 spring.data.mongodb.uri=mongodb://localhost/test就可以使用MongoTemplate来操作数据,MongoTemplate的初始化工作全部交给Starter来完成。

  自动配置麻烦的是当出现错误时,排查问题的难度上升了。自动配置的逻辑都在Spring Boot Starter中,要快速的能够定位问题,那么你必须得了解Spring Boot Starter的内部原理。接下来我们自己动手来实现一个Spring Boot Starter。

  开发Starter步骤:

  • 创建Starter项目
  • 定义Starter需要的配置(Properties)类
  • 编写自动配置类
  • 编写spring.factories文件加载自动配置类
  • 编写配置提示文件spring-configuration-metadata.json(不是必须的)

  创建一个项目spring-boot-starter-demo,Pom.xml配置如下:

    <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

  创建一个配置类,用于在属性文件中配置值,相当于spring.data.mongo这种形式

import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data; @Data
@ConfigurationProperties("spring.user")
public class UserPorperties { private String name; }

  @ConfigurationProperties指定了配置的前缀,也就是spring.user.name=XXX

  再定义一个Client,相当于MongoTemplate,里面定一个方法,用于获取配置中的值

public class UserClient {

    private UserPorperties userPorperties;

    public UserClient() {
} public UserClient(UserPorperties p) {
this.userPorperties = p;
} public String getName() {
return userPorperties.getName();
} }

  一个最基本的Starter包定义好了,但目前肯定是不能使用UserClient ,因为我们没有去自动构建UserClient 的实例,接下来开始构建UserClient

@Configuration
@EnableConfigurationProperties(UserPorperties.class)
public class UserAutoConfigure { @Bean
@ConditionalOnProperty(prefix = "spring.user", value = "enabled", havingValue = "true")
public UserClient userClient(UserPorperties userPorperties) {
return new UserClient(userPorperties);
}
}

  Spring Boot会默认扫描跟启动类平级的包,如果我们的Starter跟启动类不在同一个主包下,如何让UserAutoConfigure 生效?

  第一种方式:

  在resources下创建一个META-INF文件夹,然后在META-INF文件夹中创建一个spring.factories文件,文件中指定自动配置的类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.cxytiandi.demo.UserAutoConfigure

  Spring Boot启动时会去读取spring.factories文件,然后根据配置激活对应的配置类,到底为止就简单的实现了一个Starter包。

  现在可以在其他的项目中引入这个Starter包:

    <dependency>
<groupId>com.example</groupId>
<artifactId>spring-boot-starter-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

  引入之后就直接可以使用UserClient,UserClient 在项目启动的时候已经自动初始化好。

@RestController
public class UserController{ @Autowired
private UserClient userClient; @GetMapping("/user/name")
public String getUserName() {
return userClient.getName();
}
}

  很多时候我们不想引入了Starter包就执行初始化的逻辑,想要用户来指定是否要开启Starter包的自动配置功能,比如常用的@EnableAsync这个注解就是用于开启调用方法异步执行的功能。

  同样的我们也可以通过注解的方式来开启是否自动配置,如果用注解的方式,那么spring.factories就不需要编写了,下面来看怎么定义启用自动配置的注解。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({UserAutoConfigure.class})
public @interface EnableUserClient { }

  核心是@Import({UserAutoConfigure.class})这行代码,通过导入的方式实现把UserAutoConfigure实例加入SpringIOC容器中,这样就能开启自动配置了。

  使用方式就是在启动类上加上该注解,代码入下:

@EnableUserClient
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
} }

  在某些场景下,UserAutoConfigure中会配置多个对象,对于这些对象,不想全部配置,也想让用户指定需要开启配置的时候再去构建对象,这个时候我们可以通过@ConditionalOnProperty来指定是否开启配置的功能,代码如下:

@Bean
@ConditionalOnProperty(prefix = "spring.user", value = "enabled", havingValue = "true")
public UserClient userClient(UserPorperties userPorperties){
return new UserClient(userPorperties);
}

  通过上面的配置,只有当启动类加了@EnableUserClient并且配置文件中spring.user.enabled=true的时候才会自动配置UserClient 。

  在自定义Starter包的过程中,还有一点也比较重要,就是需要对配置的内容项进行提示,需要注意的是Eclipse中是不支持提示的,我用的Spring Tools 4 for Eclipse,如下图:

  定义提示内容需要在META-INF中创建一个spring-configuration-metadata.json

{
"properties":[
{
"name":"spring.user.name",
"defaultValue":"cxytinadi"
},
{
"name":"spring.user.enabled",
"type":"java.lang.Boolean",
"defaultValue":false
}
]
}
  • name:配置名

  • type:配置的数据类型

  • defaultValue:默认值

Spring Boot必备技能之Starter自定义的更多相关文章

  1. 让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean

    让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean 问题描述 实现思路 思路一 [不符合要求] 思路二[满足要求] 思路三[未试验] 问题描述 目前我工作环境下,后端主要的框架 ...

  2. Spring Boot 2.X(十):自定义注册 Servlet、Filter、Listener

    前言 在 Spring Boot 中已经移除了 web.xml 文件,如果需要注册添加 Servlet.Filter.Listener 为 Spring Bean,在 Spring Boot 中有两种 ...

  3. spring boot集成mybatis-plus插件进行自定义sql方法开发时报nested exception is org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):

    spring boot集成mybatis-plus插件进行自定义sql方法开发时报nested exception is org.apache.ibatis.binding.BindingExcept ...

  4. spring boot 四大组件之Starter

    1.概述 依赖管理是任何复杂项目的关键方面.手动完成这些操作并不理想; 你花在它上面的时间越多,你在项目的其他重要方面所花费的时间就越少. 构建Spring Boot启动器是为了解决这个问题.Star ...

  5. 如何在 Spring Boot 优雅关闭加入一些自定义机制

    个人创作公约:本人声明创作的所有文章皆为自己原创,如果有参考任何文章的地方,会标注出来,如果有疏漏,欢迎大家批判.如果大家发现网上有抄袭本文章的,欢迎举报,并且积极向这个 github 仓库 提交 i ...

  6. 【spring boot logback】日志使用自定义的logback-spring.xml文件后,application.properties中关于日志的相关配置还会起作用么

    本篇 将针对[日志使用自定义的logback-spring.xml文件后,application.properties中关于日志的相关配置还会起作用么]这一个主题进行探索. 这个测试项目是根据[spr ...

  7. Spring Boot 2 实战:如何自定义 Servlet Filter

    1.前言 有些时候我们需要在 Spring Boot Servlet Web 应用中声明一些自定义的 Servlet Filter 来处理一些逻辑.比如简单的权限系统.请求头过滤.防止 XSS 攻击等 ...

  8. Spring Boot 整合Mybatis非starter时,mapper一直无法注入解决

    本来呢,直接使用mybatis-spring-boot-starter还是挺好的,但是我们系统比较复杂,有多个数据源,其中一个平台自己的数据源,另外一些是动态配置出来的,两者完全没有关系.所以直接使用 ...

  9. 学习Spring Boot:(十一) 自定义装配参数

    前言 SpringMVC 中 Controller 中方法的参数非常灵活,得益于它的强大自动装配,这次将根据上次遗留下的问题,将研究下装配参数. 正文 SpringMVC中使用了两个接口来处理参数: ...

随机推荐

  1. Centos8 - 图形界面和命令行切换

    查看目前默认的启动方式 systemctl get-default 命令行模式:multi-user.target 图形界面模式:graphical.target 设置为图形界面模式 systemct ...

  2. java 基本语法(十五)Lambda (二)函数式接口

    1.函数式接口的使用说明> 如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口.> 我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是 ...

  3. 数据可视化之 图表篇(五) PowerBI图表不够炫酷?来看看这个

    现在这个大数据时代,每时每刻.各行各业都在产生多种多样的海量数据,如何简单高效的来理解.挖掘这些数据,发现背后的见解就非常重要. 本文介绍这个图表就可以帮你快速发现海量数据背后的见解,微软研究院打造的 ...

  4. vue axios接口封装、Promise封装、简单的axios方法封装、vue接口方法封装、vue post、get、patch、put方法封装

    相信大家在做前后端数据交互的时候都会给请求做一些简单的封装就像之前封装ajax方法一样axios的封装也是一样的简单下面这个就是封装的axios的方法,require.js import axios ...

  5. bzoj4716假摔

    bzoj4716假摔 题意: 给出一个矩阵,求这个矩阵中权值和第k小的长在xmin到n之间,宽在ymin到m之间的子矩阵.n,m≤1000,k≤250000. 题解: 首先求出长为xmin,宽为ymi ...

  6. MYSQL 使用基础 - 这么用就对了

    这篇文章主要梳理了 SQL 的基础用法,会涉及到以下方面内容: SQL大小写的规范 数据库的类型以及适用场景 SELECT 的执行过程 WHERE 使用规范 MySQL 中常见函数 子查询分类 如何选 ...

  7. PDFtoWORD_V1.1版本支持PDF文档中的文字和图片一起转化到word文档中了~

    ​    昨天菜鸟小白做了一个小软件——PDFtoWORD,作用就是将pdf文件中的文字提取出来自动转化为可编辑的word类型.但是这个软件目前也只能将文件PDF中的文字提取出来,还无法提取图片.为了 ...

  8. 集训作业 洛谷P1866 编号

    这个题是个数学题啊. 总体思路不是很难,每个兔子有一个编号,只要不停的看下一个兔子有多少可选编号,再乘上之前的所有可能性就可以算出一共的编号方法. #include<iostream> # ...

  9. 什么是viewstate,能否禁用?是否所用控件都可以禁用

    viewstate用于在两次postback之间保持状态的一种机制禁用viewstate将不能在回发之间保存状态 当控件状态无关使用viewstate将造成性能问题时需要禁用viewstate Vie ...

  10. 题解 洛谷 P5443 【[APIO2019]桥梁】

    考虑若只有查询操作,那么就可以构造\(Kruskal\)重构树,然后在线询问了,也可以更简单的把询问离线,把询问和边都按权值从大到小排序,然后双指针依次加入对于当前询问合法的边,用并查集维护每个点的答 ...