自定义advisor实现spring-aop

756 阅读5分钟

AOP 的基本概念

  • 切面(Aspect):
    一个关注点的模块化,这个关注点可能会横切多个对象。
    事务管理是 J2EE 应用中一个关于横切关注点的很好列子。
    在 Spring AOP 中,切面可以使用基于模式或者基于 @Aspect 注解的方式来实现。
  • 连接点(Joinpoint):
    在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。
    在 Spring AOP 中,一个连接点总是表示一个方法的执行。
  • 切入点(Pointcut):
    匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如:当执行某个特定名称的方法时)。
    切入点表达式如何和连接点匹配是 AOP 的核心:Spring 缺省使用 Aspect 切入点语法。
  • 引入(Introduction):
    用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration)) Spring 允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,可以使用引入来使一个 bean 实现 isModified 接口,以便简化缓存机制。
  • 目标对象(Target Object):
    被一个或者多个切面所通知的对象,也被称为通知(advised)对象。
    既然 Spring AOP 是通过运行时代理实现的。这个对象永远是一个被代理(proxied)对象。
  • AOP 代理(AOP Proxy):
    AOP 框架创建的对象,用来实现切面契约(例如,通知方法执行等等)。
    在 Spring 中,AOP 代理可以是 JDK 动态代理或者 CGLIB 代理。
  • 织入(Weaving):
    把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用 AspectJ 编译器),类加载时和运行时完成。Spring 和其他纯 Java AOP 框架一样,在运行时完成织入。
  • 通知(Advice):
    在切面的某个特定的连接点上执行的动作。其中包括“around”、“before” 和 “after” 等不同类型的通知。
    许多 AOP 框架(包括 Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。

自定义Advisor

首先必须知道几个对象:Advice,PointCut,MethodMatcher,Advisor的关系

Advisor(顾问)由Advice(通知)与PointCut(切点)组成,Advice(通知)用于执行具体需要增强的逻辑,切点用于设置那些方法需要增强,而PointCut(切点)是通过MethodMatcher对象筛选出那些类与方法需要增强。

  1. 定义注解
package com.iot.cloud.springaopdemo.annotation;

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

/**
 * @version: 1.00.00
 * @description:
 * @copyright: Copyright (c) 2018 立林科技 All Rights Reserved
 * @company: 厦门立林科技有限公司
 * @author: hj
 * @date: 2018-05-03 17:04
 * @history:
 */
@Target(ElementType.METHOD)//这个注解是应用在方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeLock {
    /**
     * 锁名称,支持{}格式
     * @return
     */
    String lockName();

}
  1. 定义advisor
package com.iot.cloud.springaopdemo.aspect;

import com.iot.cloud.springaopdemo.annotation.DistributeLock;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.stereotype.Component;

/**
 * @version: 1.00.00
 * @description: 自定义切面
 * @copyright: Copyright (c) 2022 立林科技 All Rights Reserved
 * @company: 厦门立林科技有限公司
 * @author: hj
 * @date: 2022-07-27 13:31
 */
@Component
public class MyAdvisor implements PointcutAdvisor {
    /**
     * 定义切点
     * @return
     */
    @Override
    public Pointcut getPointcut() {
//        //自定义切入点表达式
//        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
//        pointcut.setExpression("execution(* com.iot.cloud.springaopdemo.service.AopService.*(..))");
//        //基于method级别的注解
//        AnnotationMatchingPointcut annotationMatchingPointcut = AnnotationMatchingPointcut.forMethodAnnotation(DistributeLock.class);
//        //基于class级别的注解
//        AnnotationMatchingPointcut pointcut1 = AnnotationMatchingPointcut.forClassAnnotation(DistributeLock.class);
        return new MyPointCut();
    }

    /**
     * 定义通知
     * @return
     */
    @Override
    public Advice getAdvice() {
        return new MyBeforeAdvise();
    }

    @Override
    public boolean isPerInstance() {
        return true;
    }
}
  1. 定义切点
  • 通过spring自带的AnnotationMatchingPointcut或AspectJExpressionPointcut定义
  • 通过实现Pointcut进行个性化定义
package com.iot.cloud.springaopdemo.aspect;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;

/**
 * @version: 1.00.00
 * @description: 自定义pointCut
 * @copyright: Copyright (c) 2022 立林科技 All Rights Reserved
 * @company: 厦门立林科技有限公司
 * @author: hj
 * @date: 2022-07-27 13:45
 */
public class MyPointCut implements Pointcut {
    /**
     * 用于过滤类,先过滤类,如果类不满足,
     * 那么就不会判断方法是否匹配
     * @return
     */
    @Override
    public ClassFilter getClassFilter() {
        return ClassFilter.TRUE;
    }

    /**
     * 用于过滤方法,需要一个MyMethodMatcher用于方法逻辑判断
     * @return
     */
    @Override
    public MethodMatcher getMethodMatcher() {
        return new MyMethodMatcher();
    }
}

定义切点方法过滤规则

package com.iot.cloud.springaopdemo.aspect;

import com.iot.cloud.springaopdemo.annotation.DistributeLock;
import org.springframework.aop.MethodMatcher;

import java.lang.reflect.Method;

/**
 * @version: 1.00.00
 * @description: 方法匹配
 * @copyright: Copyright (c) 2022 立林科技 All Rights Reserved
 * @company: 厦门立林科技有限公司
 * @author: hj
 * @date: 2022-07-27 13:48
 */
public class MyMethodMatcher implements MethodMatcher {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        DistributeLock annotation = method.getAnnotation(DistributeLock.class);
        //判断方法是否使用了MyAnnotation注解。
        if (annotation != null) {
            return true;
        }
        return false;
    }

    @Override
    public boolean isRuntime() {
        return false;
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        return false;
    }
}
  1. 定义通知
package com.iot.cloud.springaopdemo.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;

import java.lang.reflect.Method;

/**
 * @version: 1.00.00
 * @description: 前置通知
 * @copyright: Copyright (c) 2022 立林科技 All Rights Reserved
 * @company: 厦门立林科技有限公司
 * @author: hj
 * @date: 2022-07-27 13:53
 */
@Slf4j
public class MyAdvise implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor, ThrowsAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        log.info("前置通知");
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        log.info("环绕前");
        Object proceed = invocation.proceed();
        log.info("环绕后");
        return proceed;
    }

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        log.info("后置通知");
    }

    public void afterThrowing(Method method, Object[] args, Object target, Exception e) {
        log.info("异常通知:" + e.getMessage());
    }
}

:

MethodBeforeAdvice: 前置通知

AfterReturningAdvice: 后置返回通知

MethodInterceptor:环绕通知

ThrowsAdvice:异常通知

  1. 测试
package com.iot.cloud.springaopdemo.handle;

import com.iot.cloud.springaopdemo.annotation.DistributeLock;
import org.springframework.stereotype.Component;

/**
 * @version: 1.00.00
 * @description: aop处理
 * @copyright: Copyright (c) 2022 立林科技 All Rights Reserved
 * @company: 厦门立林科技有限公司
 * @author: hj
 * @date: 2022-07-27 14:40
 */
@Component
public class AopHandle {
    @DistributeLock(lockName = "123")
    public int handle(String name) {
        return 15;
    }
}
package com.iot.cloud.springaopdemo.service.impl;

import com.iot.cloud.springaopdemo.handle.AopHandle;
import com.iot.cloud.springaopdemo.service.AopService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @Version: 1.0
 * @Description:
 * @copyright: Copyright (c) 2019 立林科技 All Rights Reserved
 * @company 厦门立林科技有限公司
 * @Author: hj
 * @date: 2022-07-27 14:14
 * @history:
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
class AopServiceImplTest {
    @Autowired
    private AopHandle aopHandle;
    @Test
    void test1() {
        int test = aopHandle.handle("123");
        log.info("test={}", test);
    }
}

测试结果 :

image.png 异常通知:

image.png