Mobile wallpaper 1Mobile wallpaper 2Mobile wallpaper 3Mobile wallpaper 4Mobile wallpaper 5Mobile wallpaper 6
5255 字
26 分钟
0 次阅读
SpringBoot 基础知识总结

主启动类注解#

@SpringBootApplication 是 Spring Boot 应用的核心注解,通常用于标注主启动类。 @SpringBootApplication 主要是由三个注解组合而成:

  • @SpringBootConfiguration:本质上是 @Configuration 注解,它允许在上下文中注册额外的 bean 或导入其他配置类。
  • @EnableAutoConfiguration:读取 META-INF/spring.factories 文件,加载里面预定义的配置类。
  • @ComponentScan:扫描当前启动类所在的包以及该包下的所有子包,寻找带有 @Component@Controller@Service@Respository@Configuration等注解的类,并将它们注册到 Spring 容器中。

Bean 管理#

Bean 注册#

对于可以直接修改源码的项目,可使用以下构造型注解标记类,使类能够被 Spring 发现并注册到 IoC 容器中:

  • @Component:通用注解。
  • @Respository:持久层注解。
  • @Service:服务层注解。
  • @Controller:控制层注解,返回视图名称。
  • @RestController:控制层注解,相当于 @Controller + @ResponseBody,将返回值自动序列化(JSON 或者 XML)写入 HTTP 响应体。

依赖注入#

  • @Autowired
    • 是 Spring 的原生注解。
    • 可以修饰在字段、构造器、setter方法、方法参数上。
    • 在匹配时先看类型,如果匹配上了多个 Bean,就再根据名字进行匹配。
    • 使用 @Autowired + @Qualifier 可以匹配指定名字的 Bean 。
    • 在定义 Bean 的地方添加 @Primary 可以让这个 Bean 优先被匹配。
  • @Resource
    • 是 Java 标准的注解。
    • 可以修饰在字段、setter方法上。
    • 在匹配时先看名字,如果指定了 name 属性,那么找不到就直接报错。如果没有指定 name 属性,那么找不到就再按照类型进行匹配。

Bean 作用域#

在定义 Bean 的位置使用 @Scope("scopeName") 注解可以定义 Bean 的作用域,常见的作用域包括:

  • singleton:整个 IoC 容器共享一个 Bean。
  • prototype:每次获取容器都会创建一个全新的 Bean。
  • request(仅 Web 应用可用):HTTP 请求开始时创建 Bean,HTTP 请求结束后销毁 Bean。
  • session(仅 Web 应用可用):用户首次访问时创建 Bean,session 过期或注销时销毁 Bean。
  • application(仅 Web 应用可用):整个 Web 应用共享一个 Bean。
  • websocket(仅 Web 应用可用):websocket 连接建立时创建 Bean,连接断开时销毁 Bean。

配置#

声明配置类#

  • @Configuration 用于标注 Spring 的配置类。
  • @Configuration 可以扫描且识别内部的 @Bean 方法,但是它与 @Component 的区别在于代理机制。
    • @Configuration 标注的类会由 Spring 通过 CGLIB 生成动态代理子类。这个代理的作用是拦截 @Bean 方法的调用:它会先检查 IoC 容器中是否已存在该对象,如果有则直接返回单例,否则才创建新对象。这确保了在配置类内部进行方法间调用时,依然能获取到容器管理的单例 Bean。
    • 相比之下,@Component 标注的类没有这种代理机制。在其内部直接调用另一个 @Bean 方法等同于普通的 Java 方法调用,会导致重复创建对象。因此,在 @Component 中,必须通过将依赖 Bean 作为方法参数传入的方式来正确注入依赖。
  • @Import 注解必须写在带有 @Configuration 的类上。

读取配置信息#

@Value 注入#

@Value 用于注入比较简单的配置信息,一般情况下不建议使用这种注入方式。

@RestController
public class StudentController {
@Value("${profile.age}")
private int age;
@Value("${profile.name}")
private String name;
}

@ConfigurationProperties 注入#

@ConfigurationProperties 可以实现更加复杂的配置信息注入:

@Component
@ConfigurationProperties(prefix = "library")
@Data
public class LibraryProperties {
// 绑定规则非常宽松
// 如果 Java 字段是 bookName
// 则配置文件可匹配的字段有:book_name/book-name/BOOK_NAME
private String location;
private List<Book> books;
}
@RestController
@RequestMapping("/library")
public class BookController {
private LibraryProperties libraryProperties;
public BookController(LibraryProperties libraryProperties) {
this.libraryProperties = libraryProperties;
}
}
TIP

除了使用 @Component 注解, 还可以通过以下方式进行注册 ConfigurationProperties(注意不能使用多种方式重复注册):

  • @Import(XXX.class):语意不清,不推荐。
  • @EnableConfigurationProperties(XXX.class):含义清晰,能结合 SpringBoot 自动装配机制,推荐使用。
  • @ConfigurationPropertiesScan("com.example.xxx"):批量导入指定包的 ConfigurationProperties。

加载自定义配置文件#

@PropertySource 可以指定需要加载哪个自定义配置文件中的配置信息。需要注意的是,这种方式只能加载 .properties 文件,对于 yml 文件需要进行更复杂的处理。

@Configuration
// 核心:加载 classpath 下的 custom.properties 文件
// encoding = "UTF-8" 非常重要!否则中文会乱码
// value 可以为数组
@PropertySource(value = "classpath:custom.properties", encoding = "UTF-8")
public class MyConfig {
// 加载进来后,就可以用 @Value 读取了
@Value("${my.app.name}")
private String appName;
public void print() {
System.out.println("加载到的应用名:" + appName);
}
}

MVC#

映射 HTTP 请求#

GET 请求#

// @RequestMapping(value = "/user", method = RequestMethod.GET)
@GetMapping("/user")
public Result<List<User>> getAllUsers() {
return Result.success(userService.list());
}

POST 请求#

// @RequestMapping(value = "/user", method = RequestMethod.POST)
@PostMapping("/user")
public Result<Integer> createUser(@Valid @RequestBody UserCreateRequest userCreateRequest) {
return Result.success(userService.save());
}

PUT 请求#

// @RequestMapping(value = "/user/{userId}", method = RequestMethod.PUT)
@PutMapping("/user/{userId}")
public Result<Void> updateUser(@PathVariable Long userId,
@Valid @RequestBody UserUpdateRequest userUpdateRequest) {
return Result.success();
}

DELETE 请求#

// @RequestMapping(value = "/user/{userId}, method = RequestMethod.DELETE")
@DeleteMapping("/user/{userId}")
public Result<Void> deleteUser(@PathVariable Long userId) {
return Result.success();
}

PATCH 请求#

// @RequestMapping(value = "/user/{userId}", method = RequestMethod.PATCH)
@PatchMapping("/user/{userId}")
public Result<Void> updateUser(@PathVariable Long userId,
@Valid @RequestBody UserUpdateRequest userUpdateRequest) {
return Result.success();
}

绑定参数#

从 URL 路径中提取参数#

@PathVariable 用于从 URL 路径中提取参数:

@DeleteMapping("/user/{userId}")
public Result<Void> deleteUser(@PathVariable("userId") Long userId) {
return Result.success();
}
TIP
  • 当模版里的名字与参数名字一致时,可以只写 @PathVariable
  • 也可以像示例那样显式指定模版里的名字。

绑定查询参数#

@RequestParam 用于绑定查询参数:

@GetMapping("/user")
public Result<List<User>> getUserByAge(@RequestParam("age") Integer age) {
return Result.success(userService.listByAge(age));
}
TIP
  • 当参数名和 URL 中的 key 相同时,可以直接省略注解。
  • 标准写法是像示例那样显式指定 key 的名称。
  • 如果参数是可选的,可以指定默认值 @Request(value = "age", defaultValue = 18),或者是直接让参数值为空 @Request(value = "age", isRequired = false)

绑定请求体中的 JSON 数据#

@RequestBody 可以将 Content-Typeapplication/json 的 Request 的请求体解析成 Java 对象:

@PostMapping("/user")
public Result<User> createUser(@Valid @RequestBody UserCreateRequest userCreateRequest) {
return Result.success(userService.save());
}

数据校验#

引入依赖#

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

校验注解#

  • @Null:被注释的元素必须为 null。
  • @NotNull:被注释的元素必须不为 null。
  • @AssertTrue:被注释的元素必须为 true。
  • @AssertFalse:被注释的元素必须为 false。
  • @Min(value):被注释的元素必须是一个数字,其值必须大于等于指定的最小值。
  • @Max(value):被注释的元素必须是一个数字,其值必须小于等于指定的最大值。
  • @DecimalMin(value):被注释的元素必须是一个数字,其值必须大于等于指定的最小值。
  • @DecimalMax(value):被注释的元素必须是一个数字,其值必须小于等于指定的最大值。
  • @Size(max=, min=):被注释的元素的大小必须在指定的范围内。
  • @Digits(integer=, fraction=):被注释的元素必须是一个数字,其整数位数和小数位数必须在可接受的范围内。
  • @Past:被注释的元素必须是一个过去的日期。
  • @Future:被注释的元素必须是一个将来的日期。
  • @Pattern(regex=,flag=):被注释的元素必须符合指定的正则表达式。

校验请求体#

对于请求体的校验只需要在对应方法参数前添加 @Valid 注解即可:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
@NotNull(message = "classId 不能为空")
private String classId;
@Size(max = 33)
@NotNull(message = "name 不能为空")
private String name;
@Pattern(regexp = "((^Man$|^Woman$|^UGM$))", message = "sex 值不在可选范围")
@NotNull(message = "sex 不能为空")
private String sex;
@Email(message = "email 格式不正确")
@NotNull(message = "email 不能为空")
private String email;
}
@RestController
@RequestMapping("/api")
public class PersonController {
@PostMapping("/person")
public Result<Person> getPerson(@RequestBody @Valid Person person) {
return Result.ok(person);
}
}

校验路径参数及请求参数#

对于路径参数和请求参数这种普通方法参数的校验,则需要先在类上添加 @Validated 注解,让 Spring 启用对这个类的参数校验,然后在方法参数前加上对应的校验注解:

@RestController
@RequestMapping("/api")
@Validated
public class PersonController {
@GetMapping("/person/{id}")
public Result<Integer> getPersonByID(
@PathVariable("id")
@Max(value = 5, message = "ID 不能超过 5")
Integer id
) {
return Result.ok(id);
}
@GetMapping("/person")
public Result<String> findPersonByName(
@RequestParam("name")
@NotBlank(message = "姓名不能为空")
@Size(max = 10, message = "姓名长度不能超过 10")
String name
) {
return Result.ok("Find the person : " + name);
}
}

全局异常处理#

首先定义错误码接口,错误码由 codemessagestatus组成:

public interface IErrorCode {
int getCode();
HttpStatus getStatus();
String getMessage();
}

每个模块都要实现自己的错误码接口,这里以通用错误码为例:

@Getter
@AllArgsConstructor
public enum ErrorCode implements IErrorCode {
RESOURCE_NOT_FOUND(1001, "未找到该资源", HttpStatus.NOT_FOUND),
REQUEST_VALIDATION_FAILED(1002, "请求数据格式验证失败", HttpStatus.BAD_REQUEST),
SYSTEM_ERROR(5000, "系统繁忙,请稍后重试", HttpStatus.INTERNAL_SERVER_ERROR);
private final int code;
private final String message;
private final HttpStatus status;
}

然后实现基类异常,它由错误码和负载数据组成:

@Getter
public abstract class BaseException extends RuntimeException {
private final ErrorCode errorCode;
private final Map<String, Object> data = new HashMap<>();
// 带数据的构造方法
protected BaseException(ErrorCode errorCode, Map<String, Object> data) {
super(errorCode.getMessage());
this.errorCode = errorCode;
if (!ObjectUtils.isEmpty(data)) {
this.data.putAll(data);
}
}
// 不带数据的构造方法
protected BaseException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
}

具体的业务异常,可以考虑提供更加友好的构造方法:

public class ResourceNotFoundException extends BaseException {
public ResourceNotFoundException() {
super(ErrorCode.RESOURCE_NOT_FOUND);
}
public ResourceNotFoundException(Map<String, Object> data) {
super(ErrorCode.RESOURCE_NOT_FOUND, data);
}
}

接下来实现相应结果类,响应结果包括 codemessagestatusdatapathtimestamp ,只有操作失败才会返回 path

@Data
public class Result<T> {
private int code;
private String message;
private int status;
private T data;
private Instant timestamp;
private String path;
private Result() {
this.timestamp = Instant.now();
}
public static <T> Result<T> success(T data) {
return Result.success(data, "操作成功");
}
public static <T> Result<T> success(T data, String message) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage(message);
result.setData(data);
result.setStatus(HttpStatus.OK.value());
return result;
}
public static Result<Map<String, Object>> failure(IErrorCode errorCode, String path, Map<String, Object> data) {
Result<Map<String, Object>> result = new Result<>();
result.setCode(errorCode.getCode());
result.setMessage(errorCode.getMessage());
result.setStatus(errorCode.getStatus().value());
result.setData(data);
result.setPath(path);
return result;
}
}

最后定义全局异常处理器:

// @RestControllerAdvice = @ResponseBody + @ControllerAdvice
// 使用 value = {xxx.class, yyy.class} 可以只处理指定类抛出的异常
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// 指定方法要捕获的异常字节码,匹配范围越小的匹配优先级越高
@ExceptionHandler(value = BaseException.class)
public Result<Map<String, Object>> handleBaseException(BaseException e, HttpServletRequest request) {
log.warn(
"Business exception | code={} | message={} | status={} | data={} | path={}",
e.getErrorCode().getCode(),
e.getErrorCode().getMessage(),
e.getErrorCode().getStatus(),
e.getData(),
request.getRequestURI()
);
return Result.failure(e.getErrorCode(),
request.getRequestURI(),
e.getData()
);
}
@ExceptionHandler(value = Exception.class)
public Result<Map<String, Object>> handleException(Exception e, HttpServletRequest request) {
log.error(
"System exception | path={} | message={}",
request.getRequestURI(),
e.getMessage(),
e
);
return Result.failure(
ErrorCode.SYSTEM_ERROR,
request.getRequestURI(),
null
);
}
}

事务#

事务管理方式#

  • 编程式事务管理:通过 TransactionTemplate或者TransactionManager手动管理事务,实际应用中很少使用
  • 声明式事务管理:使用 @Transactional 注解管理事务,推荐使用

事务属性详解#

事务传播行为#

@Transactional(propagation=Propagation.取值)

  • 加入当前事务
    • REQUIRED(默认):
      • 当前有事务,则加入当前事务。
      • 当前没有事务,则开启一个新事务。
    • SUPPORTS
      • 当前有事务,则加入当前事务。
      • 当前没有事务,则在无事务的状态下执行。
    • MANDATORY
      • 当前有事务,则加入当前事务。
      • 当前没有事务,抛出异常。
  • 不加入当前事务
    • REQUIRED_NEW
      • 当前有事务,则挂起当前事务,同时开启一个新事务。
      • 当前没有事务,则直接开启一个新事务。
    • NOT_SUPPORTED
      • 当前有事务,则挂起当前事务,在没有事务的状态下执行。
      • 当前没有事务,则直接在没有事务的状态下执行。
    • NEVER
      • 当前有事务,抛出异常。
      • 当前没有事务,则在没有事务的状态下执行。
    • NESTED
      • 当前有事务,则开启当前事务的子事务。
      • 当前没有事务,则开启一个新事务。
NOTE

REQUIRED_NEWNESTED 的区别主要在于:前者在方法执行完毕时就提交事务了,而后者需要在主事务执行完毕时才能提交事务。

事务隔离级别#

@Transactional(isolation=Isolation.取值)

  • DEFAULT:数据库设置的默认隔离级别。
  • READ_UNCOMMITTED:读未提交。
  • READ_COMMITTED:读已提交。
  • REPEATABLE_READ:可重复读。
  • SERIALIZABLE:串行化。

事务超时属性#

@Transactional(timeout=123):以秒为单位,默认值是-1,表示不指定超时时间。

事务只读属性#

@Transactional(readOnly = true):建议在一次需要执行多个查询语句的时候开启,这样这些查询语句可以共用一个读快照,不会出现数据不一致的问题。

事务回滚规则#

@Transactional(rollbackFor = Exception.class):指定遇到什么异常会触发事务回滚,默认值是 RuntimeException.class,一般需要指定为 Exception.class 以保证所有异常都要触发回滚。

事务使用注意事项#

  • @Transactional 注解只有作用到 public 方法上事务才生效。
  • 不推荐在接口上使用 @Transactional,因为只有 JDK 代理会识别这个注解,有兼容问题。
  • 避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效。
  • 被 @Transactional 注解的方法所在的类必须被 Spring 管理,否则不生效。
  • 底层使用的数据库必须支持事务机制,否则不生效。
  • @Transactional:可以标记在类上,也可以标记在方法上,如果同时标记在类上和方法上,则以方法上的注解为准。

核心概念#

IoC#

描述#

IoC 指的是控制反转,它将创建管理对象的权利交给第三方容器。在传统的写法里,一个对象使用另一个对象时要自己手动 new,自己来管理依赖。而使用 IoC,只需要描述自己需要什么,IoC 容器会将对象自动装配好。

优势#

  • 显著降低了对象之间的耦合度。
  • 使对象资源更加容易创建并管理。

AOP#

基本理解#

AOP 即面向切面编程。它最大的作用其实就是解耦。我们在写代码的时候经常会遇到一种情况,就是像记录日志权限校验这些逻辑,它们其实跟具体的业务没啥关系,但又必须得写,而且很多方法里都要写,这就很麻烦,代码也很乱。所以 AOP 就是为了解决这个问题的。它能把这些杂活单独抽出来,封装成一个切面,然后动态地织入到业务逻辑里。这样业务代码就干净了,只关注业务逻辑本身。从底层原理来看,AOP 是基于动态代理实现的。在 Spring 中,如果类实现了接口,用的是 JDK 动态代理,否则使用 CGLIB 动态代理。在 SpringBoot 2.0 之前,默认使用 JDK 动态代理。在 SpringBoot 2.0 之后,默认使用 CGLIB 动态代理。

核心术语#

  • 横切关注点 (Cross-cutting Concerns) :散落在系统各个角落、与核心业务逻辑无关,但又被多个模块需要的公共行为
  • 切面 (Aspect):对“横切关注点”的模块化封装。在 SpringBoot 中,它通常表现为一个带有 @Aspect 注解的类。
  • 连接点 (JoinPoint):程序执行过程中能够被拦截的所有位置。
  • 切点 (Pointcut):用于筛选连接点的表达式。它决定了切面具体要在哪些连接点上生效。
  • 通知 (Advice):切面在切点处执行的具体操作。
  • 目标对象(Target):通知所对应的对象。
  • 织入 (Weaving):将切面应用到目标对象,并生成代理对象的过程。

通知类型#

aspectj-advice-types

  • 前置通知 (Before):目标方法执行前触发。
  • 后置通知 (After):目标方法执行后触发(无论成功或异常都会执行)。
  • 返回通知 (AfterReturning):目标方法成功返回后触发。
  • 异常通知 (AfterThrowing):目标方法抛出异常后触发。
  • 环绕通知 (Around):它包裹了目标方法,可以在方法执行前后自定义逻辑,相当于前后通知;也可以用 try-catch 语句包裹方法执行语句,相当于异常通知;甚至可以决定是否执行目标方法(类似于拦截器)。

应用场景#

  • 日志记录:自定义日志记录注解,利用 AOP,一行代码即可实现日志记录。
  • 耗时统计:利用 AOP 在目标方法的执行前后统计方法的执行时间,方便优化和分析。
  • 事务管理@Transactional 注解可以让 Spring 为我们进行事务管理比如回滚异常操作,免去了重复的事务管理逻辑。@Transactional 注解就是基于 AOP 实现的。
  • 权限控制:利用 AOP 在目标方法执行前判断用户是否具备所需要的权限,如果具备,就执行目标方法,否则就不执行。
  • 接口限流:利用 AOP 在目标方法执行前通过具体的限流算法和实现对请求进行限流处理。
  • 缓存管理:利用 AOP 在目标方法执行前后进行缓存的读取和更新。

常用语法#

切面定义#

使用 @Aspect 注解标注切面定义类,这个类的内部可以定义切点方法以及通知方法。

不同切面类中,默认按照切面类的类名字母排序:

  • 对于目标方法前的通知方法,字母排名靠前的先执行。
  • 对于目标方法后的通知方法,字母排名靠前的后执行。

可以用 @Order(数字) 加在切面类上控制通知执行顺序:

  • 对于目标方法前的通知方法,数字小的先执行。
  • 对于目标方法后的通知方法,数字小的后执行。

切点定义#

@PointCut("切点表达式") 加到某个方法上,就相当于将方法名作为这个切点表达式的名称,之后其他需要这个切点表达式的地方可以简单替换为 切点表达式名称()

通知方法定义#

  • @Before("切点表达式"):前置通知。
  • @After("切点表达式"):后置通知
  • @AfterReturning("切点表达式"):返回通知。
  • @AfterThrowing("切点表达式"):异常通知。
  • @Around("切点表达式"):环绕通知。
TIP
  • @Around 环绕通知需要自己调用原始方法,而其他通知无需调用原始方法。
  • @Around 环绕通知的返回值是 Object 类型。

切点表达式#

  • execution
    • 语法结构
      • execution(修饰符? 返回值 包名.类名.?方法名(参数) 异常?)
      • ?表示前面的一小部分内容可以省略,并不用实际写出来。)
    • 核心通配符
      • *:匹配 一个 元素(任意返回值、任意类名、任意方法名、任意包的一层、一个参数)。
      • .. :匹配 多个 元素(任意层级的子包、任意个数的参数)。
    • 逻辑运算符(组合多个切点表达式)
      • && :同时满足。
      • ||:满足其一即可。
      • !:排除。
  • @annotation
    • @annotation(注解的全类名) 指被注解标注的所有方法都属于同一个切点表达式,控制颗粒度更加精细。

连接点常用方法#

@Around("webLog()")
// 其他通知方法的参数类型是 JoinPoint
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取目标类名
String className = joinPoint.getTarget().getClass().getName();
// 获取目标方法签名
Signature signature = joinPoint.getSignature();
// 获取目标方法名
String methodName = joinPoint.getSignature().getName();
// 获取目标方法运行参数
Object[] args = joinPoint.getArgs();
// 执行原始方法,获取返回值(环绕通知特有)
Object res = joinPoint.proceed();
return res;
}

实现方式#

  • 运行时增强
    • JDK 动态代理
    • CGLIB 动态代理
  • 编译时增强
    • AspectJ

自动装配原理#

概述#

Spring Boot 自动装配的核心在于 @EnableAutoConfiguration 注解。 该注解通过 @Import 导入了 AutoConfigurationImportSelector 类。作为 ImportSelector 接口的实现者,它的核心职责是批量加载配置类。 具体流程是:它会读取 classpath 下 META-INF/spring.factories 文件中的全限定名,获取所有候选的自动配置类,再配合 @ConditionalXXX 条件注解进行过滤和筛选,最终将满足条件的配置类注入到 Spring 容器中。

源码分析#

@SpringBootAppliaction 定义的源码:

// ...
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
// ...
}

@EnableAutoConfiguration 定义的源码:

// ...
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}

AutoConfigurationImportSelectorselectImports 方法的源码:

public class AutoConfigurationImportSelector implements DeferredImportSelector, ... {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
// ...
}

AutoConfigurationImportSelectorgetAutoConfigurationEntry 源码:

AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
// 检查自动配置是否启用(enabled)
// 如果注解 @EnableAutoConfiguration 被禁用(通过属性 spring.boot.enableautoconfiguration=false)
// 就直接返回一个空的 AutoConfigurationEntry,不进行自动配置
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
// 获取 @EnableAutoConfiguration 的注解属性
// 例如 exclude / excludeName 等,用于后续筛选候选自动配置类
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 获取候选的自动配置类列表
// 主要从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件读取
// 这些类都是可能需要导入的自动配置类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// 对候选自动配置类进行处理
// 去重,避免重复注册
configurations = this.removeDuplicates(configurations);
// 获取用户通过 exclude/excludeName 指定的排除类
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
// 校验排除类是否合法,抛异常提示错误配置
this.checkExcludedClasses(configurations, exclusions);
// 从候选类列表中移除排除类
configurations.removeAll(exclusions);
// 根据条件注解(如 @ConditionalOnClass / @ConditionalOnMissingBean)过滤候选类
configurations = this.filter(configurations, autoConfigurationMetadata);
// 发布 AutoConfigurationImportEvent 事件,允许监听器获取导入和排除的类信息
this.fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回最终的 AutoConfigurationEntry,包括最终导入的配置类和排除的类
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}

参考#

SpringBoot 基础知识总结
https://starrainer.cn/posts/springboot-基础知识总结/
作者
星雨月音
发布于
2026-01-10
许可协议
CC BY-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00