引言
在Java开发中,面向切面编程(AOP)是一种重要的设计思想,它允许开发者将通用的横切关注点如日志记录、事务管理、权限验证等从业务逻辑中分离出来,实现模块化和可复用性。Spring框架提供了强大的AOP支持,其中就包括使用CGLIB动态代理机制来实现代理功能。
CGLIB动态代理产生的背景
JDK自身提供的动态代理机制基于接口实现,其java.lang.reflect.Proxy类能够为我们动态地创建一个实现了指定接口的新类,这个新类在运行时可以代理对原接口方法的调用,从而在调用前后插入额外的操作。然而,这种机制有一个显著的限制,那就是它只适用于实现了接口的类。
面对大量未实现接口的业务类,如果我们仍希望对其进行代理增强,那么就需要一种能够在运行时动态创建子类的解决方案。这就催生了CGLIB动态代理库的广泛应用。CGLIB通过生成被代理类的子类并在子类中重写非final方法来实现动态代理,突破了JDK动态代理仅限于接口的局限,使得代理功能可以应用于几乎所有的Java类。
为何使用CGLIB动态代理
- 无接口依赖:CGLIB无需目标类实现任何接口,只要目标类没有声明为final,就可以为其实现代理功能,适用范围更广。
- 高效性:CGLIB底层基于ASM字节码生成框架,直接对字节码进行操作,因此在某些场景下的性能表现优于反射代理,特别是在频繁调用代理方法的情况下。
- 深度代理:由于CGLIB是通过生成子类的方式实现代理,它可以代理目标类中的所有方法,包括私有方法(虽然在AOP实践中一般不涉及私有方法的增强),并且可以方便地处理final方法以外的成员变量。
Spring Boot中使用CGLIB动态代理示例
package com.example.demo.annotation;
import java.lang.annotation.*;
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
}
package com.example.demo.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
/**
* @Pointcut注解用于定义一个切点表达式,即定义在哪些连接点(方法执行、异常抛出等)上应用通知(如前置通知、后置通知、环绕通知等)
* @annotation(com.example.demo.annotation.Log):匹配所有标记了com.example.demo.annotation.Log注解的方法
* @within(com.example.demo.annotation.Log):匹配所有声明了com.example.demo.annotation.Log注解的类型(类或接口)。这意味着如果一个类或接口被com.example.demo.annotation.Log注解修饰,那么这个类或接口下的所有公共方法都将作为切点。
* 通过使用逻辑运算符||,上述两个条件合并为一个切点表达式,表示同时满足上述任意一种情况的方法都会成为切点
*/
@Pointcut("@annotation(com.example.demo.annotation.Log)"+"|| @within(com.example.demo.annotation.Log)")
public void dsPointCut() {
}
/**
* @Before:前置通知,在目标方法执行前执行。
* @param joinPoint
*/
@Before("dsPointCut()")
public void beforeMethod(JoinPoint joinPoint) {
log.info("Pre-notification:Start executing the method");
log.info("joinPoint.getSignature().getName():{}",joinPoint.getSignature().getName());
log.info("joinPoint.getArgs():{}",joinPoint.getArgs());
log.info("joinPoint.getTarget():{}",joinPoint.getTarget());
log.info("joinPoint.getThis():{}",joinPoint.getThis());
log.info("joinPoint.getSignature():{}",joinPoint.getSignature());
}
/**
* @After:后置通知(最终通知),无论方法是否抛出异常,都会在方法执行后调用
* @param joinPoint
*/
@After("dsPointCut()")
public void afterMethod(JoinPoint joinPoint) {
log.info("Post notification: After the method is executed");
}
/**
* @AfterReturning:返回通知,仅在方法正常执行完毕并返回时调用。
* @param joinPoint
* @param result
*/
@AfterReturning(pointcut = "dsPointCut()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
log.info("Return notification: The method returns normally, with the result: {}",result);
}
/**
* @AfterThrowing:异常通知,仅在方法抛出异常后调用。
* @param joinPoint
* @param e
*/
@AfterThrowing(pointcut = "dsPointCut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Exception e) {
log.info("Exception notification: Method execution throws an exception: {}",e.getMessage());
}
/**
* @Around:环绕通知,最为强大的通知类型,能够完全控制何时执行目标方法以及如何处理目标方法的返回值。
* @param pjp
* @return
* @throws Throwable
*/
@Around("dsPointCut()")
public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
log.info("Around notification: Before the method is executed");
Object result = pjp.proceed();
log.info("Around notification: After the method is executed");
return result;
}
}
package com.example.demo.controller;
import com.example.demo.annotation.Log;
import com.example.demo.utils.result.ResultEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/log/aspect/test")
public class LogAspectTestController {
@Log
@GetMapping("/test")
public ResultEntity<String> test() {
return ResultEntity.success("test");
}
@Log
@GetMapping("/testError")
public ResultEntity<String> testError() {
int count = 1/0;
return ResultEntity.success("testError");
}
}