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

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

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;
	}
}
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;
	}
}
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();
	}
}

5、编写 Config 入口

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

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

}
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...");
	}
}

7、开始测试
	public static void main(String[] args) {

		ApplicationContext context = new AnnotationConfigApplicationContext(JavAspect.class);
		AopProxyOrderService bean = context.getBean(AopProxyOrderService.class);
		bean.crateOrder("小明");
	}

运行结果如下:

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

这里需要注意两个点:

  • 在 MethodMatcher 方法匹配过程中,传过来的 method 对象是接口的 method 对象,和具体实现了的 method 对象不是同一个,查看他们两个的 hashCode() 是不一样的,这点一定要注意,因为接口 method 没有任何注解修饰,所以判断不了注解存不存在。所以可以选择通过获取 targetClass 字节码文件获取具体实现类的 method、或者通过 AopUtils 工具类也可以(在上面的 MyDbMasterPointcut 代码中已经注释的非常清楚)
  • MyDbMasterPointcut 类中的 isRuntime() 开关要返回 true ,才会执行参数级别的匹配,否则不会。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魔道不误砍柴功

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值