easy网盘补充学习——Spring Aop
观看视频:Spring_AOP(上)_哔哩哔哩_bilibili
Spring Aop入门
✅ 1. 什么是 AOP?
- 全称:Aspect Oriented Programming → 面向切面编程
- 定位:Spring 框架两大核心特性之一(另一个是 IOC/DI)
- 关系:不是取代 OOP,而是补充 OOP。
- OOP 处理“业务主体逻辑”(如用户注册、下单)
- AOP 处理“横切关注点”(如日志、事务、安全校验)
🎭 “星爷+达叔”比喻非常形象:
- 星爷 = OOP(主角,负责主线剧情)
- 达叔 = AOP(配角,关键时刻出场帮忙,不抢戏)
❓ 2. 为什么需要 AOP?—— OOP 的局限性
传统 OOP 通过封装、继承、多态解决代码复用和模块化,但遇到以下场景会显得力不从心:
- 重复代码泛滥:每个方法都要写日志、开事务、判权限…
- 耦合度高:业务逻辑和非业务逻辑混在一起,难以维护
- 违反单一职责原则:一个方法既做业务又做日志还做事务
👉 AOP 就是为了解决这些“横切关注点”的分散问题而生!
🛠️ 3. AOP 要解决的问题
3.1 分工明确
- OOP → 主线任务(业务逻辑)
- AOP → 打辅助(通用功能)
3.2 配角的要求(AOP 的设计哲学)
- 不能抢戏 → 不影响主业务流程
- 招之即来,挥之即去 → 灵活配置,按需启用/关闭
- 实现机制:文本
weaving (织入) ↓ 动态代理 ↓ JDK 动态代理 / CGLIB
⚙️ Weaving(织入):将切面代码“插入”到目标对象的过程。
- 编译期织入(AspectJ)
- 运行期织入(Spring AOP 默认方式,基于动态代理)
3.3 典型应用场景
- ✅ 日志记录:自动记录方法调用时间、参数、返回值
- ✅ 事务管理:
@Transactional注解背后就是 AOP - ✅ 权限控制:拦截请求,检查用户是否有访问权限
- ✅ 性能监控、缓存、异常统一处理等
🧩 4. AOP 的核心概念(必须掌握!)
| 术语 | 英文 | 说明 |
|---|---|---|
| 关注点 | Concern | 程序中某个特定的功能模块,比如“日志”、“事务”。 |
| 横切关注点 | Cross-cutting Concern | 跨越多个模块的关注点(如所有 Service 层方法都需要日志)。 |
| 连接点 | Join Point | 程序执行过程中的任意一点(如方法调用、异常抛出),Spring 只支持方法级别的连接点。 |
| 切入点 | Pointcut | 【重点】对连接点的定义,即“哪些方法需要被增强”。用表达式匹配。 |
| 通知/增强 | Advice | 在切入点处执行的额外逻辑(如打印日志)。 |
| 通知类型 | Advice Type | - @Before:前置通知- @After:后置通知- @Around:环绕通知(最强大)- @AfterReturning:返回后通知- @AfterThrowing:异常后通知 |
| 切面 | Aspect | 【核心】= 切入点 + 通知。是一个类,包含“在哪里增强”和“增强什么”。 |
📌 图中公式:切面(类) = 切入点 + 通知 —— 这是理解 AOP 的关键!
关于切入点和连接点
1. 核心区别一句话总结
- 连接点 (JoinPoint,候选切入点):是“所有可能”被拦截的地方(客观存在的集合)。
- 比如:餐厅里每一道菜的制作过程(炒青菜、炖肉、煮汤...)都是连接点。
- 切入点 (Pointcut):是“实际选中”要拦截的地方(主观定义的规则/子集)。
- 比如:老板规定“只检查所有肉类菜肴的制作过程”。这个规则选中的那些菜,就是切入点。
公式:切入点 = 连接点的一个子集
2. 详细对比表
| 特性 | 连接点 (JoinPoint) | 切入点 (Pointcut) |
|---|---|---|
| 定义 | 程序执行过程中具体的某个点(在 Spring 中特指方法执行)。 | 匹配连接点的表达式或规则。 |
| 性质 | 客观存在的。只要方法被调用,它就是一个连接点。 | 主观定义的。是你写代码时指定要去拦截哪些连接点。 |
| 数量 | 程序中可能有成千上万个连接点(所有方法)。 | 通常只有几个切入点(你关心的那几个业务)。 |
| 代码体现 | 在通知方法(Advice)中作为参数 JoinPoint joinPoint 出现。 | 在注解 @Pointcut 或 @Before("expression") 的字符串中体现。 |
| 能否获取信息 | 能。可以通过它获取方法名、参数、返回值等。 | 不能。它只是一个匹配规则(字符串表达式),本身不包含运行时数据。 |
Spring Aop经典实现
@Aspect
@Component
public class MyAspect {
//切入点
@Pointcut("execution(* com.fast.springaop.Service.*.*(..) )")
public void myPointCut1() {
//切入点表达式: execution(* com.fast.springaop.Service.*.*(..) )
//含义:com.fast.springaop.Service包下的所有方法(不区分方法参数的)
//execution(* com.fast.springaop.Service.*.*(String,int) )
//含义:com.fast.springaop.Service包下的所有方法(参数为String,int)
}
//通知(环绕)
@Around("myPointCut1()")
public Object recordLogs(ProceedingJoinPoint joinPoint) throws Throwable {
//oop辅助任务
System.out.println("(前)oop辅助任务");
//oop执行主线任务
Object obj = joinPoint.proceed();
//oop辅助任务
System.out.println("(后)oop辅助任务");
//返回oop执行主线任务后的结果
return obj;
}
}
如果想排除某个方法
@Aspect
@Component
public class MyAspect {
//切入点
@Pointcut("execution(* com.fast.springaop.Service.*.*(..) )")
public void myPointCut1() {
//切入点表达式: execution(* com.fast.springaop.Service.*.*(..) )
//含义:com.fast.springaop.Service包下的所有方法(不区分方法参数的)
//execution(* com.fast.springaop.Service.*.*(String,int) )
//含义:com.fast.springaop.Service包下的所有方法(参数为String,int)
}
//切入点2
@Pointcut("execution(* com.fast.springaop.Service.*.getUserList(..) )")//
public void myPointCut2() {
//切入点表达式: execution(* com.fast.springaop.Service.*.*(..) )
//含义:com.fast.springaop.Service包下的所有方法(不区分方法参数的))
}
//通知(环绕)
@Around("myPointCut1() && !myPointCut2()")//切入点 符合myPointCut1()的方法,除了getUserList
public Object recordLogs(ProceedingJoinPoint joinPoint) throws Throwable {
//oop辅助任务
System.out.println("(前)oop辅助任务");
//oop执行主线任务
Object obj = joinPoint.proceed();
//oop辅助任务
System.out.println("(后)oop辅助任务");
//返回oop执行主线任务后的结果
return obj;
}
}
可以自定义注解进行切入
它允许你通过简单的标记来触发切面逻辑,而不需要写复杂的正则表达式(Pointcut Expression)去匹配方法名。
第一步:定义自定义注解
首先,我们需要创建一个注解。这个注解本身没有任何逻辑,它只是一个“标记”。
文件路径: src/main/java/com/fast/springaop/annotation/MyAnnotaion.java
package com.fast.springaop.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 1. @Target: 指定注解可以用在哪里
// METHOD 表示只能加在方法上
@Target(ElementType.METHOD)
// 2. @Retention: 指定注解的生命周期
// RUNTIME 表示注解在运行时依然存在,这样 AOP 才能通过反射读取到它
// 如果选 CLASS,编译后就没用了,AOP 就失效了
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotaion {
// 可以定义一些属性,比如备注信息
String value() default "";
}
//切入点3
@Pointcut("@annotation(com.fast.springaop.annotaion.MyAnnotaion)")
public void myPointCut3() {
//切入点表达式: execution(* com.fast.springaop.Service.*.*(..) )
//含义:com.fast.springaop.Service包下的所有方法(不区分方法参数的))
}
//通知(环绕)
@Around("myPointCut3()")
public Object recordLogs(ProceedingJoinPoint joinPoint) throws Throwable {
//oop辅助任务
System.out.println("(前)oop辅助任务");
//oop执行主线任务
Object obj = joinPoint.proceed();
//oop辅助任务
System.out.println("(后)oop辅助任务");
//返回oop执行主线任务后的结果
return obj;
}
在想要切入的地方加上注解
package com.fast.springaop.Service.impl;
import com.fast.springaop.Service.UserService;
import com.fast.springaop.annotaion.MyAnnotaion;
import com.fast.springaop.dao.UserDao;
import com.fast.springaop.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void getUserList() {
userDao.selectUser();
}
@Override
@MyAnnotaion("保存用户")
public void saveUser() {
userDao.insertUser();
}
@Override
public void updateUser() {
userDao.updateUser();
}
@Override
@MyAnnotaion("删除用户")
public void removeUser() {
userDao.deleteUser();
}
@Override
@MyAnnotaion("修改用户")
public void modifyUser() {
userDao.updateUser();
}
}
SpringAOP——静态代理

【Spring中的代理模式】
1.为什么需要代理
生活中的代理场景
演员 -> 经纪 -> 商演
网民 -> 代购 -> 商家
租客 -> 中介 -> 房东
搜房源 -> 分析对比 -> 联系房东 中介(代理)
看房 -> 签订合同 -> 支付租金 -> 入住 租客
扩展:房源
解除了房源带来的限制:解耦
2.什么是代理模式
使用【代理类对象】去扩展【原始类对象】的功能,而又无需对原始类进行修改的一种设计模式;
使用代理模式,对原始类对象的访问转变为对代理对象的访问,并且由代理对象把返回值给调用者;
代理模式分静态代理、动态代理两种。
3.静态代理的实现
3.1 实现手段
1)代理类、原始类实现相同接口
由中介去搜房源 -> 分析对比 -> 联系房东 ->
自己做看房 -> 签订合同 -> 支付租金 -> 入住
public class HouseAgent implements Rental {
/**
* 中介类(代理类)
*/
//who
private Tenant tenant;
public HouseAgent(Tenant tenant) {
this.tenant = tenant;
}
@Override
public void renting() {
System.out.println("搜房源 -> 分析对比 -> 联系房东 -> ");
//what
tenant.renting();
}
}
public interface Rental {
public void renting();
}
/**
* 租客类(原始类)
*/
public class Tenant implements Rental{
@Override
public void renting() {
System.out.println(" 看房 -> 签订合同 -> 支付租金 -> 入住");
}
}
@SpringBootTest
public class apptest {
@Test
public void testStaticProxyByInterface() {
//租客对象
Tenant tenant = new Tenant();
//中介对象(代理对象)
HouseAgent agent = new HouseAgent(tenant);
//调用(测试租房)
agent.renting();
}
}
2)代理类 extends 原始类
public class TenantChild extends Tenant{
//who -> 为谁代理(父类)
//what -> 方法继写
@Override
public void renting() {
System.out.print("搜房源 -> 分析对比 -> 联系房东 -> ");
super.renting();//what
}
}
@Test
public void testStaticProxyByExtends() {
//租客对象
TenantChild tenantChild = new TenantChild();
//调用(测试租房)
tenantChild.renting();
}
3.2 静态代理的局限
- 手动创建代理类 2) 类型单一 3) 代理类分散, 维护成本高(1个原始类 VS 1个代理类)
SpringAOP——动态代理

1.动态代理的实现
1.1 实现手段
- 代理类、原始类实现相同接口
- 代理类 extends 原始类
1.2 动态代理的优势
- 运行时动态生成代理类 2) 支持为所有类型做代理 3) 维护成本低
2.SpringAOP与动态代理
2.1 Spring和SpringBoot在动态代理实现上面的区别
Spring: 原始类实现了接口则jdk, 否则cglib
SpringBoot: 默认jdk -> v2.0 -> 默认cglib
/**
* 代理工厂类
*/
public class ProxyFactory {
//who
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//how
public Object jdkProxy(){
//类加载器
ClassLoader loader = this.getClass().getClassLoader();
//原始对象所实现的所有的接口数组
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler h = (Object proxy, Method method, Object[] args)->{
System.out.print("搜房源 -> 分析对比 -> 联系房东 -> ");//增强
Object invoked = method.invoke(target, args);
return invoked;
};
return Proxy.newProxyInstance(loader,interfaces,h);
}
public Object cglibProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.print("搜房源 -> 分析对比 -> 联系房东 -> ");//增强
Object invoked = method.invoke(target, args);
return invoked;
}
});
return enhancer.create();
}
}
@Test
public void testDynamicProxyByExtends() {
//租客(原始对象)
Tenant tenant = new Tenant();
//代理工厂
ProxyFactory factory = new ProxyFactory(tenant);
//代理对象
//Rental obj = (Rental) factory.jdkProxy();
Rental rental = (Rental) factory.cglibProxy();
//测试(租房)
rental.renting();
}
更多推荐


所有评论(0)