SpringBoot AOP代理学习及使用

2018-03-20  本文已影响0人  wyon

1、基本注解及说明

AOP即面向切面编程,是通过jdk动态代理或cglib代理实现在原有代码前后切入其他业务逻辑的技术,它不会破坏程序原有逻辑,因此可以很好的对业务逻辑的各个部分进行隔离,从而降低耦合度,提高程序的可重用性及开发效率。在SpringBoot中,通过注解方式实现AOP功能,代理的生成,管理及其依赖关系都是由Spring容器负责(不受Spring容器管理的对象是无法使用AOP进行切面控制的),其注解主要包括:

注解 注解功能
@Aspect 定义一个切面类,配合@Component表示一个受Spring容器管理的切面bean,配合@Order表示切面的优先级, 它的值越小,优先级越高。
@Pointcut 定义一个切点,为切入业务提供连接点,切点表达式见第二部分
@Before 在切点开始前执行,切点信息JoinPoint
@AfterReturning 在切点完成后执行,切点信息JoinPoint,可指定一个returning参数,获取切点返回值
@AfterThrowing 在切点异常时执行,切点信息JoinPoint,可指定一个throwing参数,获取异常信息
@After 在切点完成后执行,无论目标方法是否成功完成,切点信息JoinPoint
@Around 切点环绕通知,在目标方法完成前后处理,切点信息ProceedingJoinPoint,此外可以调用其proceed方法执行原有方法

进入目标方法时,先织入@Around,再织入@Before,退出目标方法时,先织入@Around,再织入@AfterReturning,最后才织入@After

2、切入点表达式

*  : 匹配任何数量字符;
.. : 匹配任何数量字符的重复。如在类型模式中匹配任何数量子包,而在方法参数模式中匹配任何数量参数。
+  : 匹配指定类型的子类型,仅能作为后缀放在类型模式后面。
&& : ‘与’组合切入点表达式。
|| : ‘或’组合切入点表达式。
!  : ‘非’切入点表达式。
// 表达式规则
execution(modifiers? result-type declaring-type? method-name(param-type) throws-type?)
// 匹配对象的任意public方法
execution(public * *(..))
// 匹配UserResource对象的任意方法
execution(* com.gwyon.spring.resource.UserResource.*(..))
// 匹配resource包及其子包中对象的任意方法:
execution(* com.gwyon.spring.resource..*.*(..))
// 表达式规则,等价于execution(* class-type.*(..))
within(class-type)
// 匹配resource包的对象的任意方法
within(com.gwyon.spring.resource.*)
// 匹配UserResource对象的任意方法
within(com.gwyon.spring.resource.UserResource)
// 表达式规则,不支持通配符
target(class-type)
// 匹配UserResource对象自身的方法
target(com.gwyon.spring.resource.UserResource)
// 表达式规则,不支持通配符
this(class-type)
// 匹配UserResource对象所有的方法
target(com.gwyon.spring.resource.UserResource)
// 表达式规则,不支持通配符
args(param-type)
// 匹配对象所有参数为model下对象的方法
args(com.gwyon.spring.model.*)
// 匹配对象所有参数为User的方法
args(com.gwyon.spring.model.User)
// 表达式规则
bean(bean-name)
// 匹配名为userResource的对象的所有方法
bean(userResource)
// 表达式规则,不支持通配符
@annotation(enum-type)
// 匹配对象所有持有RequestMapping注解的方法
@annotation(org.springframework.web.bind.annotation.RequestMapping)
// 表达式规则,不支持通配符
@within(enum-type)
// 匹配持有RestController注解的对象的所有方法
@within(org.springframework.web.bind.annotation.RestController)
// 表达式规则,不支持通配符
@target(enum-type)
// 匹配持有RestController注解的对象的自身方法
@target(org.springframework.web.bind.annotation.RestController)
// 表达式规则,不支持通配符
@args(enum-type)
// 匹配对象所有持有PathVariable注解的方法
@args(org.springframework.web.bind.annotation.PathVariable)
call,get,set,initialization,staticinitialization,preinitialization,handler,adviceexecution,withincode,cflow,cflowbelow,if,@this,@withincode

3、获取参数的方法

ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
HttpServletResponse response = attributes.getResponse();
MethodSignature methodSignature = (MethodSignature) point.getSignature();
Method method = methodSignature.getMethod();
public interface JoinPoint {
    String toString();// 连接点所在位置的相关信息  
    String toShortString();// 连接点所在位置的简短相关信息  
    String toLongString();// 连接点所在位置的全部相关信息  
    Object getThis();// 返回aop代理对象  
    Object getTarget();// 返回目标对象  
    Object[] getArgs();// 返回被通知方法参数列表  
    Signature getSignature();// 返回当前连接点签名  
    SourceLocation getSourceLocation();// 返回连接点方法所在类文件中的位置  
    String getKind();// 连接点类型  
    StaticPart getStaticPart();// 返回连接点静态部分  
}
public interface ProceedingJoinPoint extends JoinPoint {
    Object proceed() throws Throwable;
    Object proceed(Object[] args) throws Throwable;
}
public interface ProceedingJoinPoint.StaticPart {
    Signature getSignature();// 返回当前连接点签名
    String getKind();// 连接点类型
    int getId();// 唯一标识
    String toString();// 连接点所在位置的相关信息
    String toShortString();// 连接点所在位置的简短相关信息
    String toLongString();// 连接点所在位置的全部相关信息
}
@Before(value = "target(userResource)&&args(user)&&@annotation(transactional)"
        , argNames = "point,userResource,user,transactional")
public void before5(JoinPoint point, UserResource userResource, User user, Transactional transactional) {
    // 处理逻辑
}
@AfterReturning(value = "target(userResource)", returning = "obj")
public void before5(JoinPoint point, UserResource userResource, Object obj) {
    // 处理逻辑
}
@AfterThrowing(value = "target(userResource)", throwing = "error")
public void before5(JoinPoint point, UserResource userResource, Exception error) {
    // 处理逻辑
}

4、使用AOP记录网络请求日志

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface KeepLog {
    /**
     * 设置日志备注
     *
     * @return 日志备注
     */
    String value() default "";
}
@Aspect
@Component
public class KeepLogAspect {
    private final LogInfoRepository logInfoRepository;

    @Autowired
    public KeepLogAspect(LogInfoRepository logInfoRepository) {
        this.logInfoRepository = logInfoRepository;
    }

    /**
     * 获取切点
     *
     * @param keepLog 切点注解
     */
    @Pointcut("@annotation(keepLog)")
    public void logPoint(KeepLog keepLog) {
    }

    /**
     * 处理方法正常返回
     *
     * @param joinPoint 方法连接点
     * @param keepLog   切点注解
     */
    @AfterReturning(value = "logPoint(keepLog)", argNames = "joinPoint,keepLog")
    public void afterReturning(JoinPoint joinPoint, KeepLog keepLog) {
        LogInfo logInfo = getLogInfo(joinPoint, keepLog);
        logInfo.setError("");
        logInfoRepository.save(logInfo);
    }

    /**
     * 处理方法报错
     *
     * @param joinPoint 方法连接点
     * @param keepLog   切点注解
     * @param throwable 错误信息
     */
    @AfterThrowing(pointcut = "logPoint(keepLog)", throwing = "throwable", argNames = "joinPoint,keepLog,throwable")
    public void afterThrowing(JoinPoint joinPoint, KeepLog keepLog, Throwable throwable) {
        LogInfo logInfo = getLogInfo(joinPoint, keepLog);
        logInfo.setError(throwable.getClass().getName() + ":" + throwable.getMessage());
        logInfoRepository.save(logInfo);
    }

    /**
     * 解析方法信息
     *
     * @param joinPoint 方法连接点
     * @param keepLog   切点注解
     * @return logInfo
     */
    @SuppressWarnings("ConstantConditions")
    private LogInfo getLogInfo(JoinPoint joinPoint, KeepLog keepLog) {
        LogInfo logInfo = new LogInfo();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            logInfo.setRequest(request.getRequestURI());
            logInfo.setRemote(request.getRemoteAddr());
            logInfo.setMethod(request.getMethod());
        }
        logInfo.setTimes(new Date());
        logInfo.setPoint(joinPoint.getSignature().toString());
        logInfo.setNotes(keepLog.value());
        return logInfo;
    }
}
@KeepLog("modifyUser")
@PatchMapping
@Transactional(rollbackOn = Throwable.class)
public NetResult patch(@RequestBody User user) {
  return new NetResult(false, userService.patch(user));
}

使用的SpringBoot版本为2.0.0.RELEASE点此查看部分代码。

参考

上一篇 下一篇

猜你喜欢

热点阅读