Spring 自定义Advisor以编程的方式实现AOP

2021/7/10 9:06:04

本文主要是介绍Spring 自定义Advisor以编程的方式实现AOP,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

环境:Spring5.3.3


Spring中是通过Advisor来创建代理对象的,如果当前Advisor将不会创建代理对象。不管是自定义的Advisor还是通过注解@Aspect实现的切面。我们粗略查看部分源码就知道了

代理对象的创建

1、开启AOP后会注册
AnnotationAwareAspectJAutoProxyCreator的BeanPostProcesses处理器。代理对象的创建都是通过他来完成的。当执行postProcessAfterInitialization方法的时候会进行Bean的代理创建

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
  if (bean != null) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    if (this.earlyProxyReferences.remove(cacheKey) != bean) {
      return wrapIfNecessary(bean, beanName, cacheKey);
    }
  }
  return bean;
}

2、执行wrapIfNecessary方法判断是否需要对当前的Bean进行包装

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
  // other code
  // Create proxy if we have advice.
  Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
  if (specificInterceptors != DO_NOT_PROXY) {
    this.advisedBeans.put(cacheKey, Boolean.TRUE);
    Object proxy = createProxy(
    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    this.proxyTypes.put(cacheKey, proxy.getClass());
    return proxy;
  }

  this.advisedBeans.put(cacheKey, Boolean.FALSE);
  return bean;
}

通过
getAdvicesAndAdvisorsForBean方法来查找当前容器中是否有Advisor,如果有才会创建代理对象。

3、查找当前容器中的所有Advisor(Bean)也可以是@Aspect注解方式的切面

protected Object[] getAdvicesAndAdvisorsForBean(
  Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

  List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
  if (advisors.isEmpty()) {
    return DO_NOT_PROXY;
  }
  return advisors.toArray();
}

findEligibleAdvisors方法就是查找合格的Advisor。

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
  List<Advisor> candidateAdvisors = findCandidateAdvisors();
  List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
  extendAdvisors(eligibleAdvisors);
  if (!eligibleAdvisors.isEmpty()) {
    eligibleAdvisors = sortAdvisors(eligibleAdvisors);
  }
  return eligibleAdvisors;
}

findCandidateAdvisors方法会进入
AnnotationAwareAspectJAutoProxyCreator类重写的方法中

protected List<Advisor> findCandidateAdvisors() {
  // Add all the Spring advisors found according to superclass rules.
  List<Advisor> advisors = super.findCandidateAdvisors();
  // Build Advisors for all AspectJ aspects in the bean factory.
  if (this.aspectJAdvisorsBuilder != null) {
    advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
  }
  return advisors;
}

根据上面的代码会分别查找Advisor的Bean和@Aspect注解的切面

4、查找当前容器中注册的Advisor


super.findCandidateAdvisors()方法

findCandidateAdvisors---》
BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans

public List<Advisor> findAdvisorBeans() {
  // Determine list of advisor bean names, if not cached already.
  String[] advisorNames = this.cachedAdvisorBeanNames;
  if (advisorNames == null) {
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let the auto-proxy creator apply to them!
    advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
      this.beanFactory, Advisor.class, true, false);
    this.cachedAdvisorBeanNames = advisorNames;
  }
  // other code
}

通过上面的代码知道了会查找当前容器中的所有Advisor。

5、查找@Aspect注解的切面

BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors()

public List<Advisor> buildAspectJAdvisors() {
  List<String> aspectNames = this.aspectBeanNames;
  if (this.advisorFactory.isAspect(beanType)) {
    // ...
    if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
      MetadataAwareAspectInstanceFactory factory =  
        new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
      List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
    }
    // ...
  }
}

在该方法中有isAspect的判断是否有@Aspect注解

@Override
public boolean isAspect(Class<?> clazz) {
  return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean hasAspectAnnotation(Class<?> clazz) {
  return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
}

在buildAspectJAdvisors方法中会将@Aspect注解类中的相应@Before,@Around等进行解析进行包装成Advisor。通过上面的
this.advisorFactory.getAdvisors这行代码能查看到查找@Before @Around注解的代码

通过上面的步骤就找到了系统中所有的Advisor(当然中间还有一步是对找到的Advisor进行判断是否能被应用;
AbstractAdvisorAutoProxyCreator.findEligibleAdvisors() {findAdvisorsThatCanApply()}),如果不为空就会根据Advisor进行创建代理Bean。

接下来通过2种方式来定义及应用Advisor。

这里我们实现这样的一个功能:所有Bean中的方法只要标有@DS注解的都进行拦截

直接自定义Advisor

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface DS {
}
// DAO接口
public interface CustomDAO {
  public void update() ;
}
// DAO接口实现类
@Component
public class CustomerDAOImpl implements CustomDAO {    
  @DS
  public void update() {
    System.out.println("更新数据...") ;
  }    
}
@Component
public class CustomAdvisor implements PointcutAdvisor {

  @Override
  public Advice getAdvice() {
    return new MethodInterceptor() {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
          System.out.println("我被调用了...") ;
          return invocation.proceed() ;
        }
    } ;
  }

  @Override
  public boolean isPerInstance() {
    return true ;
  }

  @Override
  public Pointcut getPointcut() {
    return new Pointcut() {
        @Override
        public MethodMatcher getMethodMatcher() {
          return new MethodMatcher() {
              @Override
              public boolean matches(Method method, Class<?> targetClass, Object... args) {
                return false;
              }
              @Override
              public boolean matches(Method method, Class<?> targetClass) {
                return method.isAnnotationPresent(DS.class) ;
              }
              @Override
              public boolean isRuntime() {
                return false ;
              }
          };
       }
            
        @Override
        public ClassFilter getClassFilter() {
          return ClassFilter.TRUE;
        }
     } ;
  }

}

如果你只是上面这样操作你运行后会发现方法根本没有拦截,对象是被代理了。原因可以查看我的这篇文章:《自定义Advisor失效问题》

原因:就是默认使用的JDK代理而使用的是JdkDynamicAopProxy InvocationHandler,在invoke方法中是动态获取当前执行的方法是否带有@DS注解。代理对象实现的是接口。指向的是接口中的update方法。

解决办法2个:

第一种:在接口方法上添加@DS注解

第二种:强制使用cglib代码

通过继承AbstractAutoProxyCreator

通过继承AbstractAutoProxyCreator(BeanPostProcesses处理器)我们可以又更多的选择控制。

@Component
public class PackScanner extends AbstractAutoProxyCreator {

  private static final long serialVersionUID = 1L;

  @Resource
  private Advisor customAdvisor ;
    
  @Override
  protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, 
    TargetSource customTargetSource) throws BeansException {
    return new Object[] {new CustomAdvisor()};
  }

  @Override
  protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    return !"customerDAOImpl".equals(beanName) ;
  }

  @Override
  public boolean isProxyTargetClass() {
    return super.isProxyTargetClass() ;
  }

}

shouldSkip可以在该方法中控制那些类直接跳过不进行代理。

完毕!!!

给个关注+转发谢谢

公众:Springboot实战案例锦



这篇关于Spring 自定义Advisor以编程的方式实现AOP的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程