当前位置:   article > 正文

自己定义 Advisor 实现自定义注解修饰的方法增强_java advisor

java advisor
需求:这里我们想通过自己定义 Advisor 来实现自定义注解修饰的方法增强,直接开干
1、先自定义注解
/**
 * 定义方法级别的注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBMasterAnno {
	String value() default "on";
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
2、实现 Advisor 的接口(实现其子类 PointcutAdvisor 即可)

Advisor 必然包含两个重要元素,Pointcut 和 Advice、其中 Pointcut 使用来匹配某些类是否能够被我的这个 Advisor 增强,而实际增强逻辑是在 Advice 中实现,而最有名的 Advice 子类就是 MethodInterceptor。

实现代码如下,里面包含了这两个重要的元素 Pointcut + Advice

@Component
public class MyAdvisor implements PointcutAdvisor {

	/**
	 * 匹配对象,专门用来匹配方法、类、参数是否需要被增强
	 */
	@Autowired
	private MyDbMasterPointcut myDbMasterPointcut;

	@Autowired
	private DbMasterAdvice dbMasterAdvice;

	@Override
	public Pointcut getPointcut() {
		return myDbMasterPointcut;
	}

	@Override
	public Advice getAdvice() {
		return dbMasterAdvice;
	}

	@Override
	public boolean isPerInstance() {
		return false;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
3、实现 Pointcut 接口

Pointcut 接口作用是匹配和过滤作用的,那么这种匹配一般会有三种:类、方法、参数。类的匹配过程交给了 ClassFilter 类匹配器、方法和参数的交给了 MethodMatcher 方法匹配器,如下代码直接实现了这两个接口,并且精确到了参数级别的校验。

这个 MyDbMasterPointcut 主要实现了功能:拦截被 @DBMasterAnno 注解修饰的方法、并且参数还必须要等于 “小明” 该方法才可以被 Advisor 增强,否则不增强。

/**
 * 这里不直接在这里实现 MethodMatcher,ClassFilter 接口也行的
 */
@Component
public class MyDbMasterPointcut implements Pointcut, MethodMatcher,ClassFilter {
	/**
	 * 这里是核心匹配过程,可能是个非常复杂的匹配过程
	 * 这个只能匹配到方法级别,也就是只能判断这个方法是否被什么修饰之类的
	 */
	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		/**
		 * 可以在这里拿到原始方法,判断这个方法上是否标注了注解才可以
		 * method 这个方法对象是接口上的,注意了,接口上没有任何的注解修饰,拿到死都拿不到
		 * 所以可以通过这个 AopUtils 工具类获取到实习类上的 method 对象
		 * 或者可以通过 targetClass 字节码文件获取到都可以,方法很多
		 */
		Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
		if (specificMethod.isAnnotationPresent(DBMasterAnno.class)) {
			System.out.println("匹配到了有 @DBMaster 修饰的方法,可以对其进行拦截操作");
			return true;
		}
		return false;
	}


	/**
	 * 这里是核心匹配过程,可能是个非常复杂的匹配过程
	 * 这个不仅可以匹配方法,还可以匹配参数级别
	 */
	@Override
	public boolean matches(Method method, Class<?> targetClass, Object... args) {
		System.out.println("-------> 方法匹配成功了,然后开始参数级别的 matches() ...");
		// 先检验方法是否符合要求
		if (method.getName().equalsIgnoreCase("crateOrder")) {
			// 然后再检验参数是否符合要求
			String name = (String) args[0];
			if ("小明".equalsIgnoreCase(name)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean isRuntime() {
		/**
		 * 这里返回 true 参数级别的校验方法才会执行
		 * 这里方法 false 下面就不会被执行,看源码就知道了
		 */
		return true;
	}


	@Override
	public ClassFilter getClassFilter() {
		/**
		 * 当前自己就是 ClassFilter 所以直接返回 this
		 * 但是由于我们的 @DBMaster 注解是对方法起作用的,所以这里就没必要对类进行校验操作了
		 * 或者没必要实现 ClassFilter 接口,直接在 getClassFilter() 方法上返回 ClassFilter.TRUE
		 */
		return this;
		//return ClassFilter.TRUE;
	}

	@Override
	public MethodMatcher getMethodMatcher() {
		return this;
	}

	/**
	 * 重写 ClassFilter 方法 对类的匹配
	 * 但是由于我们的 @DBMaster 注解是对方法起作用的,所以这里就没必要对类进行校验操作了
	 * 或者没必要实现 ClassFilter 接口,直接在 getClassFilter() 方法上返回 ClassFilter.TRUE
	 */
	@Override
	public boolean matches(Class<?> clazz) {
		return true;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
4、实现 Advice 接口

这里实现的是 Advice 子类 MethodInterceptor 接口,注意这里执行完拦截逻辑,记得往下传递,否则到这里就结束了,类似 SpringMVC 拦截器功能

@Component
public class DbMasterAdvice implements MethodInterceptor {
	@Nullable
	@Override
	public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
		System.out.println("DbMasterAdvice 被调用了,记得要火炬传递哦.....");
		return invocation.proceed();
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
5、编写 Config 入口

这里不用配置 @Aspect,因为我们不用 Spring 提供的 Advice 功能,都是用我们自定义的 Advice,可以更灵活的自己控制

@Configuration
@ComponentScan({"com.gwm.spring.pointcut"})
@EnableAspectJAutoProxy
public class AdviceConfig {

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
6、Service 实现

这里我们使用自定定义的注解 @DBMasterAnno 修饰 crateOrder() 方法,表示只有这个方法需要被 Advisor 增强。

public interface AopProxyOrderService {
	void crateOrder(String name);
}
@Service
public class AopProxyOrderServiceImpl implements AopProxyOrderService {

	/**
	 * 这里使用自己定义的注解
	 */
	@DBMasterAnno
	@Override
	public void crateOrder(String name) {
		System.out.println("我是目标方法 invoke crateOrder method...");
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
7、开始测试
	public static void main(String[] args) {

		ApplicationContext context = new AnnotationConfigApplicationContext(JavAspect.class);
		AopProxyOrderService bean = context.getBean(AopProxyOrderService.class);
		bean.crateOrder("小明");
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

运行结果如下:

DbMasterAdvice 被调用了,记得要火炬传递哦.....
我是目标方法 crateOrder,我被调用了...
  • 1
  • 2

这里需要注意两个点:

  • 在 MethodMatcher 方法匹配过程中,传过来的 method 对象是接口的 method 对象,和具体实现了的 method 对象不是同一个,查看他们两个的 hashCode() 是不一样的,这点一定要注意,因为接口 method 没有任何注解修饰,所以判断不了注解存不存在。所以可以选择通过获取 targetClass 字节码文件获取具体实现类的 method、或者通过 AopUtils 工具类也可以(在上面的 MyDbMasterPointcut 代码中已经注释的非常清楚)
  • MyDbMasterPointcut 类中的 isRuntime() 开关要返回 true ,才会执行参数级别的匹配,否则不会。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/86781
推荐阅读
相关标签
  

闽ICP备14008679号