AOP(面向切面编程),实际就是代理的一个应用,常用的场景有日志、事务、缓存等,分为静态代理和动态代理。
他们的区别是静态代理只能作用在方法上,且代理类是手动编写,常见的就是使用AspectJ,静态代理是在编译期织入。
而动态代理作用域更广泛,可以在属性,方法,构造器等上插入,常用的是cglib,和jdk,动态代理是在运行期枳织入。
cglib和jdk的区别是jdk必须要有实现类,而cglib是生成一个继承类作为代理类。
他们的都是字节码增强
下面简单介绍一下AspectJ:
AspectJ 支持 5 种类型的通知注解:
@Before:前置通知,在方法执行之前执行
@After:后置通知,在方法执行之后执行 (无论是否发生异常都会执行)
@AfterRunning:返回通知,在方法返回结果之后执行
@AfterThrowing:异常通知,在方法抛出异常之后
@Around:环绕通知,围绕着方法执行
例子(接口日志异步入库):
@Aspect
@Component
public class LogAspect {
@Autowired
private SaveLogService saveLogService;
// 定义切点
@Pointcut("@annotation(com.zdww.cd.sx.config.MyLog)")
public void LogPointCut() {
}
//切面(通知和切点) 配置通知
@After("LogPointCut()")
// 一共有五种通知,after在方法之后执行
public void saveOperation(JoinPoint joinPoint) {
//用于保存日志
com.zdww.cd.sx.pojo.MyLog log = new com.zdww.cd.sx.pojo.MyLog();
//从连接点处通过反射机制获取连接点处的方法签名对象
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取连接点所在的方法
Method method = signature.getMethod();
//获取操作方法上的MyLog注解的值
MyLog myLog = method.getAnnotation(MyLog.class);
if (myLog != null) {
String operation = myLog.operation();
log.setOperation(operation);
String type = myLog.type();
log.setType(type);
}
HttpServletRequest request = ((ServletRequestAttributes) Objects
.requireNonNull(RequestContextHolder
.getRequestAttributes())).getRequest();
String requestURL = request.getRequestURL().toString();
log.setUrl(requestURL);
String ip = IPUtil.getIP(request);
log.setLoginIp(ip);
Date now = new Date();
log.setCreateTime(now);
User user = (User) request.getSession().getAttribute("user");
if (user != null) {
String userId = user.getAccount();
String userName = user.getUsername();
log.setUserId(userId);
log.setUserName(userName);
saveLogService.saveLog(log);
}
}
}
获取切入方法对象三种方法:
1.通过反射机制获取切入点目标类的方法
public void invoke(JoinPoint joinPoint) throws Throwable{
//登录拦截
MethodInvocationProceedingJoinPoint methodPoint = (MethodInvocationProceedingJoinPoint)joinPoint;
Field proxy = methodPoint.getClass().getDeclaredField("methodInvocation");
proxy.setAccessible(true);
ReflectiveMethodInvocation j = (ReflectiveMethodInvocation) proxy.get(methodPoint);
Method method = j.getMethod();
Login login = method.getAnnotation(Login.class);
}
2.通过JoinPoint的getTarget()获取连接点所在的目标对象
public void invoke(JoinPoint joinPoint) throws Throwable{
//拦截的实体类
Object target = joinPoint.getTarget();
//拦截的方法名称
String methodName = joinPoint.getSignature().getName();
//拦截的方法参数
Object[] argsa = joinPoint.getArgs();
//拦截的放参数类型
Class[] parameterTypes = ((MethodSignature)joinPoint.getSignature()).getMethod().getParameterTypes();
Method method = target.getClass().getMethod(methodName, parameterTypes);
Login login = method.getAnnotation(Login.class);
}
3.通过JoinPoint的getSignature()获取连接点的方法签名对象
public void invoke(JoinPoint joinPoint) throws Throwable{
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
Login login = method.getAnnotation(Login.class);
}