环境: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注解的都进行拦截
@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(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实战案例锦