AOP的概念
- AOP全称Aspect -Oriented Proramming,中文称为面向切面编程:
AOP 中的基础要素:
-
Join point(连接点):程序能被拦截的某些目标,比如某个执行方法。
-
Advice(通知):是指在特定的连接点(Join point)要做的动作。通知分为方法执行前通知(Before Advice),方法执行后通知(After Advice),环绕通知(Around Advice)等。。
-
Pointcut(切点): 用来切入Join point(连接点)的特定表述语言,比如拦截某个类的某些执行方法
-
Aspect(切面): 各种Advice(通知)与Pointcut(切点)相互组成的实体
-
Weaving(织入): 将Advice(通知)嵌入OOP程序中的过程
Java平台AOP的实现
- AOP是一种编程理念,需要具体的实现机制,Java平台上的实现主要分为以下几类:
- 动态代理: 功能模块类需实现接口(Spring AOP默认采用这种机制)。
- 动态字节码增强:在运行期间,通过生成子类将切面逻辑动态加入到这些子类中。
- Java代码生成:比较古老的方式,基本已经淘汰。
- 自定义类加载器:在加载class文件期间,将切面逻辑添加到功能模块的类中。
- AOL扩展:用扩展语言实现AOP。
Spring的AOP框架
- Spring的AOP采用JDK动态代理机制和CJLIB字节码生成技术实现 :
@ AspectJ形式的Spring AOP
-
AspectJ的acj编译器把aspect类编译成class字节码后,在java目标类再进行编译时织入,最终形成完整的Java类字节码文件。
Spring AOP 应用案例
- 日志处理案例
/**
* 日志AOP
*/
@Aspect
public class LogAspect {
// 日志服务类
private final LogService logService;
ThreadLocal<Long> currentTime = new ThreadLocal<>();
// 构造方法注入
public LogAspect(LogService logService) {
this.logService = logService;
}
/**
* 配置切入点
*/
@Pointcut("@annotation(demo.aspectJ.annotation.Log)")
public void logPointcut() {
// 该方法无方法体,主要为了让同类中其他方法使用此切入点
}
/**
* 配置环绕通知,使用在方法logPointcut()上注册的切入点
*
* @param joinPoint join point for advice
*/
@Around("logPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object result;
currentTime.set(System.currentTimeMillis());
result = joinPoint.proceed();
Log log = new Log("INFO",System.currentTimeMillis() - currentTime.get());
currentTime.remove();
HttpServletRequest request = RequestHolder.getHttpServletRequest();
logService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request),joinPoint, log);
return result;
}
/**
* 配置异常通知
*
* @param joinPoint join point for advice
* @param e exception
*/
@AfterThrowing(pointcut = "logPointcut()", throwing = "e")
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
Log log = new Log("ERROR",System.currentTimeMillis() - currentTime.get());
currentTime.remove();
log.setExceptionDetail(ThrowableUtil.getStackTrace(e).getBytes());
HttpServletRequest request = RequestHolder.getHttpServletRequest();
logService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request), (ProceedingJoinPoint)joinPoint, log);
}
...
}
@Log("用户登录")
public ResponseEntity<Object> login(@Validated @RequestBody UserDTO user, HttpServletRequest request) throws Exception {
// 登录过程
...
}