SpringBoot Starter机制 - 自定义Starter
前言
最近在学习Spring Boot相关的课程,过程中以笔记的形式记录下来,方便以后回忆,同时也在这里和大家探讨探讨,文章中有漏的或者有补充的、错误的都希望大家能够及时提出来,本人在此先谢谢了!
开始之前呢,希望大家带着几个问题去学习:
1、SpringBoot Starter 是什么?
2、这个功能有什么用?
3、怎么实现的?
4、这个功能能应用在工作中?
这是对自我的提问,我认为带着问题去学习,是一种更好的学习方式,有利于加深理解。好了,接下来进入主题。
1、起源
在 Spring
时代,搭建一个 Web 应用通常需要在 pom 文件中引入多个 Web 模块相关的 Maven
依赖,如 SpringMvc
、Tomcat
等依赖,而 SpringBoot
则只需引入 spring-boot-starter-web
依赖即可。这就是 SpringBoot
的 Starter 特性,用来简化项目初始搭建以及开发过程,它是一个功能模块的所有 Maven 依赖集合体。接下来,我们进行详细讨论。
注:本篇文章所用到的 Spring Boot版本是 2.0.3.RELEASE
2、SpringBoot Starter 原理
SpringBoot
提供了非常多的 Starter,下面列出常用的几个:
名称 | 功能 |
---|---|
spring-boot-starter-web | 支持 Web 开发,包括 Tomcat 和 spring-webmvc |
spring-boot-starter-redis | 支持 Redis 键值存储数据库,包括 spring-redis |
spring-boot-starter-test | 支持常规的测试依赖,包括 JUnit、Hamcrest、Mockito 以及 spring-test 模块 |
spring-boot-starter-aop | 支持面向切面的编程即 AOP,包括 spring-aop 和 AspectJ |
spring-boot-starter-data-elasticsearch | 支持 ElasticSearch 搜索和分析引擎,包括 spring-data-elasticsearch |
spring-boot-starter-jdbc | 支持JDBC数据库 |
spring-boot-starter-data-jpa | 支持 JPA ,包括 spring-data-jpa、spring-orm、Hibernate |
可以看到这些 Starter 的名称都是以 spring-boot-starter 为开头,后面跟着具体的模块名,所有官方的 Starter 遵循相似的命名模式。这些 Starter 其实不包含 Java 代码,核心是它的 pom 文件。我们以 spring-boot-starter-web
为例,来看看该 Starter 的 pom 文件包含的内容。
先在项目中引入以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
然后找到引入的 spring-boot-starter-web 依赖的文件夹位置:
打开该 pom 文件进行查看:
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.3.RELEASE</version>
<name>Spring Boot Web Starter</name>
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.3.RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- 对 json 解析的支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.0.3.RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- 提供 Tomcat 容器。通过这可以看到 ServletWeb 默认的容器是 Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.0.3.RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- hibernate 的校验框架 -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.10.Final</version>
<scope>compile</scope>
</dependency>
<!-- 提供了核心 HTTP 集成,用于集成其它 web 框架的基础结构 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- 提供了对 Spring MVC 的支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.7.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
可以看到,在该 pom 文件中已经定义好了 Web 模块需要的各个组件。之后,引入的 Starter 依赖可以与 SpringBoot
的自动装配特性、外部化配置特性进行无缝衔接,来达到快速开发的目的。关于 SpringBoot
自动装配和外部化配置大家可以分别参考《SpringBoot 自动装配(二)》和《SpringBoot 外部化配置(二)》这两篇文章。接下来,通过实现自定义的 Starter 来理解整体逻辑。
3、自定义 Starter
先创建一个项目,在该项目中定义 Starter 的内容,然后通过 Maven 将其打成 jar 包,之后在另一个项目中使用该 Starter 。
3.1 创建 Starter
1、创建一个 Maven 项目,在其 pom 文件中引入自动装配的依赖,并定义好 Starter 的名称。非官方的 Starter 命名需遵循 xxx-spring-boot-starter 的格式。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.loong</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>1.0.0.RELEASE</version>
<name>demo</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
</dependencies>
</project>
2、新建一个 Properties
配置类,用于保存外部化配置文件中定义的配置数据,其中配置文件包括 properties 或 yml 。
// 定义配置文件中的属性前缀
@ConfigurationProperties(prefix = "demo")
public class DemoProperties {
private String name;
private String date;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
关于外部化配置底层实现,大家可以参考《SpringBoot 外部化配置(二)》这篇文章。
3、新建一个功能类,主要用来返回 DemoProperties
中的 name 和 date 属性。
public class DemoService {
private DemoProperties demoProperties;
public DemoService(DemoProperties demoProperties) {
this.demoProperties = demoProperties;
}
public String getName() {
return demoProperties.getName();
}
public String getDate() {
return demoProperties.getDate();
}
}
4、创建自动配置类,在该配置类中完成 Starter 的功能。这里,通过构造器注入 DemoProperties
配置类对象,并初始化 DemoService
功能类。
@Configuration
@EnableConfigurationProperties(value = DemoProperties.class)
public class DemoAutoConfiguration {
private final DemoProperties demoProperties;
public DemoAutoConfiguration(DemoProperties demoProperties) {
this.demoProperties = demoProperties;
}
@Bean
// 当前项目是否包含 DemoService Class
@ConditionalOnMissingBean(DemoService.class)
public DemoService demoService() {
return new DemoService(demoProperties);
}
}
自动配置类是 SpringBoot
自动装配特性不可或缺的一环,关于 SpringBoot
自动装配底层实现,大家可以参考《SpringBoot自动装配(二)》这篇文章。
5、自定义初始化器和监听器,这是 SpringBoot
提供的扩展点,主要在 SpringBoot
的不同生命周期执行相应操作。
public class DemoApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println(" DemoApplicationContextInitializer 初始化成功 ");
}
}
public class DemoApplicationListener implements ApplicationListener<SpringApplicationEvent> {
@Override
public void onApplicationEvent(SpringApplicationEvent springApplicationEvent) {
if (springApplicationEvent instanceof ApplicationStartingEvent) {
System.out.println(" DemoApplicationListener 监听 ApplicationStartingEvent 事件");
}
}
}
关于初始化器和监听器大家可以参考《Spring Boot SpringApplication启动类(一)》的 2.2 和 2.3 小节 。
6、在 src/main/resources 目录下创建 META-INF 文件夹,并在文件夹中创建 spring.factories
文件,定义如下内容:
# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.loong.demo.context.DemoApplicationContextInitializer
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.loong.demo.configuration.DemoAutoConfiguration
# Application Listeners
org.springframework.context.ApplicationListener=\
com.loong.demo.listener.DemoApplicationListener
这是 SpringBoot
的规约,当我们自定义的初始化器、监听器及自动配置类需要被 SpringBoot
读取时,必须定义成该格式。关于原理,在前几篇文章说过,这里不再叙述。
最后,所有的类已经定义完成,项目结构如下:
打开通过右侧的 Maven 工具栏,点击 install 打包到本地的 Maven 库。
之后,自定义的 Starter 就可以使用,我们来测试一下。
3.2 测试自定义 Starter
1、在另一个项目中引入该 Starter 的 Maven 依赖:
<dependency>
<groupId>com.loong</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
2、在 properties 文件中定义配置数据:
demo.name = loong
demo.date = 2020.01.01
3、在启动类中,获取 DemoService
Bean ,并调用它的 getDate 和 getName 方法获取配置文件中的数据:
@SpringBootApplication
public class DiveInSpringBootApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(DiveInSpringBootApplication.class, args);
DemoService bean = run.getBean(DemoService.class);
System.out.println(bean.getDate() + " === " + bean.getName());
}
}
最后,查看控制台的输出:
/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/bin/java "-javaagent:/Applications/IntelliJ IDEA CE.app..."
DemoApplicationListener 监听 ApplicationStartingEvent 事件
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.3.RELEASE)
DemoApplicationContextInitializer 初始化成功
2020-01-01 13:14:02.023 INFO 55657 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-01-01 13:14:02.189 INFO 55657 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6b19b79: startup date [Wed Jan 01 13:13:59 CST 2020]; root of context hierarchy
2020-01-01 13:14:02.257 INFO 55657 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/hello],methods=[GET]}" onto public java.lang.String com.loong.diveinspringboot.Chapter1.controller.HelloWorldController.helloWorld(java.lang.String)
2020-01-01 13:14:02.260 INFO 55657 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2020-01-01 13:14:02.261 INFO 55657 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2020-01-01 13:14:02.296 INFO 55657 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-01-01 13:14:02.296 INFO 55657 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-01-01 13:14:02.341 WARN 55657 --- [ main] ion$DefaultTemplateResolverConfiguration : Cannot find template location: classpath:/templates/ (please add some templates or check your Thymeleaf configuration)
2020-01-01 13:14:02.718 INFO 55657 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator'
2020-01-01 13:14:02.726 INFO 55657 --- [ main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/health],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2020-01-01 13:14:02.727 INFO 55657 --- [ main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/info],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
2020-01-01 13:14:02.728 INFO 55657 --- [ main] s.b.a.e.w.s.WebMvcEndpointHandlerMapping : Mapped "{[/actuator],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto protected java.util.Map<java.lang.String, java.util.Map<java.lang.String, org.springframework.boot.actuate.endpoint.web.Link>> org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping.links(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2020-01-01 13:14:02.766 INFO 55657 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2020-01-01 13:14:02.822 INFO 55657 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-01-01 13:14:02.826 INFO 55657 --- [ main] c.l.d.C.DiveInSpringBootApplication : Started DiveInSpringBootApplication in 3.607 seconds (JVM running for 3.984)
2020.01.01 === loong
可以看到,结果正确输出,且初始化器和监听器都已被加载。这里只是一个简单的演示,Starter 较为简单,大家可以根据实际情况实现一个更为复杂的。
SpringBoot Starter
的内容就介绍到这,如果文章中有错误或者需要补充的请及时提出,本人感激不尽。
SpringBoot Starter机制 - 自定义Starter的更多相关文章
- SpringBoot系列之自定义starter实践教程
SpringBoot系列之自定义starter实践教程 Springboot是有提供了很多starter的,starter翻译过来可以理解为场景启动器,所谓场景启动器配置了自动配置等等对应业务模块的一 ...
- 深入springboot原理——一步步分析springboot启动机制(starter机制)
前言 使用过springboot的同学应该已经知道,springboot通过默认配置了很多框架的使用方式帮我们大大简化了项目初始搭建以及开发过程.本文的目的就是一步步分析springboot的启动过程 ...
- SpringBoot起飞系列-自定义starter(十)
一.前言 到现在,我们可以看出来,如果我们想用一些功能,基本上都是通过添加spring-boot-starter的方式来使用的,因为各种各样的功能都被封装成了starter,然后把相关服务注入到容器中 ...
- SpringBoot原理—分析SpringBoot启动机制(starter机制)
一:前言使用过springboot的同学应该已经知道,springboot通过默认配置了很多框架的使用方式帮我们大大简化了项目初始搭建以及开发过程.本文的目的就是一步步分析springboot的启动过 ...
- SpringBoot学习(2) - 自定义starter
自己开发一个spring boot starter的步骤1.新建一个项目(全部都基于maven),比如新建一个spring-boot-starter-redis的maven项目 pom.xml: &l ...
- SpringBoot使用AutoConfiguration自定义Starter
https://segmentfault.com/a/1190000011433487
- SpringBoot第十六篇:自定义starter
作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/11058502.html 版权声明:本文为博主原创文章,转载请附上博文链接! 前言 这一段时间 ...
- SpringBoot应用篇(一):自定义starter
一.码前必备知识 1.SpringBoot starter机制 SpringBoot中的starter是一种非常重要的机制,能够抛弃以前繁杂的配置,将其统一集成进starter,应用者只需要在mave ...
- springboot核心技术(四)-----Docker、数据访问、自定义starter
Docker 1.简介 Docker是一个开源的应用容器引擎:是一个轻量级容器技术: Docker支持将软件编译成一个镜像:然后在镜像中各种软件做好配置,将镜像发布出去,其他使用者可以直接使 用这个镜 ...
随机推荐
- python 约束. 异常处理. MD5. 日志处理
一.约束 1.抛异常 # # 项目经理写的 class Base: # # 对子类进行了约束. 必须重写该方法 # # 以后上班了. 拿到公司代码之后. 发现了NotImplementedError ...
- Warning!程序员们小心被技术绑架
通常我们说程序员需要在某个技术方向上积累到一定的厚度,要能够运用技术有效地解决实际问题.可是当程序员在某一项技术上浸淫时间长了之后,却经常会出现另外的问题,那就是:看待问题时受限于自身的技术积累. 我 ...
- 《C语言深度解剖》学习笔记之符号
第2章 符号 1.注释符号 编译器会将注释剔除,用空格代替原来的注释 y=x /* p; 编译器提示出错的原因:实际上,编译器会把“/*”当作一段注释的开始,直到出现“*/”为止. [规则 2-1]注 ...
- python基础---集合类型(Sets)
集合类型(Sets) 集合对象是不同的(不可重复)hashable对象的无序集合.常见用法包括:成员关系测试.移除序列中的重复.以及科学计算,例如交集.并集.差分和对称差分.通俗点来说,集合是一个无序 ...
- [***]HZOJ 柱状图
神仙题. 作者的正解: *logn). 算法三:对于100%的数据: 我们枚举屋顶位置再三分高度的做法,复杂度的瓶颈在于花费的计算.假设屋顶在i处,高度为hi,如果j<i,有hj-j=hi ...
- 学linux内核与学linux操作系统有什么区别!?
linux内核包括:进程管理,存储管理,IO管理,文件系统等功能.linux操作系统则是linux内核再加上像shell或图形界面和其他的实用软件,比内核庞大的多.建议先学shell命令和linux下 ...
- python基础之逻辑题(3)
Python基础之逻辑题(3) 1.编写一个函数实现将IP地址转换成一个整数 2.求结果:---lambda 3.求a的结果 4.求下面nums的输出 5.求下面片段的输出 6.写出程序的结果:--- ...
- selenium webdriver学习(四)------------定位页面元素(转)
selenium webdriver学习(四)------------定位页面元素 博客分类: Selenium-webdriver seleniumwebdriver定位页面元素findElemen ...
- git 生成秘钥连接远程仓库
二.打开GitBash ,用cd命令进入本地项目目,然后把初始化一下,把本地的目录变成git本地仓库, git status 可以查看本地目录的状态信息 git init git status 三.将 ...
- lavarel 响应宏
宏的概念 计算机里的宏是批量处理的意思.比如我们在进行文本编辑的时候,打错字会有回退的功能——control+z:但是这是我们的键盘操作,计算机在进行处理的时候是不能理解的,他必须对最近两次操作进行比 ...