四大核心
- Starter
- 简介
- 总结
- Autoconfigure
- 简介
- 示例
- 原理
- 自定义starter
- 打包
- 实践
- 总结
- CLI
- Actuator
Starter
简介
springboot项目中几乎项目依赖中基本上全是各种各样的starter, 那么到底什么是starter?
starter是一组方便的依赖描述符,当我们使用它时,可以获得所有需要的Spring和相关技术的一站式服务,典型的如spring-boot-starter-web,引入之后,自动引入所有有关spring web项目相关的依赖。
-
官方提供的 starter 命名:spring-boot-starter-xxx
<!--监控依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- 如果开启了Actuator默认不开放的endpoints,建议一定要加上Spring Security用于endpoint保护,避免重要信息泄露,必须防止未经授权的外部访问。 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
-
非官方的 starter 命名:xxx-spring-boot-starter
<!-- druid连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.2</version> </dependency>
-
其中的 xxx 就是我们想要依赖的组件或者 jar 包。引入之后通过简单的约定配置就可以正常使用。
-
Starter 帮我们封装好了所有需要的依赖,避免我们自己添加导致的一些Jar包冲突或者缺少包的情况。
总结
starter的存在让我们大大简化了项目的开发准备工作,开箱即用。有些starter 包的内容就是 pom 文件,就是一个依赖传递包;而有些Starter还帮我们自动注入了需要的 Bean 实例到 Spring 容器中,不需要我们手动配置。
Autoconfigure
简介
autoconfigure 在我们的开发中并不会被感知,因为它是存在与我们的 starter 中的。所以我们的每个 starter 都是依赖 autoconfigure 的
autoconfigure 内容是配置 Bean 实例到 Spring 容器的实际代码实现包,然后提供给 starter 依赖。所以说配置 Bean 实例到Spring容器中实际是 autoconfigure 做的,因为是 starter 依赖它,所以也可以说是 starter 干的。
所以:autocinfigure 是 starter 体现出来的能力的代码实现
示例
我相信,只要你用过Spring Boot,就会对这样一个现象非常的好奇:
引入一个组件依赖,加个配置,这个组件就生效了。
举个例子来说,比如我们常用的Redis, 在Spring Boot中的使用方式是这样的:
-
引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
编写配置
spring: redis: database: 0 timeout: 5000ms host: 127.0.0.1 port: 6379 password: 123456
-
直接使用
@Autowired private RedisTemplate redisTemplate;
这期间,我们做了什么嘛?我们什么也没有做,那么,这个RedisTemplate对象是怎么注入到Spring容器中的呢?
原理
记得刚开始学自动装配的时候,有两个注解用的很爽,分别是@Autowired和@Resource。当时还记得@Autowired默认装配是byType,可以通过@Qualifile为byName,@Resource默认装配是byName,找不到自动byType。然后还记得,加了@Component注解或其衍生注解之后就能装配了。那么,一个Bean是如何被加载到容器中的?
首先是看一个项目的启动类
分析这些注解,应该秘密就藏在@SpringBootApplication注解了
我们点进去源码可以发现,@SpringBootApplication是一个组合注解,其中上面那三个是属于Java提供的元注解,@Inherited是指可继承的(如果@SpringBootApplication注解作用于类A上,然后B继承了A,那么B也具有该注解的功能)。重要的注解是下面这三个@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。
@SpringBootConfiguration
看似很高大上的@SpringBootConfiguration注解,点进去一看,其实他就是继承了@Configuration,说白了,他就是一个配置注解,作用的话,就是表明某个类是一个配置类。
@ComponentScan
这个注解默认会扫描该类所在的包下所有被@Component注解或其衍生注解所标注的类。
@EnableAutoConfiguration
他是自动装配的总开关,意思是开启自动装配。
点进去可以看到有一个没见过的注解@AutoConfigurationPackage,这是什么作用呢?从名字中大致能看出,自动配置包,差不多吧,他的意思就是添加该注解的类所在的包作为 自动配置包进行管理,不太明白?点进去!
点进去,我们发现,里面有一个@Import({Registrar.class})
继续点进去,终于看到代码了,大致可以看出来,这是用来注册bean的,这里我们着重看一下registerBeanDefinitions方法,方法里有一个参数是BeanDefinitionRegistry registry,听名字就有那味了,重点来了,这里通过一个构造方法进行设置了packageNamenew PackageImports(metadata).getPackageNames(),接下来我要做什么,想必大家都知道了,点进去这个构造方法。
到这里终于把包名给set上了,接下来我们可以看看register方法了,温馨提示:刚刚是从register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));的构造方法点进来的(贵人多忘事嘛)。
这里会走else,这个方法呢,就完成了该包下的bean注入到容器中。
看完了@AutoConfigurationPackage注解,接下来看看这个@Import(AutoConfigurationImportSelector.class),这个是导入自动装配的ImportSelector类。 AutoConfigurationImportSelector
可以看到他实现了DeferredImportSelector接口
继续点,可以看到,他继承了ImportSelector接口。
在ImportSelector中有一个方法,是selectImports方法
可以清楚的看到,AutoConfigurationImportSelector实现了该方法
在这里首先是判断了自动装配的开关
然后获取需要装配的bean
其中这里的getCandidateConfigurations方法是读取META-INF/spring.factories
在AutoConfigurationImportSelector下,还有一个重要的静态内部类,该静态内部类的构造器中初始化读取META-INF/spring-autoconfigure-metadata.properties。
重点是两个配置类
点进去spring-autoconfigure-metadata.properties,里面是自动装配的一些元数据。
点进去spring.factories
点进去一个可以发现,里面都是写好的bean,就等被加载呢!
只要我们按照约定来写spring.factories,那么我们也可以自己定义starter,把我们的bean封装到一个配置类中!
自定义starter
打包
首先需要深思一下什么是打包,现在项目都是分工合作,写好代码运维人员直接从git拉取项目部署,都快忘记了什么是打包。
打包的目的有两种:启动和引入,不同的目的打出来的包截然不同,并且是由maven自己判断的。
maven是怎么判断的:检测项目中是否存在启动类。
-
没有启动类的项目打出来的包:
打包时pom文件中的jar不会下载 -
存在启动类时打出来的包
打包时pom文件中的jar会下载
基于上面的示例可以发现spring项目有无启动类时打出来的包会有很大的差别。其实也好理解,存在启动类的项目,打包自然是为了运行;而没有启动类的包,打包自然是为了被引用。
实践
- 创建一个普通的springboot项目,可以命名为demo-spring-boot-starter
- 创建好之后,把启动类和test文件夹删掉,因为存在启动类和test文件夹打包出来的项目无法被依赖
- 创建需要自动装配的bean
- 将bean自动装配
- 将这个项目打包到我们本地的maven仓库
- 主项目像引入其他依赖一样引入该依赖就可以了
- 自定义starter中的bean就可以直接通过自动装配创建对象。
最终形成如下目录:
- SpringTest
package com.lkw.java.demo3.service; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Description TODO() * @Authon lkw * @Date 2024/1/30 17:22 * @Version 1.0 **/ @Configuration public class SpringTest { public String test(){ System.out.println("demo3"); return "demo3"; } @Bean public SpringTest springTest(){ return new SpringTest(); } }
- spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.lkw.java.demo3.service.SpringTest
- pom.xml
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.lkw.java</groupId> <artifactId>demo3</artifactId> <version>0.0.1</version> <name>demo3</name> <description>starter project for Spring Boot</description> <properties> <java.version>8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--表示两个项目之间依赖不传递;不设置optional或者optional是false,表示传递依赖--> <!--例如:project1依赖a.jar(optional=true),project2依赖project1,则project2不依赖a.jar--> <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-autoconfigure</artifactId> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </pluginManagement> </build> </project>
总结
-
自动装配的实现原理核心是SPI机制。SPI ,全称为 Service Provider Interface(服务提供者接口),是一种服务发现机制。它通过在classpath路径下的META-INF/services文件夹查找文件,自动加载文件中所定义的类。
-
并不是每个spring-boot-starter-x中都有注入文件。所有spring-boot-starter-x的组件配置都是放在spring-boot-autoconfigura的组件中的。
-
自动装配简述:项目启动时,Spring通过@Import注解导入了AutoConfigurationImportSelector, 然后调用该类selectImports时,从classpath下的META-INF/spring.factories文件中读取key为EnableAutoConfiguration的配置类,然后Spring便会将这些类加载到Spring的容器中,变成一个个的Bean。
CLI
Spring Boot CLI 是一个命令行使用 Spring Boot 的客户端工具;主要功能如下:
- 运行 groovy 脚本
- 打包 groovy 文件到 jar
- 初始化 Spring Boot 项目
- 其他详见官网:https://docs.spring.io/spring-boot/docs/current/reference/html/cli.html
Actuator
actuator 是 Spring Boot 的监控插件,本身提供了很多接口可以获取当前项目的各项运行状态指标。
-
添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
配置需要开启监控的端点
management: endpoint: health: ## 开启健康监控端点 enabled: true beans: ## 开启Bean实例监控端点 enabled: true
重要端点:
-
health端点:端点会聚合你程序的健康指标,来检查程序的健康情况,端点有很多自动配置的健康指示器:如 redis、rabbitmq、db 等组件。
-
metrics端点:用来返回当前应用的各类重要度量指标,比如:内存信息、线程信息、垃圾回收信息、tomcat、数据库连接池等。
-
loggers端点:暴露了我们程序内部配置的所有 logger 的信息,能够动态修改你的日志等级;只需要发起一个 URL 为http://localhost:8080/actuator/loggers/root的POST请求,POST 报文如下:
{ "configuredLevel": "DEBUG" }
-
info端点:可以用来展示你程序的信息。我理解过来就是一些程序的基础信息。并且你可以按照自己的需求在配置文件application.properties中个性化配置(默认情况下,该端点只会返回一个空的 json 内容。)
-
beans端点:会返回 Spring 容器中所有 bean 的别名、类型、是否单例、依赖等信息
-
heapdump端点:会自动生成一个 Jvm 的堆文件 heapdump。我们可以使用 JDK 自带的 Jvm 监控工具 VisualVM 打开此文件查看内存快照。
-
threaddump端点:主要展示了线程名、线程 ID、线程的状态、是否等待锁资源、线程堆栈等信息。方便我们在日常定位问题的时候查看线程的情况。
-
shutdown端点:属于操作控制类端点,可以优雅关闭 Spring Boot 应用。要使用这个功能首先需要在配置文件中开启:management.endpoint.shutdown.enabled=true
-
-
启动服务并验证
-
查看各个监控信息