文章标签: #java #spring #aop #代理模式 #切面编程 #源码分析 #设计模式 #面试 #深度解析
- 引言:为什么需要AOP
- 理论基础:横切关注点的本质
- 来龙去脉:AOP技术的发展史
- AOP核心概念详解
- Spring AOP架构与源码深度分析
- JDK动态代理源码深度分析
- CGLIB代理源码深度分析
- 切面织入时机与源码分析
- 设计模式解析
- 实战案例:工业级AOP应用
- 对比分析:Spring AOP vs 替代方案
- 性能分析与优化
- 常见陷阱与最佳实践
- 面试题与参考答案
AOP(Aspect Oriented Programming,面向切面编程)不是简单的"代码拦截器",而是一种分离横切关注点的架构思想。在传统的OOP编程中,日志、事务、权限等横切关注点散落在业务代码各处,导致代码重复、难以维护。
核心认知:
传统OOP的问题:
┌─────────────────────────────────────────┐
│ UserService │
│ ├─ log.info("开始...") │
│ ├─ 事务开始 │
│ ├─ 业务逻辑 │
│ ├─ 事务提交 │
│ ├─ log.info("结束...") │
│ └─ 权限检查 │
├─────────────────────────────────────────┤
│ OrderService │
│ ├─ log.info("开始...") ← 重复代码 │
│ ├─ 事务开始 ← 重复代码 │
│ ├─ 业务逻辑 │
│ ├─ 事务提交 ← 重复代码 │
│ ├─ log.info("结束...") ← 重复代码 │
│ └─ 权限检查 ← 重复代码 │
└─────────────────────────────────────────┘
问题:
- 代码重复(DRY原则被破坏)
- 业务逻辑与非业务逻辑混杂
- 修改横切逻辑需要修改多处
AOP的解决方式:
┌─────────────────────────────────────────┐
│ LoggingAspect(日志切面) │
│ TransactionAspect(事务切面) │
│ SecurityAspect(安全切面) │
└─────────────────────────────────────────┘
↓ 织入(Weaving)
┌─────────────────────────────────────────┐
│ UserService OrderService │
│ ├─ 业务逻辑 ├─ 业务逻辑 │
│ └─ 业务逻辑 └─ 业务逻辑 │
└─────────────────────────────────────────┘
优势:
- 横切关注点集中管理
- 业务代码纯净
- 易于维护和修改
关键洞察:AOP的本质是关注点分离(Separation of Concerns),通过代理模式在不修改原始代码的情况下,动态添加横切功能。
横切关注点(Cross-cutting Concerns)是指分散在多个模块中、与核心业务逻辑无关但又必须存在的功能:
横切关注点的分类:
1. 基础设施类
- 日志记录(Logging)
- 性能监控(Performance Monitoring)
- 异常处理(Exception Handling)
2. 安全类
- 身份认证(Authentication)
- 权限控制(Authorization)
- 数据校验(Validation)
3. 事务类
- 事务管理(Transaction Management)
- 数据一致性(Data Consistency)
4. 缓存类
- 结果缓存(Caching)
- 缓存失效(Cache Eviction)
5. 通信类
- 消息发送(Messaging)
- 事件发布(Event Publishing)
AOP的实现基于代理模式和字节码操作:
AOP的核心机制:
┌─────────────────────────────────────────┐
│ 目标对象(Target) │
│ ├─ methodA() │
│ ├─ methodB() │
│ └─ methodC() │
└─────────────────────────────────────────┘
↓ 代理包装
┌─────────────────────────────────────────┐
│ 代理对象(Proxy) │
│ ├─ methodA() │
│ │ ├─ 前置通知(Before Advice) │
│ │ ├─ 目标对象.methodA() │
│ │ └─ 后置通知(After Advice) │
│ ├─ methodB() │
│ │ ├─ 环绕通知(Around Advice) │
│ │ │ ├─ 前置逻辑 │
│ │ │ ├─ 目标对象.methodB() │
│ │ │ └─ 后置逻辑 │
│ │ └─ 返回通知(AfterReturning) │
│ └─ methodC() │
│ └─ 异常通知(AfterThrowing) │
└─────────────────────────────────────────┘
织入时机对比:
1. 编译时织入(Compile-time Weaving)
- 工具:AspectJ编译器(ajc)
- 原理:在编译阶段修改源代码或字节码
- 优点:运行时无性能开销
- 缺点:需要特殊编译器
2. 加载时织入(Load-time Weaving)
- 工具:Java Agent + AspectJ
- 原理:在类加载时修改字节码
- 优点:无需修改编译过程
- 缺点:类加载时有性能开销
3. 运行时织入(Runtime Weaving)
- 工具:Spring AOP
- 原理:通过代理模式在运行时创建代理对象
- 优点:简单、灵活
- 缺点:有代理调用开销
Gregor Kiczales等人在Xerox PARC提出AOP概念,开发了AspectJ和AspectC++。
特点:
- 全新的编程范式
- 需要专门的编译器
- 学习曲线陡峭
AspectJ成为Java AOP的事实标准:
// AspectJ语法
public aspect LoggingAspect {
pointcut publicMethod(): execution(public * *.*(..));
before(): publicMethod() {
System.out.println("方法执行前: " + thisJoinPoint.getSignature());
}
}Spring 1.0引入基于代理的AOP:
<!-- Spring 1.x AOP配置 -->
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:before method="logBefore" pointcut="execution(* com.example.service.*.*(..))"/>
</aop:aspect>
</aop:config>特点:
- 简化AOP使用
- 与Spring IOC集成
- 仅支持方法级别拦截
Spring 2.0引入AspectJ注解支持:
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint jp) {
System.out.println("方法执行前: " + jp.getSignature().getName());
}
}Spring Boot时代,AOP配置进一步简化:
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}| 概念 | 说明 |
|---|---|
| Aspect(切面) | 横切关注点的模块化,包含通知和切点 |
| Join Point(连接点) | 程序执行过程中的点,如方法调用、异常抛出 |
| Pointcut(切点) | 匹配连接点的表达式 |
| Advice(通知) | 切面的具体动作,如何增强 |
| Target(目标对象) | 被代理的原始对象 |
| Proxy(代理) | AOP框架创建的对象 |
| Weaving(织入) | 将切面应用到目标对象 |
@Aspect
@Component
public class LogAspect {
// 前置通知:方法执行前
@Before("execution(* com.example.service.*.*(..))")
public void before(JoinPoint jp) {
System.out.println("方法执行前: " + jp.getSignature().getName());
}
// 后置通知:方法执行后(无论是否异常)
@After("execution(* com.example.service.*.*(..))")
public void after() {
System.out.println("方法执行后");
}
// 返回通知:方法正常返回后
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void afterReturning(Object result) {
System.out.println("方法返回: " + result);
}
// 异常通知:方法抛出异常后
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
public void afterThrowing(Exception ex) {
System.out.println("方法异常: " + ex.getMessage());
}
// 环绕通知:包围方法执行
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前");
Object result = pjp.proceed(); // 执行目标方法
System.out.println("环绕后");
return result;
}
}单个切面的执行顺序:
@Around(前半部分)
→ @Before
→ 目标方法
→ @AfterReturning / @AfterThrowing
→ @After
→ @Around(后半部分)
多个切面的执行顺序(@Order控制):
Aspect1 @Order(1) Aspect2 @Order(2)
@Around(前半) @Around(前半)
@Before @Before
目标方法
@After @After
@Around(后半) @Around(后半)
注意:
- 数值越小优先级越高
- 优先级高的切面的@Before先执行
- 优先级高的切面的@After后执行(类似嵌套)
┌─────────────────────────────────────────────────────────────┐
│ Spring AOP 代理架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 调用方 │
│ │ │
│ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Proxy代理 │───►│ Advisor链 │───►│ Target目标 │ │
│ │ (JDK/CGLIB) │ │ (拦截器链) │ │ (原始Bean) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ Proxy = Target + Advice(s) │
│ │
│ 代理创建流程: │
│ 1. AbstractAutoProxyCreator.postProcessAfterInitialization │
│ 2. 检查是否有匹配的Advisor │
│ 3. 创建ProxyFactory │
│ 4. 选择代理策略(JDK/CGLIB) │
│ 5. 生成代理对象 │
│ │
└─────────────────────────────────────────────────────────────┘
Spring AOP基于代理模式实现:
- JDK动态代理:目标类实现了接口
- CGLIB代理:目标类没有实现接口
默认策略:
- 目标类实现了接口 → JDK代理
- 目标类没有实现接口 → CGLIB代理
Spring Boot 2.x+默认全部使用CGLIB(proxyTargetClass=true)。
// java.lang.reflect.Proxy
public class Proxy implements java.io.Serializable {
// 生成代理类的核心方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
// 1. 获取或生成代理类
Class<?> cl = getProxyClass0(loader, intfs);
// 2. 获取构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
// 3. 创建实例
return cons.newInstance(new Object[]{h});
}
}public class JdkProxyDemo {
public interface UserService {
void saveUser(String name);
String getUser(Long id);
}
public static class UserServiceImpl implements UserService {
@Override
public void saveUser(String name) {
System.out.println("保存用户: " + name);
}
@Override
public String getUser(Long id) {
return "User-" + id;
}
}
public static class LogInvocationHandler implements InvocationHandler {
private Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[日志] 开始调用: " + method.getName());
System.out.println("[日志] 参数: " + Arrays.toString(args));
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long cost = System.currentTimeMillis() - start;
System.out.println("[日志] 调用完成: " + method.getName() + ", 耗时: " + cost + "ms, 返回值: " + result);
return result;
}
}
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LogInvocationHandler(target)
);
proxy.saveUser("张三");
String user = proxy.getUser(1L);
System.out.println("返回结果: " + user);
// 输出代理类名
System.out.println("代理类: " + proxy.getClass().getName());
}
}// JdkDynamicAopProxy - Spring JDK代理的核心实现
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
private final AdvisedSupport advised;
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
this.advised = config;
}
@Override
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
// 获取代理要实现的接口
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
// 生成代理对象
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
// 1. 处理equals/hashCode/toString等特殊方法
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
return equals(args[0]);
}
// 2. 获取目标对象
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 3. 获取匹配的拦截器链(Advisor链)
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(
method, targetClass);
// 4. 如果没有拦截器,直接调用目标方法
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
} else {
// 5. 创建MethodInvocation,执行拦截器链
invocation = new ReflectiveMethodInvocation(proxy, target, method, args,
targetClass, chain);
// 执行拦截器链
retVal = invocation.proceed();
}
return retVal;
} finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
}
}
}CGLIB(Code Generation Library)通过生成目标类的子类来创建代理,可以代理没有实现接口的类。
// CglibAopProxy - Spring CGLIB代理的核心实现
public class CglibAopProxy implements AopProxy, Serializable {
protected final AdvisedSupport advised;
public CglibAopProxy(AdvisedSupport config) throws AopConfigException {
this.advised = config;
}
@Override
public Object getProxy() {
return getProxy(null);
}
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
// 创建Enhancer
Enhancer enhancer = createEnhancer();
// 设置父类
Class<?> rootClass = this.advised.getTargetClass();
enhancer.setSuperclass(rootClass);
// 设置接口
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
// 设置回调
Callback[] callbacks = getCallbacks(rootClass);
enhancer.setCallbacks(callbacks);
// 生成代理类并创建实例
return createProxyClassAndInstance(enhancer, callbacks);
}
}public class CglibProxyDemo {
public static class OrderService {
public void createOrder(String orderId) {
System.out.println("创建订单: " + orderId);
}
public String getOrder(String orderId) {
return "Order-" + orderId;
}
}
public static class LogMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("[日志] 开始: " + method.getName());
System.out.println("[日志] 参数: " + Arrays.toString(args));
long start = System.currentTimeMillis();
Object result = proxy.invokeSuper(obj, args);
long cost = System.currentTimeMillis() - start;
System.out.println("[日志] 结束: " + method.getName() + ", 耗时: " + cost + "ms, 结果: " + result);
return result;
}
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class);
enhancer.setCallback(new LogMethodInterceptor());
OrderService proxy = (OrderService) enhancer.create();
proxy.createOrder("1001");
String order = proxy.getOrder("1001");
System.out.println("返回结果: " + order);
// 输出代理类名
System.out.println("代理类: " + proxy.getClass().getName());
System.out.println("父类: " + proxy.getClass().getSuperclass().getName());
}
}Spring AOP是运行时织入(Runtime Weaving):
1. 加载BeanDefinition
2. 实例化Bean
3. 初始化Bean(调用init方法)
4. BeanPostProcessor.postProcessAfterInitialization
- 检查是否需要代理(是否有切面匹配)
- 创建代理对象(JDK或CGLIB)
- 返回代理对象替代原始Bean
5. 容器中存储的是代理对象
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 包装为代理对象(如果需要)
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 1. 获取所有Advisor
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 2. 创建代理
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors,
new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// 添加接口
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
} else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 构建Advisor数组
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
// 自定义代理工厂
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 创建代理
return proxyFactory.getProxy(getProxyClassLoader());
}
}public class DefaultAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName,
@Nullable TargetSource targetSource) {
// 1. 获取所有Advisor
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 获取所有候选Advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 筛选匹配的Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors,
beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
}// ReflectiveMethodInvocation.proceed()
public Object proceed() throws Throwable {
// 如果执行到拦截器链末尾,调用目标方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint(); // 执行目标方法
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// 动态匹配
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
} else {
return proceed(); // 不匹配,继续下一个
}
} else {
// 静态匹配,直接执行
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}体现: JDK动态代理、CGLIB代理
为什么: 在不修改目标对象代码的情况下,添加额外功能。Spring通过代理拦截方法调用,在调用前后执行通知逻辑。
// 代理模式的核心:Proxy持有Target的引用,拦截调用
public class Proxy {
private Target target;
public void method() {
before(); // 前置通知
target.method(); // 调用目标
after(); // 后置通知
}
}体现: Advisor链、MethodInterceptor链
为什么: 动态地给对象添加功能。多个Advisor可以叠加,每个Advisor装饰前一个Advisor的功能。
// 装饰器链
Advisor1 → Advisor2 → Advisor3 → Target
// 每个Advisor在执行目标前后添加自己的逻辑体现: ReflectiveMethodInvocation执行的拦截器链
为什么: 将请求沿着处理者链传递,直到被处理。Spring的拦截器链按顺序执行,每个拦截器决定是否继续传递。
体现: AopProxy接口的不同实现(JdkDynamicAopProxy、CglibAopProxy)
为什么: 根据不同条件(是否实现接口)选择不同的代理策略。
public interface AopProxy {
Object getProxy();
Object getProxy(@Nullable ClassLoader classLoader);
}
// JdkDynamicAopProxy:基于接口
// CglibAopProxy:基于继承体现: AopProxyFactory根据条件创建不同的代理对象
为什么: 将代理创建逻辑封装在工厂中,客户端无需关心具体实现。
@Aspect
@Component
public class LoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
// 定义切点:service包下所有方法
@Pointcut("execution(* com.example.aop.demo.service.*.*(..))")
public void serviceLayer() {}
// 前置通知
@Before("serviceLayer()")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
logger.info("[LOG] 调用开始: {}.{}, 参数: {}",
joinPoint.getTarget().getClass().getSimpleName(),
methodName,
Arrays.toString(args));
}
// 返回通知
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
logger.info("[LOG] 调用成功: {}.{}, 返回: {}",
joinPoint.getTarget().getClass().getSimpleName(),
methodName,
result);
}
// 异常通知
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
logger.error("[LOG] 调用异常: {}.{}, 异常: {}",
joinPoint.getTarget().getClass().getSimpleName(),
methodName,
ex.getMessage(),
ex);
}
// 环绕通知(性能监控)
@Around("serviceLayer()")
public Object measurePerformance(ProceedingJoinPoint pjp) throws Throwable {
String className = pjp.getTarget().getClass().getSimpleName();
String methodName = pjp.getSignature().getName();
long start = System.currentTimeMillis();
try {
Object result = pjp.proceed();
long cost = System.currentTimeMillis() - start;
if (cost > 1000) {
logger.warn("[PERF] 慢查询: {}.{} 耗时: {}ms", className, methodName, cost);
} else {
logger.info("[PERF] {}.{} 耗时: {}ms", className, methodName, cost);
}
return result;
} catch (Exception e) {
long cost = System.currentTimeMillis() - start;
logger.error("[PERF] {}.{} 耗时: {}ms (异常)", className, methodName, cost);
throw e;
}
}
}@Aspect
@Component
public class SecurityAspect {
@Around("@annotation(requireRole)")
public Object checkRole(ProceedingJoinPoint pjp, RequireRole requireRole) throws Throwable {
String currentRole = getCurrentUserRole(); // 从SecurityContext获取
boolean hasRole = Arrays.stream(requireRole.value())
.anyMatch(role -> role.equals(currentRole));
if (!hasRole) {
throw new AccessDeniedException("当前用户没有权限执行此操作");
}
return pjp.proceed();
}
private String getCurrentUserRole() {
// 模拟获取当前用户角色
return "ADMIN";
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequireRole {
String[] value();
}@Aspect
@Component
public class CacheAspect {
private final RedisTemplate<String, Object> redisTemplate;
public CacheAspect(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Around("@annotation(cacheable)")
public Object cache(ProceedingJoinPoint pjp, Cacheable cacheable) throws Throwable {
String key = cacheable.key() + "::" + generateKey(pjp);
// 先查缓存
Object value = redisTemplate.opsForValue().get(key);
if (value != null) {
System.out.println("[CACHE] 命中缓存: " + key);
return value;
}
// 执行方法
value = pjp.proceed();
// 放入缓存
redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(cacheable.ttl()));
System.out.println("[CACHE] 放入缓存: " + key);
return value;
}
private String generateKey(ProceedingJoinPoint pjp) {
return Arrays.stream(pjp.getArgs())
.map(Object::toString)
.collect(Collectors.joining("-"));
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Cacheable {
String key();
long ttl() default 300; // 默认5分钟
}@Aspect
@Component
public class DistributedLockAspect {
@Autowired
private StringRedisTemplate redisTemplate;
@Around("@annotation(distributedLock)")
public Object around(ProceedingJoinPoint pjp, DistributedLock distributedLock) throws Throwable {
String lockKey = distributedLock.key();
String lockValue = UUID.randomUUID().toString();
int expireTime = distributedLock.expireTime();
Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, expireTime, TimeUnit.SECONDS);
if (!Boolean.TRUE.equals(locked)) {
throw new RuntimeException("获取分布式锁失败: " + lockKey);
}
try {
return pjp.proceed();
} finally {
// 释放锁(使用Lua脚本保证原子性)
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(lockKey), lockValue);
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DistributedLock {
String key();
int expireTime() default 30;
}| 特性 | Spring AOP | AspectJ |
|---|---|---|
| 织入时机 | 运行时 | 编译时/加载时/运行时 |
| 实现方式 | 代理模式 | 字节码修改 |
| 性能 | 有代理开销 | 更高性能 |
| 功能范围 | 仅方法级别 | 方法、字段、构造器、异常处理 |
| 依赖 | Spring框架 | AspectJ编译器(ajc) |
| 学习曲线 | 低 | 高 |
| 配置方式 | 注解/XML | 注解/AspectJ语法 |
选择建议: 大多数场景Spring AOP足够;需要更高性能或更细粒度控制时用AspectJ。
| 特性 | Spring AOP | Guice AOP |
|---|---|---|
| 绑定方式 | 自动扫描+注解 | 显式绑定(Module) |
| 切面定义 | @Aspect注解 | MethodInterceptor |
| 代理方式 | JDK/CGLIB | CGLIB |
| 功能丰富度 | 高 | 低(仅方法拦截) |
| 特性 | Spring AOP | CDI Interceptors |
|---|---|---|
| 标准 | Spring专属 | JSR-318标准 |
| 绑定方式 | 切点表达式 | @InterceptorBinding注解 |
| 适用场景 | Spring生态 | Java EE/Jakarta EE |
// JDK代理和CGLIB代理都有创建开销
// 优化:避免对不需要代理的Bean创建代理
// 精确的切点表达式
@Pointcut("execution(* com.example.service.*.*(..)) && !execution(* com.example.service.*.get*(..))")
public void serviceWriteOps() {}
// 排除getter方法,减少代理创建// 静态切点(execution)在启动时匹配一次,性能较好
// 动态切点(如基于参数的匹配)每次调用都匹配,性能较差
// 避免动态切点
@Before("args(param) && @annotation(loggable)") // 动态匹配,性能较差
public void log(Object param, Loggable loggable) { }
// 使用静态切点
@Before("@annotation(loggable)") // 静态匹配,性能较好
public void log(JoinPoint jp, Loggable loggable) { }// 过多的Advisor会增加方法调用开销
// 优化:合并相似的切面
// 不要这样做
@Aspect
@Component
public class LogAspect1 { /* 记录日志 */ }
@Aspect
@Component
public class LogAspect2 { /* 记录参数 */ }
@Aspect
@Component
public class LogAspect3 { /* 记录返回值 */ }
// 合并为一个切面
@Aspect
@Component
public class UnifiedLogAspect { /* 统一日志处理 */ }| 特性 | JDK代理 | CGLIB |
|---|---|---|
| 创建速度 | 快 | 慢(生成字节码) |
| 调用性能 | 有反射开销 | 有方法索引开销 |
| 内存占用 | 较小 | 较大 |
| 适用场景 | 接口代理 | 类代理 |
建议: Spring Boot 2.x+默认使用CGLIB,一般情况下无需修改。
@Aspect
@Component
public class AopPerformanceMonitor {
private static final Logger logger = LoggerFactory.getLogger(AopPerformanceMonitor.class);
@Around("execution(* com.example.service.*.*(..))")
public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
long cost = System.currentTimeMillis() - start;
if (cost > 500) {
logger.warn("慢方法: {}.{} 耗时: {}ms",
pjp.getTarget().getClass().getSimpleName(),
pjp.getSignature().getName(),
cost);
}
}
}
}@Service
public class UserService {
public void methodA() {
methodB(); // 同类调用,AOP失效!
}
@Transactional
public void methodB() {
// 事务不会生效
}
}最佳实践: 通过注入自身代理或从容器获取Bean调用。
@Service
public class UserService {
@Autowired
private UserService self; // 注入代理对象
public void methodA() {
self.methodB(); // 通过代理调用,AOP生效
}
@Transactional
public void methodB() {
}
}@Aspect
@Component
public class SecurityAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 如果目标方法返回this,拿到的是原始对象而非代理对象
Object result = pjp.proceed();
return result;
}
}最佳实践: 避免在AOP拦截的方法中返回this,或确保外部通过代理访问。
@Around("execution(* *(..))") // 拦截所有方法,性能灾难!
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// ...
}最佳实践: 切点表达式尽量精确,限制包路径和类名,减少不必要的代理创建。
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前");
// 忘记调用 pjp.proceed(),目标方法不会执行!
System.out.println("环绕后");
return null;
}最佳实践: 环绕通知必须调用pjp.proceed(),并正确处理返回值和异常。
@Service
public final class FinalService { // final类不能被CGLIB代理
@Transactional
public final void method() { // final方法不能被代理
}
}最佳实践: 需要AOP增强的类和方法不要用final修饰。
// 多个切面时,不指定顺序可能导致不可预期的行为
@Aspect
@Component
public class SecurityAspect { }
@Aspect
@Component
public class LoggingAspect { }最佳实践: 明确指定切面顺序。
@Aspect
@Order(1) // 安全切面优先
@Component
public class SecurityAspect { }
@Aspect
@Order(2)
@Component
public class LoggingAspect { }答:
- JDK动态代理:基于接口实现,要求目标类必须实现接口。生成的代理类实现相同接口,通过
InvocationHandler拦截方法调用。优点是不依赖第三方库,缺点是只能代理接口方法。 - CGLIB代理:基于继承实现,通过生成目标类的子类来创建代理。可以代理类的方法(包括非接口方法),但无法代理
final类和final方法。需要依赖CGLIB库,Spring已内置。
Spring默认策略:目标类实现接口用JDK代理,否则用CGLIB。Spring Boot 2.x+默认全部使用CGLIB(proxyTargetClass=true)。
深度补充:
- JDK代理通过反射调用目标方法
- CGLIB通过方法索引(MethodProxy)调用,性能略优
- CGLIB不能代理private方法
答: 单个切面的执行顺序:
@Around(前半部分)
→ @Before
→ 目标方法
→ @AfterReturning / @AfterThrowing
→ @After
→ @Around(后半部分)
多个切面时,通过@Order注解或实现Ordered接口控制优先级,数值越小优先级越高。优先级高的切面的@Before先执行,@After后执行(类似嵌套)。
答:
| 特性 | Spring AOP | AspectJ AOP |
|---|---|---|
| 织入时机 | 运行时织入 | 编译时/加载时/运行时 |
| 实现方式 | 代理模式(JDK/CGLIB) | 字节码修改 |
| 性能 | 有代理开销 | 更高性能 |
| 功能 | 仅方法级别 | 方法、字段、构造器等 |
| 依赖 | Spring框架 | AspectJ编译器 |
Spring AOP是运行时基于代理的AOP,功能相对简单但易用;AspectJ是功能更完整的AOP框架,支持更细粒度的切面。
答:
@Aspect
@Component
public class LogAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 获取方法签名
MethodSignature signature = (MethodSignature) pjp.getSignature();
String methodName = signature.getName();
// 获取参数
Object[] args = pjp.getArgs();
// 执行目标方法并获取返回值
Object result = pjp.proceed();
// 记录日志
log.info("方法:{},参数:{},返回值:{}", methodName, args, result);
return result;
}
}答:
- 同类方法调用:
this.method()不走代理,AOP失效 - 非public方法:Spring AOP基于代理,无法拦截非public方法
- final方法/类:CGLIB通过继承实现,final方法无法重写
- 非Spring管理的Bean:AOP只对Spring容器中的Bean生效
- 异步方法:
@Async会在新线程执行,事务和AOP可能不生效
答: AOP代理在Bean初始化的最后阶段生成。具体是在BeanPostProcessor.postProcessAfterInitialization()方法中,由AbstractAutoProxyCreator实现。此时Bean已经完成属性赋值和所有初始化回调(@PostConstruct、InitializingBean等),Spring检查该Bean是否有匹配的Advisor,如果有则创建代理对象替换原始Bean。
深度补充:
- AbstractAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor
- 代理创建时会检查earlyProxyReferences,避免重复代理
- 如果Bean已经被提前暴露(解决循环依赖),会在getEarlyBeanReference中创建代理
答:
- Advice:通知,定义了切面要执行的逻辑(如@Before、@Around标记的方法)。
- Advisor:顾问,是Advice和Pointcut的包装器,将通知和切点绑定在一起。Spring AOP内部使用Advisor来管理切面。
// Advisor = Advice + Pointcut
public interface Advisor {
Advice getAdvice();
}
public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut();
}答: 可以通过实现BeanPostProcessor或继承AbstractAutoProxyCreator来自定义代理创建逻辑。
@Component
public class CustomAutoProxyCreator extends AbstractAutoProxyCreator {
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName,
TargetSource customTargetSource) throws BeansException {
// 自定义匹配逻辑
if (beanClass.getName().startsWith("com.example.special")) {
Advisor advisor = createCustomAdvisor();
return new Object[]{advisor};
}
return DO_NOT_PROXY;
}
private Advisor createCustomAdvisor() {
// 创建自定义Advisor
return new DefaultPointcutAdvisor(new MyAdvice());
}
}答: Spring通过getEarlyBeanReference方法在循环依赖解决阶段提前创建代理对象:
// AbstractAutowireCapableBeanFactory.getEarlyBeanReference()
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp =
(SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}答:
- 检查Bean是否为代理对象:
userService.getClass().getName() - 检查
@EnableAspectJAutoProxy是否添加 - 检查切面类是否有
@Component - 检查切点表达式是否匹配
- 检查目标类是否被Spring管理
- 开启DEBUG日志:
logging.level.org.springframework.aop=DEBUG
答:
- JDK动态代理:目标类实现接口时生成,代理类实现相同接口
- CGLIB代理:目标类不实现接口时生成,代理类继承目标类
- Spring Boot 2.x+默认全部使用CGLIB代理
答: @EnableAspectJAutoProxy注解用于开启Spring AOP的自动代理支持。它会向Spring容器中注册AnnotationAwareAspectJAutoProxyCreator,这是一个BeanPostProcessor,负责在Bean初始化后检查是否需要创建代理对象。
答:
- Before Advice:前置通知,方法执行前
- After Returning Advice:返回通知,方法正常返回后
- After Throwing Advice:异常通知,方法抛出异常后
- After (Finally) Advice:后置通知,方法执行后(无论是否异常)
- Around Advice:环绕通知,包围方法执行
答:
- AspectJ编译时织入:在编译阶段修改字节码,性能更好,支持更细粒度的切面(字段、构造器等),需要AspectJ编译器
- Spring AOP运行时织入:通过代理模式在运行时创建代理对象,简单灵活,仅支持方法级别拦截,无需特殊编译器
答:
@Around("@annotation(loggable)")
public Object around(ProceedingJoinPoint pjp, Loggable loggable) throws Throwable {
// 获取方法上的注解
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
Loggable annotation = method.getAnnotation(Loggable.class);
// 使用注解属性
String value = annotation.value();
// ...
return pjp.proceed();
}答: 不能直接转换。JDK代理实现的是接口,CGLIB代理继承的是类。如果需要获取原始对象,可以通过AopContext或AopProxyUtils:
// 方式1:AopContext
UserService target = (UserService) AopContext.currentProxy();
// 方式2:Advised接口
if (proxy instanceof Advised) {
Object target = ((Advised) proxy).getTargetSource().getTarget();
}答: Spring AOP基于运行时类型,对于泛型方法的切面匹配需要注意类型擦除。可以使用args()指示器进行运行时类型匹配:
@Around("execution(* com.example.service.*.*(..)) && args(param)")
public Object around(ProceedingJoinPoint pjp, Object param) throws Throwable {
// param的类型是运行时实际类型
return pjp.proceed();
}答: Introduction是AOP的一种特殊Advice,允许为现有类添加新的接口实现。Spring AOP支持Introduction,通过@DeclareParents注解实现:
@Aspect
@Component
public class IntroductionAspect {
@DeclareParents(value = "com.example.service.*", defaultImpl = AuditableImpl.class)
private Auditable auditable;
}此文原创,转载请注明出处。