进阶篇-SpringBoot2.x自定义starter启动器
1、本篇前言
Spring Boot为我们提供了简化企业级开发绝大多数场景的
starter pom【比如springb-boot-starter-web,springb-boot-starter-jdbc等】, 使用应用场景所需要的starter pom,只需要引入对应的starter,即可以得到Spring Boot为我们提供的自动配置的Bean。
然而,可能在很多情况下,我们需要自定义stater,这样可以方便公司内部系统调用共同的配置模块的时候可以自动进行装载配置。比如公司的很多内部系统都有认证授权模块、以及基于AOP实现的日志切面等,这些技术在不同的项目中逻辑基本相同,而这些功能可以通过starter自动配置的形式进行配置,即可达到可复用的效果。
(1)Starter的概念
SpringBoot之所以大大地简化了我们的开发,用到的一个很重要的技术就是Starter机制!
Starter机制抛弃了以前xml中繁杂的配置,将各种配置统一集成进了Starter中,开发人员只需要在maven中引入Starter依赖,SpringBoot就能自动扫描出要加载的配置信息并按相应的默认配置来启动项目。
所以Starter可以理解为一个可拔插式的插件,提供了一系列便利的依赖描述符,使得我们可以获得所需的所有Spring和相关技术的一站式服务。应用程序只需要在maven中引入Starter依赖,SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置,我们可以把Starter看做是Springboot的场景启动器。
(2)Starter的优点
- Starter可以让我们摆脱开发过程中对各种依赖库的冲突处理。
- 可以简化原有xml中各种负载的配置信息。
- SpringBoot提供了针对一般研发中各种场景的spring-boot-starter依赖,所有这些依赖模块都遵循着约定成俗的默认配置(”约定大于配置“),并允许我们调整这些配置。Starter的出现极大的帮助开发者们从繁琐的框架配置中解放出来,从而更专注于业务代码。
(3)自定义Starter的场景
在我们的日常开发工作中,经常会有一些独立于业务之外的配置模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,只需要将其在pom中引用依赖即可,利用SpringBoot为我们完成自动装配即可。
常见的自定义Starter场景比如:
- 动态数据源
- 登录模块
- 基于AOP技术实现日志切面
- …
(4)自定义Starter命名规范
SpringBoot官方建议其官方推出的starter以spring-boot-starter-xxx的格式来命名,而第三方开发者自定义的starter则以xxxx-spring-boot-starter的规则来命名,比如 mybatis-spring-boot-starter。
(5)自定义starter中几个重要注解
- @Configuration: 表明此类是一个配置类,将变为一个bean被Spring进行管理。
- @EnableConfigurationProperties: 启用属性配置,将读取指定类里面的属性。
- @ConditionalOnClass: 当类路径下面有指定的类时,进行自动配置。
- @ConditionalOnProperty: 判断指定的属性是否具备指定的值。
- @ConditionalOnMissingBean:当容器中没有指定bean是,创建此bean。
- @Import: 引入其他的配置类
2、自定义Starter记录日志案例
(1)项目结构
两个工程如下:log-spring-boot-starter[starter模块],spring-boot-demo-starter-test[starter使用演示工程]。
(2)log-spring-boot-starter项目pom文件添加依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><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><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
(3)编写请求日志注解
/*** 功能描述: 请求日志注解* @author TuYong* @date 2022/9/7 20:48*/ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequestLog {//接口方法上的描述信息String desc() default ""; }
(4)编写配置类
@Setter @Getter @ConfigurationProperties(prefix = "request.log") public class LogProperties {private Boolean enabled = Boolean.FALSE; }
(5)编写日志拦截器
/*** 功能描述: 日志拦截器* @author TuYong* @date 2022/9/7 20:49*/ @Slf4j public class LogInterceptor implements HandlerInterceptor {private static final ThreadLocal<Long> THREAD_LOCAL = new ThreadLocal<>();@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HandlerMethod handlerMethod = (HandlerMethod) handler;RequestLog methodAnnotation = handlerMethod.getMethodAnnotation(RequestLog.class);if(methodAnnotation != null){long start = System.currentTimeMillis();THREAD_LOCAL.set(start);}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerMethod handlerMethod = (HandlerMethod) handler;RequestLog methodAnnotation = handlerMethod.getMethodAnnotation(RequestLog.class);if(methodAnnotation != null){Method method = handlerMethod.getMethod();String requestUri = request.getRequestURI();String methodName = method.getDeclaringClass().getName()+":"+method.getName();String desc = methodAnnotation.desc();long end = System.currentTimeMillis();long start = THREAD_LOCAL.get();long l = end - start;THREAD_LOCAL.remove();log.info("请求路径:{},请求方法:{},描述信息:{},总计耗时:{}",requestUri,methodName,desc,l);}} }
(6)编写自动配置类
/*** 功能描述: 自动配置* @author TuYong* @date 2022/9/7 20:55*/ @Configuration @EnableConfigurationProperties({LogProperties.class}) @ConditionalOnProperty(prefix = "request.log",name = {"enabled"},havingValue = "true" ) public class LogAutoConfiguration implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LogInterceptor()).addPathPatterns("/api/**");WebMvcConfigurer.super.addInterceptors(registry);} }
(7)编写spring.factories
在resources/META-INF/下建立spring.factories文件,配置自动装配类路径
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\cn.javaxxw.springboot.LogAutoConfiguration
(8)创建starter演示工程引入starter依赖
<dependencies><dependency><groupId>cn.javaxxw</groupId><artifactId>log-spring-boot-starter</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>
(9)编写测试接口并使用starter中定义的注解
@RestController @RequestMapping("api") public class ApiController {@GetMapping("test")@RequestLog(desc = "测试接口")public Object test(){return "test api!";} }
(10)配置文件
server:port: 8080 # 开启starter组件 request:log:enabled: true
(11)功能测试
通过请求测试接口,我们可以看到starter中的拦截器已经生效
(12)演示工程代码
gitee: https://gitee.com/trazen/springboot-demo.git
3、原理解析
第二章节中的开发流程,有两个地方需要我们了解一下:
(1)starter的自动识别加载:spring.factories里的EnableAutoConfiguration原理。
(2)实现自动加载的智能化、可配置化:@Configuration配置类里注解。
(1)配置类自动加载机制
在SpringBoot的启动类都会加上 @SpringBootApplication 注解。这个注解会引入 @EnableAutoConfiguration 注解。然后 @EnableAutoConfiguration 会 @Import(AutoConfigurationImportSelector.class) 。
AutoConfigurationImportSelector.class 的 selectImports 方法最终会通过 SpringFactoriesLoader.loadFactoryNames ,加载 META-INF/spring.factories 里的 EnableAutoConfiguration 配置值,也就是我们上文中设置的资源文件。
(2)自动加载的智能化可配置化
实际项目中,我们并不总是希望使用默认配置。比如有时候我想自己配置相关功能,或者只有某些bean没有被加载时才会加载starter配置类。这些常见的场景Starter都可以实现,并提供了如下的解决方案:
@Conditional*注解
springboot starter提供了一系列的@Conditional*注解,代表什么时候启用对应的配置,具体的可以去查看springboot的官方文档。常用注解如下:
- @ConditionalOnBean:当容器里有指定的Bean的条件下。
- @ConditionalOnClass: 当类路径下面有指定的类时,进行自动配置。
- @ConditionalOnProperty: 判断指定的属性是否具备指定的值。
- @ConditionalOnMissingBean:当容器中没有指定bean是,创建此bean。
比如我们案例中的 @ConditionalOnProperty(prefix = "request.log", name = {"enabled"},havingValue = "true") ,只有在应用配置 request.log.enabled = true 时该请求日志拦截功能才会生效。
@ConfigurationProperties注解
这个注解主要是为了解决如下场景:我想要使用starter的默认配置类,但是又想对配置中的某些参数进行自定义配置。@ConfigurationProperties类就是做这个工作的。例如上述例子中,我们在配置中使用enabled参数来决定是否启动该组件的日志拦截功能。
进阶篇-SpringBoot2.x自定义starter启动器相关推荐
- @configurationproperties注解的使用_徒手使用SpringBoot自定义Starter启动器
前言 在使用SpringBoot框架时,SpringBoot 最强大的功能就是把我们常用的场景抽取封装成了一个个starter,将其称之为场景启动器.搭建项目时,通过引入SpringBoot为我提供的 ...
- Android自定义View之画圆环(进阶篇:圆形进度条)
前言: 如果你想读懂或者更好的理解本篇文章关于自定义圆环或圆弧的内容.请你务必提前阅读下Android自定义View之画圆环(手把手教你如何一步步画圆环).在这篇文章中,详细描述了最基本的自定义圆环的 ...
- SpringBoot学习之旅(七)---JPA进阶篇之自定义查询、修改、分页
文章目录 前言 源码下载 其他文章 查询关键字 自定义Select和Update 分页及自定义分页 自定义分页 分页查询的业务代码 前言 前一节SpringBoot学习之旅(六)-JPA操作MySql ...
- 002 第一季SpringBoot2核心技术-核心功能:配置文件、Web开发(原生组件)、数据访问、单元测试、指标监控、原理解析:@Value、命令行参数、手动获取bean、自定义starter
三.核心技术之- ->核心功能 1. 配置文件 1.1 文件类型 1.1.1 properties 同以前的properties用法 优先级高于yml的方式. 1.1.2 yaml 1) 简介 ...
- 为SSIS编写自定义数据流组件(DataFlow Component)之进阶篇:自定义编辑器
我们之前几篇讨论过自定义数据流组件的一些技术,分别如下 入门篇 http://www.cnblogs.com/chenxizhang/archive/2009/06/20/1507467.html 数 ...
- Java面试题-进阶篇(2022.4最新汇总)
Java面试题-进阶篇 1. 基础篇 1.1 基本数据类型和包装类 1.2 Double转Bigdecimal可能会出现哪些问题?怎么解决? 1.3 equals 与 == 的区别? 1.4 Java ...
- springboot框架学习 - 自定义 starter
本篇主要演示 springboot 中自定义 starter 的实例,不涉及底层原理. 首先,创建一个什么都没有的工程作为父工程,这个工程不需要进行任何操作,然后创建两个模块,分别命名为 tyu-he ...
- Enterprise Library Step By Step系列(十二):异常处理应用程序块——进阶篇
一.把异常信息Logging到数据库 在日志和监测应用程序块中,有朋友提意见说希望能够把异常信息Logging到数据库中,在这里介绍一下具体的实现方法. 1.创建相关的数据库环境: 我们可以用日志和监 ...
- Kafka核心设计与实践原理总结:进阶篇
作者:未完成交响曲,资深Java工程师!目前在某一线互联网公司任职,架构师社区合伙人! kafka作为当前热门的分布式消息队列,具有高性能.持久化.多副本备份.横向扩展能力.我学习了<深入理解K ...
最新文章
- 解读 2018 之运维篇:我们离高效智能的运维还有多远
- js date 加一天_你不一定了解的js数据类型
- 一次心血来潮的C程序编译 makefile
- linux 运行msi文件是什么意思,查看Msi文件内容
- 已知网友建立html,职称计算机模拟试题:Dreamweaver网页设计模拟试题及答案(5)...
- 针对Algorand所使用的密码相关技术细节进行介绍
- python web开发 CSS基础
- TIOBE 1 月编程语言排行榜:C 语言再度「C 位」出道!
- LV自动挂载,快照,删除等操作
- 常见字符编码详解ANSI,UTF-8,UCS,GBK,GB2312,BIG5
- Android Studio一键汉化,分享一个自己写的小工具
- 【学习笔记】seckill-秒杀项目--(8)页面优化
- 自制win10 PE usb启动盘教程
- qma7981 源码 驱动_高品质PCB板配单报价,QMA7981-TR
- 计算机研究生怎么研究黑洞,麻省理工学院研究生的工作使黑洞的事件视野成为可能...
- javascript百炼成仙 第一章 掌握JavaScript基础1.3变量的声明
- Pycharm补丁包使用教程
- vue+flask微博大数据舆情监控+情感分析可视化系统+爬虫
- Vivado、modelsim、VHDL错误集锦
- 專注於目標,自然朝成功邁進
热门文章
- 我见众生皆无意,唯有见你动了情(表白日记分享篇)
- [PHP代码审计]LightCMS1.3.7存在命令执行漏洞
- Vue动态组件、组件缓存、组件激活和非激活、组件插槽、组件name
- 【渝粤教育】电大中专跨境电子商务理论与实务 (12)作业 题库
- 线上tomcat服务器假死排查
- 初识Cpp之 六、内存分配
- TLS1.3抓包分析(3)——EncryptedExtentions等
- 计算机网络基本知识(四):网络传输介质
- echarts省级地图展示(包含3D地图)
- 抖音内容运营全解剖,看这一篇就够了 !