Appearance
Spring Framework
plain
┌─────────────────────────────────────────────────────────┐
│ Spring Framework │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Spring Data Access │ │
│ │ (JDBC, ORM, JMS, Transactions, OXM) │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Spring Web Layer │ │
│ │ (Spring MVC, WebFlux, WebSocket, Portlet) │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Spring AOP & Instrumentation │ │
│ │ (AOP, Aspects, Instrumentation) │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Spring Core Container (IoC/DI) │ │
│ │ (Beans, Core, Context, SpEL) │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Spring Test │ │
│ │ (TestContext, Mock Objects, MVC Test) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘Core Container
| 模块 | 功能说明 |
|---|---|
| spring-core | 提供框架的基础工具类,包括资源加载、类型转换等 |
| spring-beans | Bean 工厂和依赖注入的实现(BeanFactory, ApplicationContext) |
| spring-context | 扩展核心容器,添加事件传播、资源加载、国际化支持 |
| spring-expression | SpEL(Spring Expression Language)表达式语言 |
AOP & Instrumentation
| 模块 | 功能说明 |
|---|---|
| spring-aop | 面向切面编程实现(基于代理) |
| spring-aspects | 集成 AspectJ 框架 |
| spring-instrument | 类加载器工具,用于应用服务器集成 |
Data Access
| 模块 | 功能说明 |
|---|---|
| spring-jdbc | JDBC 抽象层,简化数据库操作 |
| spring-orm | 集成 ORM 框架(Hibernate, JPA, MyBatis) |
| spring-jms | Java 消息服务支持 |
| spring-tx | 声明式事务管理(@Transactional) |
Web Layer
| 模块 | 功能说明 |
|---|---|
| spring-web | Web 基础功能(文件上传、过滤器、HTTP 客户端) |
| spring-webmvc | 传统的 Spring MVC 框架(Servlet 基础) |
| spring-webflux | 响应式 Web 框架(Reactive Streams) |
| spring-websocket | WebSocket 协议支持 |
- 加载配置 → XML/注解/Java Config
- 创建 BeanFactory → 解析 Bean 定义
- 实例化 Bean → 依赖注入(DI)
- 初始化 Bean → 执行生命周期回调
- 放入容器 → 单例池管理
- 应用使用 → 通过上下文获取 Bean
// 核心接口层次 BeanFactory (基础容器) ↓ ApplicationContext (高级容器) ↓ ├─ ClassPathXmlApplicationContext ├─ AnnotationConfigApplicationContext └─ WebApplicationContext
控制反转(IoC, Inversion of Control)
对象不再自己创建依赖,而是由外部容器把依赖"塞"进来。
plain
┌─────────────────────────────────────────┐
│ ApplicationContext extends BeanFactory │
│ │
├─────────────────────────────────────────┤
│ • 国际化支持 (MessageSource) │
│ • 事件传播 (ApplicationEventPublisher) │
│ • 资源加载 (ResourceLoader) │
│ • 自动装配 (AutowireCapable) │
└─────────────────────────────────────────┘
↑
┌─────────────────────────────────────────┐
│ BeanFactory │
│ (基础容器,核心 IoC 功能) │
├─────────────────────────────────────────┤
│ • Bean 定义管理 (BeanDefinition) │
│ • Bean 实例化 & 生命周期管理 │
│ • 依赖解析 & 注入 │
└─────────────────────────────────────────┘| 实现类 | 使用场景 |
|---|---|
ClassPathXmlApplicationContext | XML 配置(传统) |
AnnotationConfigApplicationContext | 注解/Java Config(主流) |
WebApplicationContext | Web 应用环境 |
依赖注入(DI, Dependency Injection)
- 构造器注入(推荐)
- Setter 注入
- 字段注入(常见,但不推荐)
FactoryBean 与 BeanFactory
| 特性 | BeanFactory | FactoryBean |
|---|---|---|
| 本质 | Spring 的核心容器接口 | 一种特殊的 Bean |
| 职责 | 负责管理和创建所有 Bean | 负责自定义创建特定 Bean 的过程 |
| 地位 | 基础设施,IoC 容器的基础 | 扩展点,允许开发者介入 Bean 创建 |
| 获取方式 | applicationContext.getBean() | 作为普通 Bean 注入使用 |
Bean
懒加载
单实例Bean会在容器启动时立即实例化并加载到Spring容器中。然而,有时我们希望延迟加载某些Bean,直到它们第一次被调用时才进行实例化。为此,Spring提供了懒加载机制。
@Lazy、XML 中lazy-init="true
Bean 作用域
| 作用域 | 说明 | 使用场景 |
|---|---|---|
| singleton(默认) | 每个 Spring 容器只有一个实例 | 无状态服务、配置类 |
| prototype | 每次获取都创建新实例 | 有状态对象 |
| request | 每个 HTTP 请求一个实例 | Web 应用 |
| session | 每个 HTTP Session 一个实例 | 用户会话数据 |
| application | ServletContext 生命周期 | 全局应用数据 |
FactoryBean
定义:一个接口,当 Bean 实现了该接口时,Spring 容器不会直接返回该 Bean 实例,而是返回其 getObject() 方法创建的对象。
java
public interface FactoryBean<T> {
T getObject() throws Exception; // 返回实际创建的 Bean
Class<?> getObjectType(); // 返回创建 Bean 的类型
boolean isSingleton(); // 是否为单例
}
// 例如, Mybatis MybatisSqlSessionFactoryBean
public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
this.afterPropertiesSet();
}
return this.sqlSessionFactory;
}
}Bean 生命周期
plain
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 加载配置 │ ──→ │ 解析 Bean │ ──→ │ 注册 Bean │
│ (XML/注解) │ │ 定义信息 │ │ Definition │
└─────────────┘ └─────────────┘ └──────┬──────┘
↓
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 放入容器 │ ←── │ 初始化回调 │ ←── │ 属性注入 │
│ (单例池) │ │ (生命周期) │ │ (DI 完成) │
└─────────────┘ └─────────────┘ └─────────────┘
1. 实例化(Instantiation)
↓ 调用构造器
2. 属性赋值(Populate)
↓ 依赖注入
3. 初始化(Initialization)
↓ 执行初始化方法
├─ @PostConstruct
├─ InitializingBean.afterPropertiesSet()
└─ 自定义 init-method
4. Bean 就绪,放入单例池
↓ 使用
5. 销毁(Destruction)
↓ 容器关闭时
├─ @PreDestroy
├─ DisposableBean.destroy()
└─ 自定义 destroy-method示例
java
@Component
public class MyBean implements InitializingBean, DisposableBean {
@PostConstruct
public void postConstruct() {
System.out.println("1. @PostConstruct");
}
@Override
public void afterPropertiesSet() {
System.out.println("2. InitializingBean");
}
public void customInit() {
System.out.println("3. 自定义 init");
}
// ========== 销毁阶段 ==========
@PreDestroy
public void preDestroy() {
System.out.println("1. @PreDestroy");
}
@Override
public void destroy() {
System.out.println("2. DisposableBean");
}
public void customDestroy() {
System.out.println("3. 自定义 destroy");
}
}循环依赖解决
构造器注入的循环依赖无法解决(因为实例化阶段就需要依赖)。
| 缓存级别 | 名称 | 存储内容 |
|---|---|---|
| 一级 | singletonObjects | 完全初始化好的 Bean |
| 二级 | earlySingletonObjects | 提前暴露的 Bean(未填充属性) |
| 三级 | singletonFactories | Bean 工厂(用于创建代理) |
plain
// A → B → A 的循环依赖场景
A 实例化 → 放入三级缓存 → A 属性注入发现需要 B
↓
B 实例化 → B 属性注入发现需要 A → 从三级缓存取 A 的工厂
↓
B 初始化完成 → A 继续初始化Bean Proxy
plain
┌─────────────────────────────────────────────────────────────┐
│ Bean 创建 & 代理流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 扫描 @Component → BeanDefinition │
│ ↓ │
│ 2. 实例化 Bean(反射调用构造器) │
│ UserService userService = new UserService(); │
│ ↓ │
│ 3. 依赖注入(populateBean) │
│ userService.dao = @Autowired 的 UserDao │
│ ↓ │
│ 4. 初始化(initializeBean) │
│ ├─ @PostConstruct │
│ ├─ afterPropertiesSet() │
│ └─ init-method │
│ ↓ │
│ 5. 【AOP 代理创建】BeanPostProcessor │
│ AbstractAutoProxyCreator.postProcessAfterInitialization() │
│ ↓ │
│ 5.1 查找匹配的 Advisor(@Transactional, @Aspect等) │
│ ↓ │
│ 5.2 创建 ProxyFactory │
│ ↓ │
│ 5.3 选择代理类型 │
│ ├─ 有接口 → JdkDynamicAopProxy │
│ └─ 无接口 → ObjenesisCglibAopProxy │
│ ↓ │
│ 5.4 生成代理类字节码 │
│ ├─ JDK: ProxyGenerator.generateProxyClass() │
│ └─ CGLIB: Enhancer.create() → ASM 生成子类 │
│ ↓ │
│ 6. 将代理对象放入单例池(singletonObjects) │
│ 注意:此时容器中存的是代理,不是原始 Bean! │
│ ↓ │
│ 7. @Autowired 注入到其他 Bean 的是代理对象 │
│ │
└─────────────────────────────────────────────────────────────┘配置方式演进
XML 配置(Spring 1.x/2.x)
xml
<beans>
<bean id="userService" class="com.example.UserService">
<constructor-arg ref="userRepository"/>
</bean>
<bean id="userRepository" class="com.example.UserRepository"/>
</beans>注解配置(Spring 2.5+)
java
@Configuration
@ComponentScan("com.example")
public class AppConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
}Spring Boot 自动配置(现代)
java
@SpringBootApplication // 包含 @ComponentScan + @EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}面向方面编程(AOP, Aspect-Oriented Programming)
AbstractAutoProxyCreator(AOP 核心类)
plain
┌─────────────────────────────────────────────────────────┐
│ AOP 核心架构 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ @Aspect 切面 │───→│ Advisor │ │
│ │ (开发者定义) │ │ (Spring 内部) │ │
│ └─────────────────┘ └────────┬────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────────┐ │
│ │ PointcutAdvisor │ │
│ │ ├─ pointcut │ │
│ │ └─ advice │ │
│ └────────┬────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ProxyFactory(创建代理) │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ JdkDynamicAopProxy│ or │ CglibAopProxy │ │ │
│ │ │ (基于接口) │ │ (基于继承) │ │ │
│ │ └─────────────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────────┐ │
│ │ 代理对象 │ │
│ │ (客户端使用) │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘Spring AOP 核心流程
plain
┌─────────────────────────────────────────────────────────────┐
│ Spring AOP 完整流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 解析 @Aspect 切面类 │
│ ↓ AnnotationAwareAspectJAutoProxyCreator │
│ │
│ 2. 提取 @Pointcut 切点表达式 │
│ ↓ AspectJExpressionPointcut │
│ │
│ 3. 提取 @Before/@Around 等通知方法 │
│ ↓ 封装为 Advisor(Pointcut + Advice) │
│ │
│ 4. Bean 实例化后,匹配适用的 Advisor │
│ ↓ AbstractAutoProxyCreator.postProcessAfterInitialization│
│ │
│ 5. 创建代理对象 │
│ ├─ 有接口 → JDK 动态代理(JdkDynamicAopProxy) │
│ └─ 无接口 → CGLIB 代理(CglibAopProxy) │
│ │
│ 6. 代理对象方法调用 │
│ ↓ ReflectiveMethodInvocation.proceed() │
│ ↓ 递归执行拦截器链(MethodInterceptor Chain) │
│ ↓ 最终调用目标方法 │
│ │
│ 7. 返回结果 │
│ │
└─────────────────────────────────────────────────────────────┘Spring Aop 无法拦截的场景
java
// ❌ 无法拦截以下场景(因为基于代理)
public class UserService {
// 1. 同类内部调用(this 不是代理)
public void methodA() {
this.methodB(); // @Transactional 失效!
}
@Transactional
public void methodB() {}
// 2. 非 public 方法(CGLIB 可以,但默认不代理)
@Transactional
private void privateMethod() {} // 无法拦截
// 3. 静态方法
@Transactional
public static void staticMethod() {} // 无法拦截
// 4. final 方法(CGLIB 无法重写)
@Transactional
public final void finalMethod() {} // 无法拦截
}内部调用失效原理
plain
┌─────────────────────────────────────────────────────────────┐
│ 同类内部调用失效原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 根本原因: │
│ this 指向的是原始目标对象(Target), │
│ 而不是 Spring 容器中的代理对象(Proxy)。 │
│ │
│ ┌─────────┐ 注入 ┌─────────┐ │
│ │ 客户端 │ ───────────────→ │ 代理对象 │ │
│ └─────────┘ │ Proxy │ │
│ └────┬────┘ │
│ │ 调用 target │
│ ┌────┴────┐ │
│ │ 原始对象 │ │
│ │ Target │ │
│ │ │ │
│ │ this. │ ──→ 直接调用, │
│ │ method()│ 不走代理! │
│ └─────────┘ │
│ │
│ 解决方案优先级: │
│ 1. 拆分到不同类(最佳实践) │
│ 2. 注入自身代理(简单快速) │
│ 3. 使用 AopContext(需要 exposeProxy=true) │
│ │
└─────────────────────────────────────────────────────────────┘示例
启动 AOP
java
@Configuration
@EnableAspectJAutoProxy // 开启 AOP 自动代理
public class AopConfig {
}@PointCut 使用
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
| 表达式 | 含义 |
|---|---|
execution(* *(..)) | 拦截所有方法 |
execution(public * *(..)) | 所有 public 方法 |
execution(* set*(..)) | 所有 set 开头的方法 |
execution(* com.example.service.*.*(..)) | service 包下所有类的所有方法 |
execution(* com.example.service..*.*(..)) | service 包及其子包下所有方法 |
execution(* com.example.service.UserService.*(..)) | UserService 的所有方法 |
execution(String com.example.service.UserService.save*(..)) | 返回 String 且 save 开头 |
execution(* save*(String, ..)) | 第一个参数是 String 的 save 方法 |
| org.aspectj.lang.annotation.@Aspect 自定义切面编程 |
java
@Aspect // 声明为切面
@Component // 交给 Spring 管理
public class LoggingAspect {
// ========== 切点定义(复用)==========
// 拦截 UserService 的所有方法
@Pointcut("execution(* com.example.service.UserService.*(..))")
public void userServiceMethods() {}
// 拦截所有 @Transactional 方法
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}
// 组合切点:UserService 中有事务注解的方法
@Pointcut("userServiceMethods() && transactionalMethods()")
public void userServiceTxMethods() {}
// ========== 通知实现 ==========
// 前置通知
@Before("userServiceMethods()")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
log.info("[BEFORE] 方法 {} 被调用,参数:{}", methodName, Arrays.toString(args));
}
// 环绕通知(最强大,可控制目标方法执行)
@Around("transactionalMethods()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
String methodName = joinPoint.getSignature().getName();
try {
log.info("[AROUND-BEFORE] 开始执行 {}", methodName);
// 执行目标方法(关键!不调用则目标方法不会执行)
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - start;
log.info("[AROUND-AFTER] {} 执行成功,耗时 {}ms,返回值:{}",
methodName, duration, result);
return result;
} catch (Exception e) {
log.error("[AROUND-EXCEPTION] {} 执行失败: {}", methodName, e.getMessage());
throw e; // 必须抛出,否则异常被吞掉
}
}
// 返回后通知
@AfterReturning(pointcut = "userServiceMethods()", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
log.info("[AFTER-RETURNING] 方法 {} 返回:{}",
joinPoint.getSignature().getName(), result);
}
// 异常通知
@AfterThrowing(pointcut = "userServiceMethods()", throwing = "ex")
public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
log.error("[AFTER-THROWING] 方法 {} 抛出异常:{}",
joinPoint.getSignature().getName(), ex.getMessage());
}
// 最终通知(类似 finally)
@After("userServiceMethods()")
public void logAfter(JoinPoint joinPoint) {
log.info("[AFTER] 方法 {} 执行结束", joinPoint.getSignature().getName());
}
}自定义-AOP 访问限流
java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
int maxRequests() default 100; // 最大请求数
int timeWindow() default 60; // 时间窗口(秒)
String key() default ""; // 限流 key(支持 SpEL)
}
@Aspect
@Component
public class RateLimitAspect {
@Autowired
private StringRedisTemplate redisTemplate;
@Around("@annotation(rateLimit)")
public Object around(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {
// 1. 获取限流 key
String key = generateKey(point, rateLimit);
// 2. Redis 滑动窗口限流
Long current = redisTemplate.opsForZSet().size(key);
if (current != null && current >= rateLimit.maxRequests()) {
throw new RateLimitException("请求过于频繁,请稍后再试");
}
// 3. 记录本次请求
long now = System.currentTimeMillis();
redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), now);
redisTemplate.expire(key, rateLimit.timeWindow(), TimeUnit.SECONDS);
// 4. 清理过期数据(滑动窗口)
redisTemplate.opsForZSet().removeRangeByScore(key, 0, now - rateLimit.timeWindow() * 1000);
// 5. 执行目标方法
return point.proceed();
}
private String generateKey(ProceedingJoinPoint point, RateLimit rateLimit) {
// 实现 SpEL 解析,生成动态 key
String methodName = point.getSignature().getName();
return "rate_limit:" + methodName;
}
}
@RestController
public class ApiController {
@RateLimit(maxRequests = 10, timeWindow = 60) // 每分钟最多10次
@GetMapping("/api/sensitive")
public String sensitiveApi() {
return "敏感数据";
}
}