赞
踩
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 在我们的开发中并不会被感知,因为它是存在与我们的 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封装到一个配置类中!
首先需要深思一下什么是打包,现在项目都是分工合作,写好代码运维人员直接从git拉取项目部署,都快忘记了什么是打包。
打包的目的有两种:启动和引入,不同的目的打出来的包截然不同,并且是由maven自己判断的。
maven是怎么判断的:检测项目中是否存在启动类。
没有启动类的项目打出来的包:
打包时pom文件中的jar不会下载
存在启动类时打出来的包
打包时pom文件中的jar会下载
基于上面的示例可以发现spring项目有无启动类时打出来的包会有很大的差别。其实也好理解,存在启动类的项目,打包自然是为了运行;而没有启动类的包,打包自然是为了被引用。
最终形成如下目录:
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();
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.lkw.java.demo3.service.SpringTest
<?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。
Spring Boot CLI 是一个命令行使用 Spring Boot 的客户端工具;主要功能如下:
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
启动服务并验证
查看各个监控信息
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。