返回顶部

SpringAOP增强-几种不同将注解和切面方法的关联方式

@Around的作用

  • 既可以在目标方法之前织入增强动作,也可以在执行目标方法之后织入增强动作;
  • 可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止目标目标方法的执行;
  • 可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值; 当需要改变目标方法的返回值时,只能使用Around方法;
  • 虽然Around功能强大,但通常需要在线程安全的环境下使用。因此,如果使用普通的Before、AfterReturing增强方法就可以解决的事情,就没有必要使用Around增强处理了。

注解方式:如果需要对某一方法进行增强,只需要在相应的方法上添加上自定义注解即可

 

注解类:

package com.rq.aop.common.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)//运行时有效
@Target(ElementType.METHOD)//作用于方法
public @interface MyAnnotation {
    String methodName () default "";
}

 

将注解和切面方法的关联方式 ---第一种切点方式:

@Around(value = "@annotation(around)") //around 与 下面参数名around对应
    public void processAuthority(ProceedingJoinPoint point,MyAnnotation around) throws Throwable{
.....
}
注解上加
@annotation(变量名称),
下面参数加 注解类型+变量名称,
注意,这两个变量名称必须是一样的,这样在加@MyAnnotation 注解方法上,就能切入到此方法上了。
package com.rq.aop.common.advice;

import com.rq.aop.common.annotation.MyAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect //标注增强处理类(切面类)
@Component //交由Spring容器管理
public class AnnotationAspect {

    /*
    可自定义切点位置,针对不同切点,方法上的@Around()可以这样写ex:@Around(value = "methodPointcut() && args(..)")
    @Pointcut(value = "@annotation(com.rq.aop.common.annotation.MyAnnotation)")    
    public void methodPointcut(){}

    @Pointcut(value = "@annotation(com.rq.aop.common.annotation.MyAnnotation2)")
    public void methodPointcut2(){}
    */

    //定义增强,pointcut连接点使用@annotation(xxx)进行定义
    @Around(value = "@annotation(around)") //around 与 下面参数名around对应
    public void processAuthority(ProceedingJoinPoint point,MyAnnotation around) throws Throwable{
        System.out.println("ANNOTATION welcome");
        System.out.println("ANNOTATION 调用方法:"+ around.methodName());
        System.out.println("ANNOTATION 调用类:" + point.getSignature().getDeclaringTypeName());
        System.out.println("ANNOTATION 调用类名" + point.getSignature().getDeclaringType().getSimpleName());
        point.proceed(); //调用目标方法
        System.out.println("ANNOTATION login success");
    }
}

 

将注解和切面方法的关联方式 

方式二:直接在切面方法上 指定注解路径

@Before(value = "@annotation(com.cms.common.annotation.ControllerOptLog)")  //这里直接 指定注解的位置即可(注解的全路径)。 然后再需要添加注解的方法A上加这个注解,那方法A就能被当前的beforeAdvice()方法所切入。
public void beforeAdvice(JoinPoint joinPoint) throws Throwable { }




@ControllerOptLog public void 方法A(参数类型,变量) throws Exception

{ .....

..... ....... }

 

方式三:

先将这个注解 和某个方法绑定,然后再在@before注解中指定这个方法: 其实可以理解成是一个间接关联。

注解关联到-方法    方法再关联到曾强的具体逻辑处。

    /**
     * 定义作为切入点的方法 ,并且将切入方法和@ParamValided 关联起来
     */
    @Pointcut("@annotation(com.atguigu.gulimall.coupon.learn.annotation.ParamValided)")  //这里是注解的定义全路径
    public void pointcut(){};

    @Before(value="pointcut()")
    public void before(JoinPoint joinPoint) throws Exception{
            .................
    }

@ParamValided

public void 方法A(参数类型,变量) throws Exception

{ ..........

....... }



 

方式four: 在上述的两种,不声明切面方法与自定义注解的直接绑定,而是间接的在切面方法中,用到“”是否有这个注解”的判断,如果有这个注解,那再去做一些处理逻辑。这是这种切面方法和自定义注解的关联方式。

自定义注解:MyParamValided

/**
 * myself自定义的参数校验注解
 */
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyParamValided {
}

 

切面类中的拦截方法: isValidedAnnotation(mSig.getMethod())

@Slf4j
@Aspect
@Component
public class ParamValidAspect {

    /**
     * 依据hibernate.validator校验参数
     *
     * @param joinPoint 连接点
     * @return 方法执行结果
     * @throws Throwable 调用出错
     */
    @Around(value = "execution(* com.hbis.cwms.*.controller..*(..))")
//    @Around(value = "execution(* com.hbis.vesta.controller.**(..))")
    public Object valid(ProceedingJoinPoint joinPoint) throws Throwable {

        long beginTime = System.currentTimeMillis();

        // 获取业务处理方法注解
        MethodSignature mSig = (MethodSignature)joinPoint.getSignature();
        Validator v = ApplicationContextRegister.getBean(ValidatorImpl.class);

        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            for (Object arg : args) {
                // 判断该方法是否需要校验参数
                if (isValidedAnnotation(mSig.getMethod())) {  //这里其实就是在判断,当前拦截到的 类的方法的参数上是否有@MyParamValided  这个注解,有的话,则进行下面的校验

                    ValidatorResult chkRst = beanValidator(v, arg);
                    if (!chkRst.isSucess()) {
                        // 数据校验失败,直接返回错误信息
                        return Result.error("错误信息提示!")
                    }
                }
            }
        }

        return joinPoint.proceed();  //切面执行完毕之后,继续执行原始方法
    }

 

判断是否有这个注解的方法具体内容:isValidedAnnotation(Method method) 的具体实现内容

    /**
     * 获取在拦截范围内,所有的方法是否有@MyParamValided 这个注解做注释。
     *
     * @param method 要获取参数名的方法
     * @return 按参数顺序排列的参数名列表
     */

    private boolean isValidedAnnotation(Method method) {
        Annotation[][] paramAnnotations = method.getParameterAnnotations();
        if (paramAnnotations == null || paramAnnotations.length == 0) {
            return false;
        }
        for (Annotation[] parameterAnnotation : paramAnnotations) {
            for (Annotation annotation : parameterAnnotation) {
                if (annotation instanceof MyParamValided) {
                    return true;
                }
            }
        }

        return false;
    }

 

校验方法:beanValidator(v, arg); 具体是通过hutool工具做的参数校验,做的是参数实体类的属性上像@Blank   @NotBlank等等注解的验证是否符合。

 

PermissionInterceptor 
posted @ 2024-03-31 13:32  fen斗  阅读(103)  评论(0编辑  收藏  举报