网站建设与管理作业/品牌宣传策划方案
一、注解的继承性回顾
- 被@Inherited元注解标注的注解标注在类上的时候,子类可以继承父类上的注解。
- 注解未被@Inherited元注解标注的,该注解标注在类上时,子类不会继承父类上标注的注解。
- 注解标注在接口上,其子类及子接口都不会继承该注解
- 注解标注在类或接口方法上,其子类重写该方法不会继承父类或接口中方法上标记的注解
根据注解继承的特性,我们再做AOP切面拦截的时候会遇到拦截不到的问题,今天我们就讲解下对这些特殊情况如何解决,对源码不做过渡深入的讲解。
二、注解标注在父类、接口、父类方法、接口方法上如何通过子类拦截,首先了解下几个核心类
AnnotationMatchingPointcut切点类是用来判定是否需要拦截类、父类、实现接口中方法,其中一共四个构造函数如下:
/*** Create a new AnnotationMatchingPointcut for the given annotation type.* @param classAnnotationType the annotation type to look for at the class level*/
public AnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationType) {this(classAnnotationType, false);}/*** Create a new AnnotationMatchingPointcut for the given annotation type.* @param classAnnotationType the annotation type to look for at the class level* @param checkInherited whether to also check the superclasses and interfaces* as well as meta-annotations for the annotation type* @see AnnotationClassFilter#AnnotationClassFilter(Class, boolean)*/public AnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationType, boolean checkInherited) {this.classFilter = new AnnotationClassFilter(classAnnotationType, checkInherited);this.methodMatcher = MethodMatcher.TRUE;}/*** Create a new AnnotationMatchingPointcut for the given annotation types.* @param classAnnotationType the annotation type to look for at the class level* (can be {@code null})* @param methodAnnotationType the annotation type to look for at the method level* (can be {@code null})*/public AnnotationMatchingPointcut(@Nullable Class<? extends Annotation> classAnnotationType,@Nullable Class<? extends Annotation> methodAnnotationType) {this(classAnnotationType, methodAnnotationType, false);}/*** Create a new AnnotationMatchingPointcut for the given annotation types.* @param classAnnotationType the annotation type to look for at the class level* (can be {@code null})* @param methodAnnotationType the annotation type to look for at the method level* (can be {@code null})* @param checkInherited whether to also check the superclasses and interfaces* as well as meta-annotations for the annotation type* @since 5.0* @see AnnotationClassFilter#AnnotationClassFilter(Class, boolean)* @see AnnotationMethodMatcher#AnnotationMethodMatcher(Class, boolean)*/public AnnotationMatchingPointcut(@Nullable Class<? extends Annotation> classAnnotationType,@Nullable Class<? extends Annotation> 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