Appearance
Spring篇
本章内容都由网上整合,以及个人思考,仅用于学习分享,如有侵权部分或者思考错误地方,欢迎大家及时提出,本人一定及时纠正。
Spring、SpringMVC、SpringBoot有什么区别?
Spring
Spring 是一个开源的 Java 应用程序框架,旨在简化企业级应用程序的开发。它的核心特性包括:
控制反转(IoC):通过依赖注入(DI),Spring 管理对象的创建和生命周期,降低组件之间的耦合度。
面向切面编程(AOP):允许将跨越多个模块的通用功能(如日志记录、事务管理)分离出来,提高代码的模块化和可维护性。
SpringMVC
SpringMVC 是 Spring 框架中的一个模块,专门用于构建基于模型-视图-控制器(MVC)模式的 Web 应用程序。它的主要功能包括:
- 请求处理:将 HTTP 请求映射到相应的控制器方法。
- 视图解析:将控制器返回的数据模型渲染为视图(如 JSP、Thymeleaf)。
- 表单处理和验证:简化用户输入的处理和验证。
Spring Boot
Spring Boot 是基于 Spring 框架的一个子项目,旨在简化 Spring 应用程序的创建和部署。它的特点包括:
- 自动配置:根据项目依赖和配置,自动设置合理的默认值,减少手动配置的工作量。
- 内嵌服务器:内置如 Tomcat、Jetty 等服务器,使应用程序可以独立运行,无需外部容器。
- 简化依赖管理:提供一系列开箱即用的启动器(Starter),方便引入常用功能模块。
总结
- Spring:一个全面的企业级应用程序框架,提供 IoC、AOP 等核心功能。
- SpringMVC:Spring 框架中的一个模块,专注于构建基于 MVC 模式的 Web 应用程序。
- Spring Boot:基于 Spring 的子项目,提供自动配置和内嵌服务器,简化 Spring 应用程序的开发和部署。
Spring中有哪些常用注解
1. 组件扫描和自动装配相关注解:
@Component
:用于将一个类标识为 Spring 管理的组件(Bean)。@Service
:用于标识服务层组件,实际上是@Component
的一种特殊化形式,表示业务逻辑层的组件。@Repository
:用于标识持久层组件,同样是@Component
的特殊化,表示数据访问层的组件,并支持持久层的异常转换。@Controller
:用于标识控制层组件,表示该类是一个控制器,用于处理用户请求。@Autowired
:用于自动装配 Bean,Spring 会自动满足被注解的字段、构造函数或方法的依赖。@Qualifier
:与@Autowired
一起使用,用于在有多个同类型 Bean 时,指定注入的具体 Bean。@Primary
:当有多个同类型的 Bean 时,标识首选的 Bean。
2. 配置相关注解:
@Configuration
:用于定义配置类,该类可以包含一个或多个@Bean
方法,这些方法会被 Spring 容器处理以生成 Bean 定义和服务请求。@Bean
:用于告诉 Spring,一个方法将返回一个需要注册为 Bean 的对象。@PropertySource
:用于指定外部属性文件的位置,并将其加载到 Spring 的环境中。
3. AOP(面向切面编程)相关注解:
@Aspect
:用于定义一个切面(Aspect),即关注于横切关注点的模块化。@Before
:在目标方法执行之前执行通知。@After
:在目标方法执行之后执行通知。@AfterReturning
:在目标方法成功执行之后执行通知。@AfterThrowing
:在目标方法抛出异常后执行通知。@Around
:环绕通知,可以在目标方法执行前后自定义行为。
4. 事务管理相关注解:
@Transactional
:用于声明事务性,在方法或类上使用该注解,表示其中的操作需要在事务中执行。
5. Spring MVC 相关注解:
@RequestMapping
:用于映射请求到处理方法或类上,指定 URL 路径和请求方法等。@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
:分别用于映射 GET、POST、PUT、DELETE 请求到处理方法上,是@RequestMapping
的快捷方式。@RequestParam
:用于将请求参数绑定到方法的参数上。@PathVariable
:用于将 URL 路径中的变量绑定到方法的参数上。@RequestBody
:用于将请求体绑定到方法的参数上。@ResponseBody
:用于将方法的返回值作为响应体返回。@RestController
:组合注解,相当于@Controller
和@ResponseBody
的结合,表示该类中的所有方法的返回值都直接作为响应体返回。
6. 条件装配相关注解:
@Conditional
:根据特定条件,决定某个 Bean 是否被注册到 Spring 容器中。@Profile
:用于根据环境配置,选择性地加载某些 Bean 或配置类。
7. 异常处理相关注解:
@ExceptionHandler
:用于定义全局或局部的异常处理方法。@ControllerAdvice
:用于定义全局的异常处理类,集中处理控制器层的异常。
SpringAOP的理解
Spring AOP(Aspect-Oriented Programming,面向切面编程)。旨在通过将横切关注点(cross-cutting concerns)从业务逻辑中分离出来,提高代码的模块化和可维护性。
- 核心概念
- 切面(Aspect):封装横切逻辑的模块,包含通知和切入点定义。通过
@Aspect
注解标记。 - 通知(Advice):定义切面在何时执行,包括:
- 前置通知(@Before):方法执行前执行。
- 后置通知(@After):方法执行后执行(无论成功或异常)。
- 返回通知(@AfterReturning):方法成功返回后执行。
- 异常通知(@AfterThrowing):方法抛出异常后执行。
- 环绕通知(@Around):包裹目标方法,控制其执行。
- 切入点(Pointcut):通过表达式(如
execution(...)
)定义哪些方法需要被拦截。 - 连接点(Join Point):程序执行中的可插入点(如方法调用),Spring AOP仅支持方法级别的连接点。
- 织入(Weaving):将切面应用到目标对象的过程,Spring AOP通过动态代理在运行时实现。
- 实现机制
- 动态代理:
- JDK动态代理:针对实现了接口的类。
- CGLIB代理:针对无接口的类,生成子类(继承的方式)作为代理。
- 代理对象:客户端调用的是代理对象,代理负责将方法调用委托给目标对象,并在适当时机执行通知逻辑。
- 局限性
- 作用范围:仅拦截Spring容器管理的Bean。
- 方法限制:默认仅支持public方法。
- 自调用问题:同一类内部方法调用不会触发代理(需通过代理对象调用或使用AspectJ)。
- 应用场景
- 日志记录:统一记录方法调用信息。
- 事务管理:通过
@Transactional
实现声明式事务。 - 安全控制:检查权限或角色。
- 性能监控:统计方法执行时间。
对Spring中的IOC的理解?
Ioc——Inversion of Control
,即控制反转,是一种设计思想。在Java开发中,IOC
意味着将你设计好的对象交给容器管理。下面举一些例子说明
1. 传统方式:手动管理对象依赖
开发者需要 自己创建对象 并 手动注入依赖,代码耦合度高。
java
// 1. 定义UserRepository
public class UserRepository {
public void saveUser(String username) {
System.out.println("保存用户: " + username);
}
}
// 2. 定义UserService,直接依赖UserRepository
public class UserService {
private UserRepository userRepository;
// 开发者需要手动创建依赖对象
public UserService() {
this.userRepository = new UserRepository(); // 直接实例化
}
public void createUser(String username) {
userRepository.saveUser(username);
}
}
// 3. 使用UserService
public class Main {
public static void main(String[] args) {
UserService userService = new UserService(); // 手动创建对象
userService.createUser("张三");
}
}
问题:
UserService
直接依赖具体的UserRepository
,紧耦合。- 若想替换
UserRepository
的实现(比如换为MockUserRepository
),必须修改UserService
的代码。
2. IOC方式:容器管理对象依赖
将对象的创建和依赖注入交给 IOC容器(如Spring),代码解耦。
步骤1:定义Bean
java
// 1. 定义UserRepository接口,面向接口编程
public interface UserRepository {
void saveUser(String username);
}
// 2. 具体实现类(由容器管理)
@Component // 标记为Bean,由Spring容器管理
public class DatabaseUserRepository implements UserRepository {
@Override
public void saveUser(String username) {
System.out.println("数据库保存用户: " + username);
}
}
// 3. 定义UserService,依赖注入UserRepository
@Component // 标记为Bean
public class UserService {
private final UserRepository userRepository;
// 通过构造函数注入依赖(容器自动完成)
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void createUser(String username) {
userRepository.saveUser(username);
}
}
步骤2:配置容器(使用Java Config)
java
@Configuration
@ComponentScan("com.example") // 扫描包下的@Component注解
public class AppConfig {
// 容器会自动创建UserService和DatabaseUserRepository的Bean
}
步骤3:从容器获取对象
java
public class Main {
public static void main(String[] args) {
// 启动IOC容器
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 从容器获取UserService(容器已自动注入依赖)
UserService userService = context.getBean(UserService.class);
userService.createUser("张三");
}
}
关键点:
- 控制反转:对象的创建和依赖注入由容器完成,而非开发者手动控制。
- 解耦:
UserService
不再依赖具体的DatabaseUserRepository
,而是依赖接口。 - 灵活性:若想替换实现(例如用
MockUserRepository
),只需修改容器配置,无需改动UserService
。
对比总结
传统方式 | IOC方式 |
---|---|
手动创建对象 (new UserRepository() ) | 容器自动创建对象 (@Component ) |
类之间直接依赖具体实现 | 依赖接口,容器注入具体实现 (@Autowired ) |
代码紧耦合,难以扩展 | 代码解耦,易于替换实现 |
生动类比
想象你去餐馆点菜:
- 传统方式:你不仅要下单,还要亲自去厨房做菜(自己管理对象)。
- IOC方式:你只需下单(定义需求),厨师(容器)负责做菜并端给你(依赖注入)。
补充:注入方式有三种
构造函数注入
javaimport org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; @Component public class UserService { private final UserRepository userRepository; @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } // 业务方法 }
Setter方法注入
javaimport org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; @Component public class UserService { private UserRepository userRepository; @Autowired public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } // 业务方法 }
注解注入
javaimport org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; @Component public class UserService { @Autowired private UserRepository userRepository; // 业务方法 }
Bean的作用域
在 Spring 框架中,Bean 的作用域(Scope) 决定了 Bean 实例的生命周期和可见范围。Spring 提供了多种作用域,开发者可以根据业务需求选择合适的范围来管理 Bean 的创建和销毁行为。
Spring 支持的 Bean 作用域
作用域 | 描述 | 适用场景 |
---|---|---|
singleton | 默认作用域,容器中仅存在一个 Bean 实例,所有请求共享同一对象。 | 无状态的 Bean(如 Service、DAO),线程安全且无需维护状态。 |
prototype | 每次请求 Bean 时,容器都会创建一个新实例。 | 需要维护状态的 Bean(如用户会话),或有线程安全问题的对象。 |
request | 每个 HTTP 请求创建一个新实例(仅限 Web 应用)。 | 存储与单个 HTTP 请求相关的数据(如表单提交)。 |
session | 每个用户会话(Session)创建一个新实例(仅限 Web 应用)。 | 存储用户会话数据(如购物车、登录信息)。 |
global session | 全局会话作用域仅在基于 portlet 的 Web 应用中有效。 | 在基于 Portlet 的应用程序中,多个 Portlet 可能需要访问共享的数据。使用全局会话作用域,可以确保在整个全局会话期间,所有 Portlet 都访问同一个 Bean 实例,从而共享数据。 |
application | 每个 ServletContext 生命周期内创建一个实例(类似单例,但属于 ServletContext 范围)。 | 全局共享但需与 ServletContext 生命周期绑定的对象(如全局配置)。 |
websocket | 每个 WebSocket 会话创建一个实例(WebSocket 长连接)。 | 实时通信场景(如聊天室),存储与单个 WebSocket 连接相关的状态。 |
代码示例
- 单例(Singleton)
java
@Component
@Scope("singleton") // 可省略(默认)
public class SingletonBean {
// 无论多少次注入,都是同一个实例
}
- 原型(Prototype)
java
@Component
@Scope("prototype")
public class PrototypeBean {
// 每次注入或 getBean() 时生成新实例
}
- 请求(Request)和会话(Session)
java
// Web 应用中需添加依赖(如 spring-web)
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedBean {
// 每个 HTTP 请求生成一个新实例
}
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionScopedBean {
// 每个用户会话生成一个新实例
}
Bean的生命周期
流程图解
mermaid
graph TD
A[容器启动] --> B[加载Bean定义]
B --> C[实例化Bean: 构造函数]
C --> D[属性注入: 依赖赋值]
D --> E[Aware接口回调]
E --> F1[BeanPostProcessor.postProcessBeforeInitialization]
F1 --> G1[初始化方法1: @PostConstruct]
G1 --> G2[初始化方法2: InitializingBean.afterPropertiesSet]
G2 --> G3[初始化方法3: 自定义init-method]
G3 --> F2[BeanPostProcessor.postProcessAfterInitialization]
F2 --> H[Bean就绪]
H --> I[容器关闭]
I --> J1[销毁方法1: @PreDestroy]
J1 --> J2[销毁方法2: DisposableBean.destroy]
J2 --> J3[销毁方法3: 自定义destroy-method]
分步详解与代码验证
1. 加载 Bean 定义
触发时机:容器启动时解析配置(XML/注解/JavaConfig)。
动作:读取 Bean 的元数据(类名、作用域、初始化方法等)。
扩展点:
BeanFactoryPostProcessor
(可修改 Bean 定义)。java@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { System.out.println("BeanFactoryPostProcessor 修改Bean定义..."); } }
2. 实例化 Bean
动作:调用 构造函数 创建对象。
注意:若使用构造器注入,依赖会在此阶段通过构造函数参数注入。
java@Component public class UserService { // 构造函数注入示例 private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; System.out.println("UserService 实例化"); } }
3. 属性注入
动作:通过反射为字段或 Setter 方法注入依赖。
示例:
java@Component public class OrderService { @Autowired // 字段注入 private UserService userService; @Autowired // Setter注入 public void setPaymentService(PaymentService paymentService) { System.out.println("OrderService 依赖注入完成"); } }
4. Aware 接口回调
作用:让 Bean 感知容器信息。
常见接口:
接口 回调方法 获取的信息 BeanNameAware
setBeanName(String)
Bean 的 ID BeanFactoryAware
setBeanFactory(BeanFactory)
BeanFactory 实例 ApplicationContextAware
setApplicationContext(ApplicationContext)
Spring 上下文 java@Component public class AwareDemo implements BeanNameAware, ApplicationContextAware { private String beanName; private ApplicationContext context; @Override public void setBeanName(String name) { this.beanName = name; System.out.println("BeanNameAware: " + beanName); } @Override public void setApplicationContext(ApplicationContext context) { this.context = context; System.out.println("ApplicationContextAware 完成"); } }
5. BeanPostProcessor 前置处理
接口:
BeanPostProcessor
方法:
postProcessBeforeInitialization()
用途:在初始化前修改 Bean(如生成代理)。
java@Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { System.out.println("Before初始化: " + beanName); return bean; // 可返回代理对象 } }
6. 初始化方法
执行顺序:
@PostConstruct
注解方法(JSR-250 标准)InitializingBean.afterPropertiesSet()
(Spring 接口)- 自定义初始化方法(XML 中
init-method
或@Bean(initMethod="...")
)
java@Component public class InitDemo implements InitializingBean { @PostConstruct public void postConstruct() { System.out.println("@PostConstruct 调用"); } @Override public void afterPropertiesSet() { System.out.println("InitializingBean.afterPropertiesSet 调用"); } public void customInit() { System.out.println("自定义初始化方法"); } }
7. BeanPostProcessor 后置处理
方法:
postProcessAfterInitialization()
示例:
java@Override public Object postProcessAfterInitialization(Object bean, String beanName) { System.out.println("After初始化: " + beanName); return bean; }
8. Bean 就绪
状态:Bean 已完全初始化,可被其他对象使用。
验证:
javapublic static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = context.getBean(UserService.class); userService.createUser("张三"); // Bean正常使用 }
9. 销毁阶段
触发条件:容器关闭(仅 Singleton Bean)。
执行顺序:
@PreDestroy
注解方法DisposableBean.destroy()
- 自定义销毁方法(XML 中
destroy-method
或@Bean(destroyMethod="...")
)
java@Component public class DestroyDemo implements DisposableBean { @PreDestroy public void preDestroy() { System.out.println("@PreDestroy 调用"); } @Override public void destroy() { System.out.println("DisposableBean.destroy 调用"); } public void customDestroy() { System.out.println("自定义销毁方法"); } }
Spring循环依赖是怎么解决的
Spring 通过 三级缓存机制 和 提前暴露半成品 Bean 来解决单例作用域下的循环依赖问题。以下是详细的分步说明:
循环依赖场景
假设存在两个 Bean:
java
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
A 依赖 B,B 也依赖 A,形成循环依赖。
Spring 解决循环依赖的流程
1. 三级缓存的作用
Spring 通过三级缓存管理 Bean 的创建过程:
- 一级缓存(singletonObjects):存储完全初始化好的 Bean。
- 二级缓存(earlySingletonObjects):存储提前暴露的 Bean(已实例化但未完成初始化的半成品)。
- 三级缓存(singletonFactories):存储 Bean 的工厂对象,用于生成半成品 Bean。
2. 解决流程详解
mermaid
sequenceDiagram
participant Spring容器
participant A的创建流程
participant B的创建流程
Spring容器->>A的创建流程: 1. 开始创建Bean A
A的创建流程->>A的创建流程: 2. 实例化A(调用构造函数)
A的创建流程->>Spring容器: 3. 将A的半成品对象工厂放入三级缓存
A的创建流程->>A的创建流程: 4. 属性注入:发现依赖B
A的创建流程->>Spring容器: 5. 请求获取Bean B
Spring容器->>B的创建流程: 6. 开始创建Bean B
B的创建流程->>B的创建流程: 7. 实例化B(调用构造函数)
B的创建流程->>Spring容器: 8. 将B的半成品对象工厂放入三级缓存
B的创建流程->>B的创建流程: 9. 属性注入:发现依赖A
B的创建流程->>Spring容器: 10. 请求获取Bean A
Spring容器->>A的创建流程: 11. 从三级缓存获取A的半成品对象
Spring容器-->>B的创建流程: 12. 返回A的半成品对象
B的创建流程->>B的创建流程: 13. 完成B的属性注入和初始化
B的创建流程->>Spring容器: 14. 将B放入一级缓存(完成态)
Spring容器-->>A的创建流程: 15. 返回B的完成态对象
A的创建流程->>A的创建流程: 16. 完成A的属性注入和初始化
A的创建流程->>Spring容器: 17. 将A放入一级缓存(完成态)
3. 关键步骤解析
- 创建 Bean A:
- 实例化 A(调用构造函数),此时 A 是一个半成品(未注入依赖)。
- 将 A 的工厂对象(
ObjectFactory
)存入 三级缓存(singletonFactories
)。
- 注入 A 的依赖 B:
- Spring 发现 A 需要注入 B,于是尝试从容器中获取 B。
- 由于 B 尚未创建,触发 B 的创建流程。
- 创建 Bean B:
- 实例化 B(调用构造函数),此时 B 是一个半成品。
- 将 B 的工厂对象存入三级缓存。
- 注入 B 的依赖 A:
- Spring 发现 B 需要注入 A,尝试从容器中获取 A。
- 关键点:此时 A 的半成品工厂对象仍在三级缓存中:
- Spring 从三级缓存中取出 A 的工厂对象,生成 A 的早期引用(半成品)。
- 将 A 的半成品对象存入 二级缓存(
earlySingletonObjects
),并从三级缓存中删除 A 的工厂对象。
- 完成 B 的初始化:
- 将 A 的半成品注入到 B 中。
- 继续完成 B 的属性注入和初始化(如
@PostConstruct
)。 - 将 B 的完成态对象存入 一级缓存(
singletonObjects
)。
- 完成 A 的初始化:
- 从一级缓存中获取 B 的完成态对象,注入到 A 中。
- 继续完成 A 的属性注入和初始化。
- 将 A 的完成态对象存入一级缓存,并从二级缓存中删除 A 的半成品。
为何需要三级缓存?
- 三级缓存(singletonFactories):存储 Bean 的工厂对象,用于处理存在 AOP 代理的情况。 当 Bean 需要被代理时,工厂对象可以确保返回的是代理后的对象(而不是原始对象)。 若直接缓存半成品对象,则无法处理代理对象的生成。
- 二级缓存(earlySingletonObjects): 避免重复执行工厂逻辑。一旦通过三级缓存生成半成品对象,后续直接从二级缓存获取。
无法解决的循环依赖场景
构造函数注入的循环依赖:
java@Component public class A { private B b; @Autowired public A(B b) { // 构造函数注入 this.b = b; } } @Component public class B { private A a; @Autowired public B(A a) { // 构造函数注入 this.a = a; } }
- 原因:实例化 A 时需要先实例化 B,而实例化 B 又需要实例化 A,导致死锁。Spring 会抛出
BeanCurrentlyInCreationException
。
- 原因:实例化 A 时需要先实例化 B,而实例化 B 又需要实例化 A,导致死锁。Spring 会抛出
原型(Prototype)作用域的循环依赖:
- 原型 Bean 每次请求都生成新实例,无法通过缓存解决循环依赖。Spring 直接抛出异常。
总结
- 解决范围:单例 Bean + Setter/字段注入。
- 核心机制:三级缓存 + 提前暴露半成品 Bean。
- 设计意义: 通过空间换时间,允许在 Bean 未完全初始化时提前暴露引用,打破循环依赖的死锁。 三级缓存确保代理对象的一致性,避免重复创建。
Spring中用到了哪些设计模式
1. 工厂模式(Factory Pattern)
作用:将对象的创建与使用分离,通过工厂统一管理对象的生命周期。 Spring 应用:
BeanFactory
:Spring 容器的核心接口,负责创建和管理 Bean。ApplicationContext
:BeanFactory
的扩展,提供更高级的工厂功能(如事件发布、资源加载)。
java
// 通过工厂获取 Bean
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = context.getBean(UserService.class);
2. 单例模式(Singleton Pattern)
作用:确保一个类只有一个实例,并提供全局访问点。 Spring 应用:
- 默认 Bean 作用域:Spring 管理的 Bean 默认是单例的,容器内共享同一实例。
java
@Component
public class SingletonBean {
// 容器中仅存在一个实例
}
3. 代理模式(Proxy Pattern)
作用:通过代理对象控制对目标对象的访问,用于实现 AOP 等功能。 Spring 应用:
- JDK 动态代理:基于接口的代理(实现相同接口的类)。
- CGLIB 代理:生成目标类的子类作为代理(用于无接口的类)。
java
// Spring AOP 通过代理增强目标方法
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("方法执行前日志");
}
}
4. 模板方法模式(Template Method Pattern)
作用:定义算法的骨架,将某些步骤延迟到子类实现。 Spring 应用:
JdbcTemplate
:封装 JDBC 操作的通用流程(如连接管理、异常处理),用户只需实现 SQL 逻辑。RestTemplate
:HTTP 请求的模板化处理。
java
jdbcTemplate.execute("INSERT INTO users(name) VALUES ('张三')");
5. 观察者模式(Observer Pattern)
作用:定义对象间的一对多依赖关系,当一个对象状态改变时,自动通知依赖它的对象。 Spring 应用:
- 事件驱动模型:通过
ApplicationEvent
和ApplicationListener
实现。
java
// 自定义事件
public class OrderEvent extends ApplicationEvent {
public OrderEvent(Object source) {
super(source);
}
}
// 监听事件
@Component
public class OrderListener implements ApplicationListener<OrderEvent> {
@Override
public void onApplicationEvent(OrderEvent event) {
System.out.println("收到订单事件: " + event.getSource());
}
}
// 发布事件
applicationContext.publishEvent(new OrderEvent("订单创建"));
6. 适配器模式(Adapter Pattern)
作用:将一个接口转换成客户端期望的另一个接口。 Spring 应用:
HandlerAdapter
:在 Spring MVC 中适配不同类型的处理器(如@Controller
、Servlet)。- AOP 中的
AdvisorAdapter
:将不同类型的通知(Advice)适配到 AOP 框架。
java
// Spring MVC 中通过 HandlerAdapter 处理不同 Controller
public class RequestMappingHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof HandlerMethod;
}
}
7. 装饰者模式(Decorator Pattern)
作用:动态地为对象添加额外职责,比继承更灵活。 Spring 应用:
BeanDefinitionDecorator
:装饰 Bean 的定义(如 XML 配置中的属性增强)。HttpServletRequestWrapper
:对请求对象进行包装(如 Spring Security 中的请求加密处理)。
java
// 自定义装饰器示例
public class CachingDataSourceDecorator implements DataSource {
private final DataSource targetDataSource;
public CachingDataSourceDecorator(DataSource dataSource) {
this.targetDataSource = dataSource;
}
@Override
public Connection getConnection() throws SQLException {
// 添加缓存逻辑
return targetDataSource.getConnection();
}
}
8. 策略模式(Strategy Pattern)
作用:定义一系列算法,将每个算法封装起来,并使它们可以互换。 Spring 应用:
- 资源加载策略:
ResourceLoader
根据前缀(如classpath:
、file:
)选择不同的资源加载策略。 - 事务管理策略:
PlatformTransactionManager
的不同实现(如 JDBC、JTA)。
java
// 根据环境选择事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource); // JDBC 事务策略
}
9. 责任链模式(Chain of Responsibility Pattern)
作用:将请求的发送者和接收者解耦,使多个对象都有机会处理请求。 Spring 应用:
- Spring Security 过滤器链:多个
Filter
按顺序处理 HTTP 请求。 - AOP 的拦截器链:多个通知(Advice)按顺序织入目标方法。
java
// Spring Security 过滤器链配置
http
.addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new LoggingFilter(), SecurityContextPersistenceFilter.class);
10. 建造者模式(Builder Pattern)
作用:分步骤构建复杂对象,分离对象的构造与表示。 Spring 应用:
BeanDefinitionBuilder
:构建 Bean 的定义元数据。UriComponentsBuilder
:构建 URI 路径。
java
UriComponents uri = UriComponentsBuilder
.fromUriString("https://example.com/{path}")
.queryParam("name", "张三")
.buildAndExpand("user");
11. 原型模式(Prototype Pattern)
作用:通过复制现有对象来创建新对象,避免重复初始化开销。 Spring 应用:
- Bean 的
prototype
作用域:每次请求时生成新实例。
java
@Scope("prototype")
@Component
public class PrototypeBean {
// 每次注入时生成新实例
}
SpringMVC的执行流程是
mermaid
sequenceDiagram
participant 用户
participant DS as DispatcherServlet
participant HM as HandlerMapping
participant HA as HandlerAdapter
participant H as Controller
participant VR as ViewResolver
participant V as View
用户 ->> DS: 发起请求 (/user/list)
DS ->> HM: 查找 Handler
HM -->> DS: 返回 Handler
DS ->> HA: 选择 HandlerAdapter
HA ->> H: 执行 Controller 方法
H -->> HA: 返回 ModelAndView
HA -->> DS: 返回 ModelAndView
DS ->> VR: 解析视图名称
VR -->> DS: 返回 View 对象
DS ->> V: 渲染视图
V -->> DS: 返回响应内容
DS ->> 用户: 返回响应结果
- 客户端发送请求
- 描述: 用户在浏览器中输入 URL 或点击链接,浏览器生成一个 HTTP 请求,包含请求头、参数等信息。
- 关键点: 请求发送到部署了 SpringMVC 应用的服务器。
- 前端控制器(DispatcherServlet)接收请求
- 描述: 所有请求首先由配置在 web.xml(或通过 Spring Boot 自动配置)中的
DispatcherServlet
捕获。 它充当整个请求处理的入口和中央调度者。 - 关键点: DispatcherServlet 被设计为 Front Controller,统一管理请求分发和响应处理。
- 查找 Handler(处理器)
- 描述: DispatcherServlet 根据请求 URL 调用 HandlerMapping。 HandlerMapping 负责扫描已注册的 Controller 以及其映射(如
@RequestMapping
注解)来确定哪个 Handler(通常是 Controller 的方法)能够处理当前请求。 - 关键点:
- 可以使用多种 HandlerMapping 实现,如
RequestMappingHandlerMapping
(基于注解)或传统的 XML 配置。 - 如果多个映射匹配,Spring 会根据优先级选择最合适的 Handler。
- 可以使用多种 HandlerMapping 实现,如
- 选择 HandlerAdapter
- 描述: DispatcherServlet 不直接调用 Handler,而是委托给一个合适的 HandlerAdapter。 HandlerAdapter 的作用是封装对 Handler 调用的细节,使得 DispatcherServlet 与具体的 Handler 类型(例如基于注解的 Controller 或实现了特定接口的 Controller)解耦。
- 关键点:
- 常用的 HandlerAdapter 有
RequestMappingHandlerAdapter
等。 - HandlerAdapter 会负责参数解析、类型转换、数据绑定等工作。
- 常用的 HandlerAdapter 有
- 执行 Handler(Controller)方法
- 描述: 在 HandlerAdapter 的帮助下,调用具体的 Controller 方法。 在此过程中,SpringMVC 使用 HandlerMethodArgumentResolver 来解析方法参数(如请求参数、路径变量、请求体等),并将它们传入 Controller 方法。
- 关键点:
- 请求参数经过数据绑定和类型转换后注入到方法参数中。
- 如果配置了拦截器(Interceptor),它们的
preHandle
方法会在此之前被调用。
- 返回 ModelAndView
- 描述: Controller 方法执行完毕后,返回一个 ModelAndView 对象。 该对象封装了:
- Model:业务数据和视图渲染所需的属性集合。
- View:视图名称(例如 JSP、Thymeleaf 模板)或视图对象。
- 关键点:
- ModelAndView 可以为空,此时表示 Controller 已直接写入响应(例如返回 JSON 数据)。
- 选择视图解析器(ViewResolver)
- 描述: DispatcherServlet 将 ModelAndView 中的视图名称交给 ViewResolver 进行解析,找到具体的视图模板。 ViewResolver 根据配置(如前缀、后缀、模板引擎等)将逻辑视图名称解析为实际视图资源。
- 关键点:
- 常用的视图解析器包括
InternalResourceViewResolver
、ThymeleafViewResolver
等。
- 常用的视图解析器包括
- 渲染视图
- 描述: 得到视图对象后,SpringMVC 将 Model 数据传递给视图进行渲染。 渲染过程通常包括将数据填充到模板中,生成最终的 HTML 页面。
- 关键点:
- 在视图渲染过程中,还可以调用一些拦截器的
postHandle
方法处理后置逻辑。 - 如果发生异常,可能会交由异常处理机制(如
@ControllerAdvice
)处理。
- 在视图渲染过程中,还可以调用一些拦截器的
- 返回响应给客户端
- 描述: 渲染完成的 HTML 响应由 DispatcherServlet 写入到 HTTP 响应中,发送给客户端浏览器。 浏览器接收到响应后展示最终页面。
- 关键点:
- 请求处理结束后,拦截器的
afterCompletion
方法会被调用,用于执行资源清理等后续工作。
- 请求处理结束后,拦截器的
补充说明
- 拦截器(Interceptors): 在整个流程中,如果配置了拦截器,SpringMVC 会在调用 Handler 前后分别执行
preHandle
、postHandle
和afterCompletion
方法,实现日志记录、安全校验、请求统计等功能。 - 异常处理: 如果某个环节出现异常,DispatcherServlet 会调用异常解析器(HandlerExceptionResolver)进行处理,返回相应的错误视图或 JSON 数据。
- 异步请求处理: SpringMVC 还支持异步请求(通过
Callable
或DeferredResult
),这部分流程会稍有不同,但总体思路与同步处理类似,只是在执行 Controller 方法后将结果异步返回。
对SpringBoot的理解是
Spring Boot 是基于 Spring 框架的 快速开发脚手架,旨在简化 Spring 应用的初始搭建、配置和开发流程。其核心理念是 约定优于配置,通过自动化机制减少开发者的手动配置工作,使开发者能专注于业务逻辑。以下是 Spring Boot 的核心理解和关键特性:
1. 核心目标
- 简化配置:消除传统 Spring 应用中繁琐的 XML 配置和样板代码。
- 快速启动:通过内嵌服务器(Tomcat/Jetty)和自动化依赖管理,实现“一键启动”应用。
- 生产就绪:集成健康检查、指标监控(Actuator)、外部化配置等企业级功能。
2. 核心特性
(1) 自动配置(Auto-Configuration)
- 原理:根据类路径中的依赖(如
spring-boot-starter-data-jpa
)和现有 Bean,自动配置 Spring 组件。 - 示例:
- 若引入
spring-boot-starter-data-jpa
,Spring Boot 会自动配置数据源、JPA 实体管理器等。 - 若未配置数据源 URL,默认使用内存数据库(如 H2)。
- 若引入
(2) 起步依赖(Starters)
作用:预定义一组依赖集合,简化 Maven/Gradle 配置。
示例:
xml<!-- 引入Web开发所需的所有依赖(Tomcat、Spring MVC、JSON支持等) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
(3) 内嵌服务器(Embedded Server)
实现:将 Tomcat、Jetty 或 Undertow 服务器嵌入应用,无需外部部署。
代码验证:
java@SpringBootApplication public class MyApp { public static void main(String[] args) { SpringApplication.run(MyApp.class, args); // 启动内嵌服务器 } }
(4) Actuator 监控
功能:提供 HTTP 端点监控应用状态(健康检查、指标、环境变量等)。
配置:
yamlmanagement: endpoints: web: exposure: include: "*" # 暴露所有监控端点
(5) 外部化配置
支持格式:YAML、Properties 文件、环境变量、命令行参数等。
优先级:命令行参数 > 外部配置文件 > 默认配置。
示例:
yaml# application.yml server: port: 8081 spring: datasource: url: jdbc:mysql://localhost:3306/mydb
3. 与传统 Spring 应用的区别
对比项 Spring Spring Boot 配置方式 需手动配置 XML 或 JavaConfig 自动配置,按需覆盖 依赖管理 手动管理依赖版本 通过 Starters 统一管理版本 内嵌服务器 需外部部署(如 WAR 到 Tomcat) 内置服务器,JAR 直接运行 开发效率 适合高度定制化场景 适合快速开发、微服务和原型验证 监控支持 需集成第三方工具(如 Prometheus) 内置 Actuator 提供开箱即用的监控功能 4. Spring Boot 核心组件
组件 作用 @SpringBootApplication
组合注解( @Configuration
+@ComponentScan
+@EnableAutoConfiguration
)SpringApplication 启动类入口,负责加载配置、启动内嵌服务器和应用上下文。 Starters 预定义依赖包(如 spring-boot-starter-web
包含 Web 开发所需依赖)。Actuator 提供生产级监控端点(如 /health
、/metrics
)。Spring Boot CLI 命令行工具,支持 Groovy 脚本快速开发。 5. 适用场景
- 微服务架构:快速构建独立部署的小型服务。
- RESTful API 开发:结合
spring-boot-starter-web
快速创建 API。 - 原型验证:通过内嵌数据库和自动化配置快速验证想法。
- 企业级应用:集成安全(Spring Security)、事务管理、批处理(Spring Batch)等模块。
SpringBoot Starter有什么用
简化依赖管理
Starter 是一组预定义的依赖集合,它将构建某类功能所需的所有依赖打包在一起。例如,
spring-boot-starter-web
包含了 Spring MVC、Jackson(用于 JSON 解析)、内嵌 Tomcat 等组件。这样,开发者只需在项目中添加一个 Starter 依赖,就能获得一整套相关功能,而不用单独配置每个库的版本。自动配置支持
Starter 与 Spring Boot 的自动配置机制紧密结合。通过引入 Starter,Spring Boot 能够根据项目的依赖自动推断出所需的默认配置,并为这些组件进行自动化配置。例如,当添加了
spring-boot-starter-data-jpa
后,Spring Boot 会自动配置数据源、EntityManagerFactory 等 JPA 相关组件。降低开发复杂性
Starter 使项目配置更加简单、直观。开发者可以专注于业务逻辑,而不用关心底层框架和依赖的细节,进一步提高开发效率。
模块化和可维护性
不同功能通过不同的 Starter 模块进行划分,项目可以根据需要灵活引入或者剔除某个功能模块,避免不必要的依赖冗余,提高应用的可维护性。
示例:
假设你正在开发一个 Web 应用,只需要在 pom.xml
中添加以下依赖即可获得 Web 开发所需的全部功能:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
通过这个 Starter,Spring Boot 会自动配置:
- Spring MVC 用于处理 HTTP 请求
- 内嵌的 Tomcat 服务器,使应用可以独立运行
- JSON 序列化/反序列化相关的库(例如 Jackson)
SpringBoot常用注解有哪些
核心启动与配置相关
@SpringBootApplication
组合了@Configuration
、@EnableAutoConfiguration
和@ComponentScan
。用于标识主启动类,让 Spring Boot 自动扫描和配置应用。@Configuration
表示该类可以使用 Spring IoC 容器作为配置类,替代传统 XML 配置文件。@EnableAutoConfiguration
让 Spring Boot 根据项目依赖自动配置 Spring 应用,通常由@SpringBootApplication
包含。@ComponentScan
指定 Spring 应扫描的包路径,自动注册 Bean。也通常由@SpringBootApplication
包含。
控制层相关
@RestController
组合了@Controller
和@ResponseBody
,用于创建 RESTful 控制器,方法返回值会自动转换为 JSON 或其他格式直接响应给客户端。@Controller
用于标识一个控制器组件,处理 Web 请求(通常结合视图解析)。@RequestMapping
、@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
用于映射 HTTP 请求到控制器方法。@GetMapping
、@PostMapping
等是@RequestMapping
的快捷方式。@RequestParam
、@PathVariable
、@RequestBody
、@ResponseBody
用于处理请求参数、路径变量、请求体与响应体的数据绑定和转换。
依赖注入相关
@Autowired
让 Spring 自动装配 Bean,可用于构造器、Setter 方法或字段上。@Qualifier
在多个同类型 Bean 存在时,用来指定注入具体的 Bean。@Value
用于将配置文件中的值(如 application.properties 或 application.yml)注入到字段或方法参数中。
Bean 定义与作用域
@Bean
标识一个方法创建一个 Bean,该 Bean 将由 Spring 容器管理。@Scope
指定 Bean 的作用域,如单例(默认)、原型、请求、会话等。@Profile
根据不同的环境(开发、测试、生产)加载不同的配置或 Bean。
异步、定时与其他
@EnableScheduling
启用定时任务支持,配合@Scheduled
使用。@EnableAsync
启用异步方法执行,配合@Async
注解使用。
测试相关
@SpringBootTest
用于测试时启动完整的 Spring Boot 应用上下文,适合集成测试。@WebMvcTest
专注于 Spring MVC 控制器层的测试,不加载全部应用上下文。@DataJpaTest
用于测试 JPA 组件,配置嵌入式数据库并只加载与数据访问相关的 Bean。
**问题13:**SpringBoot的启动流程(后续还需修改,本人目前也是一知半解)
Spring Boot的启动流程可以分为以下几个关键步骤,每个步骤通过Spring Boot的自动化配置和扩展机制简化了传统Spring应用的初始化过程:
1. 初始化SpringApplication实例
- 触发入口:调用
SpringApplication.run(主类.class, args)
。 - 操作:
- 推断应用类型(Web或非Web),决定创建哪种应用上下文(如
AnnotationConfigServletWebServerApplicationContext
)。 - 加载
META-INF/spring.factories
中的ApplicationContextInitializer
和ApplicationListener
。 - 设置主配置类(即标注
@SpringBootApplication
的类)。
- 推断应用类型(Web或非Web),决定创建哪种应用上下文(如
2. 准备运行环境(Environment)
- 操作:
- 加载配置文件(
application.properties
或application.yml
)。 - 处理命令行参数(
--server.port=8081
)。 - 设置激活的Profiles(如
--spring.profiles.active=dev
)。
- 加载配置文件(
- 触发事件:
ApplicationEnvironmentPreparedEvent
,允许监听器修改环境配置。
3. 创建应用上下文(ApplicationContext)
- 根据应用类型创建上下文:
- Servlet Web应用:
AnnotationConfigServletWebServerApplicationContext
。 - Reactive Web应用:
AnnotationConfigReactiveWebServerApplicationContext
。 - 非Web应用:
AnnotationConfigApplicationContext
。
- Servlet Web应用:
- 初始化上下文:
- 注册主配置类(通过
@SpringBootApplication
引入的配置)。 - 加载条件化配置(如
@ConditionalOnClass
)。
- 注册主配置类(通过
4. 刷新应用上下文(Refresh Context)
- 核心操作:调用
AbstractApplicationContext.refresh()
方法,执行以下子步骤:- 准备BeanFactory:配置类解析器、属性编辑器等。
- 调用BeanFactoryPostProcessor:
- 关键点:
ConfigurationClassPostProcessor
处理@Configuration
类,触发自动配置(@EnableAutoConfiguration
)。 - 自动配置原理:加载
META-INF/spring.factories
中的AutoConfiguration
类,按条件筛选有效配置。
- 关键点:
- 注册BeanPostProcessor:如处理
@Autowired
、@PostConstruct
的处理器。 - 初始化消息源、事件广播器。
- 初始化单例Bean:包括内嵌Web服务器(如Tomcat)。
- 内嵌服务器启动:在
onRefresh()
阶段创建WebServer
并启动。
- 内嵌服务器启动:在
- 发布
ContextRefreshedEvent
:表示上下文刷新完成。
5. 执行Runners
- 触发点:上下文刷新完成后,调用
CommandLineRunner
和ApplicationRunner
的实现。 - 用途:执行启动后逻辑(如数据初始化、连接测试)。
java
@Component
public class MyRunner implements CommandLineRunner {
@Override
public void run(String... args) {
System.out.println("应用启动完成,执行自定义逻辑");
}
}
6. 完成启动
- 触发事件:
ApplicationStartedEvent
。 - 日志输出:显示应用启动耗时和访问地址(如Tomcat的端口)。
- 进入运行状态:等待处理请求或执行其他任务。
关键机制解析
1. 自动配置(Auto-Configuration)
- 触发条件:
@EnableAutoConfiguration
注解(包含在@SpringBootApplication
中)。 - 流程:
- 扫描
META-INF/spring.factories
中的org.springframework.boot.autoconfigure.EnableAutoConfiguration
配置类。 - 根据条件注解(如
@ConditionalOnClass
、@ConditionalOnMissingBean
)过滤有效配置。 - 将符合条件的配置类注册为Bean,完成组件自动装配。
- 扫描
2. 内嵌服务器启动
- 实现类:
TomcatServletWebServerFactory
(默认)、JettyServletWebServerFactory
等。 - 启动时机:在应用上下文刷新的
onRefresh()
阶段创建并启动服务器。 - 端口绑定:通过
ServerProperties
配置(如server.port=8080
)。
3. 事件驱动模型
- 主要事件:
ApplicationStartingEvent
:应用启动开始。ApplicationEnvironmentPreparedEvent
:环境准备完成。ApplicationPreparedEvent
:Bean定义加载完成,上下文刷新前。ApplicationStartedEvent
:上下文刷新完成,Runners执行前。ApplicationReadyEvent
:Runners执行完成,应用就绪。
- 监听器注册:通过
META-INF/spring.factories
或@EventListener
注解。
启动流程示意图
mermaid
sequenceDiagram
participant User
participant SpringApplication
participant Environment
participant ApplicationContext
participant AutoConfiguration
participant WebServer
User->>SpringApplication: 执行run()
SpringApplication->>Environment: 准备环境(加载配置)
SpringApplication->>ApplicationContext: 创建应用上下文
ApplicationContext->>AutoConfiguration: 加载自动配置类
ApplicationContext->>WebServer: 启动内嵌服务器
SpringApplication->>ApplicationContext: 刷新上下文(Bean初始化)
SpringApplication->>Runners: 执行CommandLineRunner/ApplicationRunner
SpringApplication-->>User: 启动完成,输出日志
总结
Spring Boot通过自动化配置和内嵌服务器机制,将传统Spring应用的复杂启动流程简化为几个关键步骤:
- 环境准备:加载配置和参数。
- 上下文创建与刷新:完成Bean注册、自动配置和服务器启动。
- 后置处理:执行Runners和监听器逻辑。
问题14:SpringBoot的Jar包和普通Jar包的区别
可执行性:
- 普通JAR包:通常包含项目的编译类文件和资源,但无法独立运行。要运行这些类,通常需要将JAR包添加到应用程序的类路径中,由外部容器或应用程序加载。
- Spring Boot可执行JAR包:通过内嵌服务器(如Tomcat、Jetty等),使得应用程序可以独立运行。使用
java -jar your-application.jar
命令即可启动,无需依赖外部的应用服务器。
内部结构:
- 普通JAR包:其结构较为简单,主要包含项目的类文件和资源,通常没有特定的目录结构。
- Spring Boot可执行JAR包:具有特定的目录结构,包括
BOOT-INF/classes/
(存放应用的类文件)、BOOT-INF/lib/
(存放依赖的库)和META-INF/MANIFEST.MF
(包含启动类等配置信息)。这种结构支持Spring Boot应用的自包含运行。
依赖管理:
- 普通JAR包:不包含其依赖的其他JAR包。使用时,需要确保所有依赖项都在类路径中,否则可能导致
ClassNotFoundException
等错误。 - Spring Boot可执行JAR包:将所有依赖的JAR包打包在
BOOT-INF/lib/
目录下,确保应用在任何环境中都能找到所需的依赖,增强了部署的便利性和可靠性。
注意:
- Spring Boot默认生成的可执行JAR包不适合作为其他项目的依赖。