AOP 是什么?
面向切面编程(Aspect Oriented Programing)
应用场景
适用于具有横切逻辑的场合,如性能检测、访问控制、事务管理及日志记。
AOP
两种拦截方式
- 基于注解
- 基于方法
连接点 JoinPoint
Spring 近支持方法的连接点,能在方法调用前、方法调用后、方法抛出异常时及方法调用前后这些执行点织入增强
连接点是客观存在的方法
切点 Pointcut
指定在哪些类的哪些方法上织入横切逻辑
一个切点可以匹配多个连接点
对应连接点表达式
增强 Advice
增强是织入目标类连接点上的横切逻辑代码,包含定位连接点的方位信息
- 前置增强:方法执行前增强 @Before("logPointCut()")
- 后置增强:方法执行后增强 @After("logPointCut()")
- 环绕增强:方法执行前后增强 @Around("logPointCut()")
- 异常抛出增强:方法抛出异常后增强 @AfterThrowing()
- 引介增强:在目标类添加新的方法和属性
切面 Aspect
切面由切点和增强组成。对应一个类
织入
织入是将增强添加到目标类的具体连接点上的过程。
Spring采用动态代理织入,AspectJ采用编译器织入和类装载期织入。
实例
@Slf4j
@Aspect
@Component
public class LogAspect {
ThreadLocal<Long> startTime = new ThreadLocal<>();
@Pointcut("execution(public * com.pengtech.school.*.controller..*.*(..))")
public void logPointCut(){
}
@Before("logPointCut()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
startTime.set(System.currentTimeMillis());
log.info("########请求地址:" + request.getRequestURL());
log.info("########开始时间:" + LocalDateTime.now());
log.info("########请求方法:" + joinPoint.getSignature().getDeclaringTypeName() +
"." + joinPoint.getSignature().getName());
Object[] args = joinPoint.getArgs();
for (Object param : args) {
log.info("########方法参数:" + param);
}
}
@After("logPointCut()")
public void doAfter() throws Throwable {
log.info("#######执行间隔:" + (System.currentTimeMillis() - startTime.get())
+ "ms");
log.info("#######完成时间:" + LocalDateTime.now());
}
}
AOP 面向切面编程
概述
适合具有横切逻辑的应用场合,如性能监测、访问控制、事务管理和日志记录。
Spring AOP使用动态代理技术在运行期向目标类织入增强的代码。
Spring AOP 两种代理机制
- 基于JDK的动态代理
- 基于CGLib的动态代理
JDK 动态代理
在运行期创建接口的代理实例
限制:只能为接口创建代理实例
// InvocationHandler 实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态的将横切逻辑和业务逻辑编织在一起。
public class PerformanceHandler implements InvocationHandler {
// 目标业务类
private Object target;
public PerformanceHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 横切代码
PerformanceMonitor.begin(target.getClass().getName()+"."+ method.getName());
// 反射调用目标类的目标方法
Object object = method.invoke(target, args);
// 横切代码
PerformanceMonitor.end();
return object;
}
}
Proxy 利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象
// 使用JDK动态代理
// 被代理的目标业务类
ForumService target = new ForumServiceImpl();
// 将目标业务类和横切代码编织到一起
PerformanceHandler handler = new PerformanceHandler(target);
// 创建代理实例
ForumService proxy = (ForumService) Proxy.newProxyInstance(target
.getClass().getClassLoader(),
target.getClass().getInterfaces(), handler);
// 调用代理方法
proxy.removeForum(10);
proxy.removeTopic(1012);
CGLib 动态代理
动态创建子类的方式生成代理对象
不能对final或private方法进行代理
//实现MethodInterceptor接口,并重写intercept方法
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
//创建动态代理对象
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
// 拦截所有目标类方法的调用
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
PerformanceMonitor.begin(obj.getClass().getName()+"."+method.getName());
Object result=proxy.invokeSuper(obj, args);
PerformanceMonitor.end();
return result;
}
//动态生成子类的方式创建代理类
CglibProxy cglibProxy = new CglibProxy();
ForumService forumService = (ForumService)cglibProxy.getProxy(ForumServiceImpl.class);
forumService.removeForum(10);
forumService.removeTopic(1023);
}