2

Java里使用AspectJ实现AOP - kiba518

 6 months ago
source link: https://www.cnblogs.com/kiba/p/18027435
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Java里使用AspectJ实现AOP

已经正式从NET转型JAVA。今后开始多写一点JAVA相关的文章。

因为已经正式转Java了,所以,对于Java的一些判断,应该就比以前更准确了。总得来说,java有好的东西,有不好的东西,就语言本身和java的常用组件来讲,并不能判断,java比其他语言高一个档次,当然,也不会低一个档次。应该跟其他语言是一个段位的。

但java的调试,确实是比较花费时间,他做不到编译成功后,就能运行成功。这里有注解的问题,有maven的问题,有组件版本的问题。总之,检测的非常不好,非常浪费时间。

java的好处就是,团队成员比较多,毕竟开发起来真的很废人。但好处也在这里,人多,代表着,1,大家的压力都不大,人多压力就会分散。2,功能和性能有时间做的更优秀,人多就是工时多。

而且Java工资确实相对比其他语言高。

总体来说,java是比较幸福的。

Aspectj提供一种在字符串里编程的模式,即在字符串里写函数,然后程序启动的时候会动态的把字符串里的函数给执行了。

"execution(* *(..))"

这里的execution就是一个函数,我们调用它,然后传递的参数是【* *(..)】。

Aspectj 使用
使用前,我们先了解一下execution和它的参数的匹配规则:
execution: 用于匹配方法执行的连接点;
execution(public * *(..)) ==> 匹配所有目标类的public方法,第一个*代表返回类型,第二个*代表方法名,而..代表任意入参的方法。
execution(* com.oysept.springboot.controller..*.*(..)) ==> 该包及所有子包下任何类的任何方法。
execution(* com.oysept.springboot.controller.*(..)) ==> 该包下任何类的任何方法。
execution(* com.oysept.springboot.controller.AspectJController.*(..)) ==> 该包下AspectJController类的任何方法。
execution(* com..*.*Controller.method*(..)) ==> 匹配包名前缀为com的任何包下类名后缀为Controller的方法,方法名必须以method为前缀。
execution(* *To(..)) ==> 匹配目标类所有以To为后缀的方法。
注: 该方法只是为了声明一个公共的环绕通知,也可以直接在具体方法配置,如: @Around("execution(* com.oysept.springboot.controller..*.*(..))")

@Before和@AfterReturning

然后我们编写一个aspect的基础使用代码,如下:
/**
 * @Before:定义了前置通知方法。打印出入参
 * @AfterReturning:定义了后置返回通知方法。打印出入参、返参
 */
@Slf4j
@Aspect
@Component
public class AopAspect_Basic {  
    @Before("execution(public * com.k.tender.controller.business.user.UserController.*(..))")
    public void doBefore(JoinPoint point){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("调用前连接点方法为:" + methodName + ",参数为:" + args);
    }

    @AfterReturning(value = "execution(public * com.k.tender.controller.business.user.UserController.*(..))", returning = "returnValue")
    public void doAfterReturning(JoinPoint point, Object returnValue){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("调用前连接点方法为:" + methodName + ",参数为:" + args + ",返回值为:" + returnValue);
    }

}

 如上代码,我们使用了@Before和@AfterReturning注解,在UserController调用前和后,分别埋了点,并输出了函数的入参和出参。

@Pointcut

@Pointcut其实是一个提取execution函数的操作,就是指定一个埋点,然后使用了@Before和@AfterReturning注解时,就不用每次都写那个execution函数了,这样就不用担心写错了。
代码示例如下:
  @Pointcut("execution(public * com.k.tender.controller.business.tender.TenderController.*(..))")
    public void doPointCut() {

    }

    @Before("doPointCut()")
    public void doBefore(JoinPoint point){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("调用前连接点方法为:" + methodName + ",参数为:" + args);
    }

    @AfterReturning(value = "doPointCut()", returning = "returnValue")
    public void doAfterReturning(JoinPoint point, Object returnValue){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("调用前连接点方法为:" + methodName + ",参数为:" + args + ",返回值为:" + returnValue);
    }

对注解埋点

有时候,我们希望编写一个注解,然后让有该注解的函数,都被拦截,那么就可以使用Aspectj的注解埋点模式。

代码如下: 

@Slf4j
@Aspect
@Component
public class AopAspect_Annotation {

    @Before("@annotation(com.k.tender.aop.MyAop)")
    public void doBefore(JoinPoint point){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("调用前连接点方法为:" + methodName + ",参数为:" + args);
    }
    @AfterReturning(value ="@annotation(com.k.tender.aop.MyAop)", returning = "returnValue")
    public void doAfterReturning(JoinPoint point, Object returnValue){
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("调用前连接点方法为:" + methodName + ",参数为:" + args + ",返回值为:" + returnValue);
    }
}

public @interface MyAop {
  String value() default "自定义注解拦截";
}

如果觉得写注解的命名空间麻烦,也可以这样写:

@Before("@annotation(apiOperation)")
    public void doBefore(JoinPoint point, MyAopAsyncTask apiOperation) {
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("调用前连接点方法为:" + methodName + ",参数为:" + args);
    }

还可以将注解和前面的excution函数结合写:

  @Before("execution(public * com.k..*.*(..)) && @annotation(apiOperation)") 
    public void doBefore(JoinPoint point, MyAopAsyncTask apiOperation) throws NoSuchMethodException {
        String methodName = point.getSignature().getName();
        List<Object> args = Arrays.asList(point.getArgs());
        log.info("调用前连接点方法为:" + methodName + ",参数为:" + args); 
    }

有时候我们的拦截会触发多次,这个具体原因调查起来很麻烦,我们也可以这样解决,代码如下:

 private volatile long hashcode = 0;//应对重复触发
    @Before("execution(public * com.k..*.*(..)) && @annotation(apiOperation)")

    public void doBefore(JoinPoint point, MyAopAsyncTask apiOperation) throws NoSuchMethodException {
        if (hashcode != point.getTarget().hashCode()) {
            log.info("========doBefore========");
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = requestAttributes.getRequest();
            String method = request.getMethod();
           
        }

    }

使用hashcode来过滤多次拦截。

 ----------------------------------------------------------------------------------------------------

 到此,Android里使用AspectJ实现AOP就介绍完了。

----------------------------------------------------------------------------------------------------

注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!
若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

https://www.cnblogs.com/kiba/p/18027435

 

243596-20190904083750507-629449790.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK