springboot切面AOP拦截父类或接口中标记注解的方法

springboot切面AOP拦截父类或接口中标记注解的方法,第1张

springboot切面AOP拦截父类或接口中标记注解方法 一、注解的继承性回顾
    被@Inherited元注解标注的注解标注在类上的时候,子类可以继承父类上的注解。注解未被@Inherited元注解标注的,该注解标注在类上时,子类不会继承父类上标注的注解。注解标注在接口上,其子类及子接口都不会继承该注解注解标注在类或接口方法上,其子类重写该方法不会继承父类或接口中方法上标记的注解

根据注解继承的特性,我们再做AOP切面拦截的时候会遇到拦截不到的问题,今天我们就讲解下对这些特殊情况如何解决,对源码不做过渡深入的讲解。

二、注解标注在父类、接口、父类方法、接口方法上如何通过子类拦截,首先了解下几个核心类

AnnotationMatchingPointcut切点类是用来判定是否需要拦截类、父类、实现接口中方法,其中一共四个构造函数如下:

	
public AnnotationMatchingPointcut(Class classAnnotationType) {
		this(classAnnotationType, false);
	}

	
	public AnnotationMatchingPointcut(Class classAnnotationType, boolean checkInherited) {
		this.classFilter = new AnnotationClassFilter(classAnnotationType, checkInherited);
		this.methodMatcher = MethodMatcher.TRUE;
	}

	
	public AnnotationMatchingPointcut(@Nullable Class classAnnotationType,
			@Nullable Class methodAnnotationType) {

		this(classAnnotationType, methodAnnotationType, false);
	}

	
	public AnnotationMatchingPointcut(@Nullable Class classAnnotationType,
			@Nullable Class methodAnnotationType, boolean checkInherited) {

		Assert.isTrue((classAnnotationType != null || methodAnnotationType != null),
				"Either Class annotation type or Method annotation type needs to be specified (or both)");

		if (classAnnotationType != null) {
			this.classFilter = new AnnotationClassFilter(classAnnotationType, checkInherited);
		}
		else {
			this.classFilter = new AnnotationCandidateClassFilter(methodAnnotationType);
		}

		if (methodAnnotationType != null) {
			this.methodMatcher = new AnnotationMethodMatcher(methodAnnotationType, checkInherited);
		}
		else {
			this.methodMatcher = MethodMatcher.TRUE;
		}
	}

以上四个构造函数,支持仅标记在类上注解、仅标记方法上的注解、即指定标记类上且标记在方法上的注解(是否拦截父类及接口上标记的方法)三种构造方式。

通过上述四个构造函数可以构造如下几种切点类型:

注解标注在当前类,只拦截当前类的方法注解标注在当前类的父类上,拦截父类及子类中的方法注解标注在当前类实现的接口上 ,拦截接口方法的实现方法注解标注在当前类的方法上,拦截当前类的方法注解标注在当前类父类或接口的方法上,拦截父类的方法或者当前类实现方法。

通过上述切点可以构造出我们需要的大多数场景,如果需要更灵活的实现还需要结合ComposablePointcut类,此类可以实现类级别标注的交集、并集,方法级别的交集、并集,切点级别的交集并集。

三、切点实现案例
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public Advisor mybatisLogAdvisor(MybatisProperties properties) {
        //限定类级别的切点
        Pointcut cpc = new AnnotationMatchingPointcut(Mapper.class, properties.isCheckClassInherited());
        //限定方法级别的切点
        Pointcut mpc = new AnnotationMatchingPointcut(null, Mapper.class, properties.isCheckMethodInherited());
        //组合切面(并集),一、ClassFilter只要有一个符合条件就返回true,二、
        Pointcut pointcut = new ComposablePointcut(cpc).union(mpc);
        //mybatis日志拦截切面
        MethodInterceptor interceptor = new MybatisMethodInterceptor();
        //切面增强类
        AnnotationPointcutAdvisor advisor = new AnnotationPointcutAdvisor(interceptor, pointcut);
        //切面优先级顺序
        advisor.setOrder(AopOrderInfo.MYBATIS);
        return advisor;
    }

此拦截器可以实现对标注了Mapper方法进行日志拦截,具体实现可以参考GitHub源码

四、上述向上查询父类、父接口及其方法的核心是AnnotatedElementUtils工具类,示例参考如下:

 //返回当前类或父类或接口方法上标注的注解对象
targetDataSource = AnnotatedElementUtils.findMergedAnnotation(method, TargetDataSource.class);
//返回当前类或父类或接口上标注的注解对象
targetDataSource = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), TargetDataSource.class);

GitHub地址:https://github.com/mingyang66/spring-parent

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5707513.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存