AOP:面向切面编程,在不修改目标类代码的前提下,可以通过AOP技术去增强目标类的功能。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
利用AOP可以对业务代码中【业务逻辑】和【系统逻辑】进行隔离,从而使得代码耦合度降低,提高程序的可用性,同时提高开发效率。
AOP采用横向抽取的机制,补充了传统纵向集成体系OOP(面向对象编程)无法解决的重复性代码优化(系统检测、事务管理、安全检查、缓存),将业务逻辑和系统处理的代码(关闭连接、事务操作、日志记录)解耦。
AOP相关术语介绍:
Joinpoint(连接点):指被拦截到的点,这些点指的是方法,spring只支持方法类型的连接点。
Pointcut(切入点):指我们要对哪些Joinpoint进行拦截定义。
Advice(通知\增强):指拦截到Joinpoint之后要做的事情就是通知,通知分为前置通知、后置通知、异常通知、最终通知、环绕通知(切面要完成的功能)。
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态的添加一些方法或Field。
Target(目标对象):代理的目标对象。
Weading(织入):是指把增强应用到目标对象来创建新的代理对象的过程。
Proxy(代理):一个类被AOP组织增强后,就产生一个代理结果类。
Aspect(切面):切入点和通知的结合,需要自己编写和配置。
Advisor(通知器、顾问):和Aspect很像。
基于注解实现:
- 定义注解
package com.zww.demo.aop.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ILog {
// String flag default ""
}
- 定义方法
package com.zww.demo.aop.controller;
import com.zww.demo.aop.annotation.ILog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AopController {
@ILog
@GetMapping("/test")
public void test(){
// int i = 1/0;
System.out.println("hello!");
}
}
- 定义切面
package com.zww.demo.aop.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Component
/** 切面 */
@Aspect
public class LogAdvice {
/** 切点
* @annotation 注解,可配置系统注解或者自定义注解,只要该方法添加该注解即接入切面
* 例:@annotation(com.zww.demo.aop.annotation.ILog)
* execution 表达式,通过全限定类名,方法匹配
* 例:execution(public * com.zww.demo.aop.controller..*(..))
*/
// @Pointcut("execution(public * com.zww.demo.aop.controller..*(..))")
@Pointcut("@annotation(com.zww.demo.aop.annotation.ILog)")
public void logPointcut(){};
@Before("logPointcut()")
public void before(JoinPoint point){
System.out.println("前置通知");
// TODO
// 获取签名
Signature signature = point.getSignature();
System.out.println("执行方法:"+signature.getName()+", 包:"+signature.getDeclaringTypeName());
// 可以记录一些请求信息,URL和IP
ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
String url = request.getRequestURL().toString();
String remoteAddr = request.getRemoteAddr();
System.out.println("请求地址:"+url+",请求ip:"+remoteAddr);
}
@After("logPointcut()")
public void after(JoinPoint point){
// TODO
System.out.println("后置通知");
}
@Around("logPointcut()")
public Object around(ProceedingJoinPoint joinPoint){
// TODO
// 获取请求参数
Object[] args = joinPoint.getArgs();
Object result;
try {
// @Before
System.out.println("环绕前置通知");
result = joinPoint.proceed(args);
// @AfterReturning
System.out.println("环绕返回通知");
} catch (Throwable e) {
// @AfterThrowing
System.out.println("环绕异常通知");
throw new RuntimeException(e);
} finally {
// @after
System.out.println("环绕后置通知");
}
return result;
}
@AfterReturning("logPointcut()")
public void afterReturning(JoinPoint point){
System.out.println("返回通知");
}
@AfterThrowing("logPointcut()")
public void afterThrowing(JoinPoint point){
System.out.println("异常通知");
}
}
- 验证结果