大数跨境
0
0

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

Spring 自定义Advisor以编程的方式实现AOP Spring全家桶实战案例
2021-06-27
0
导读: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实战案例锦集


【声明】内容源于网络
0
0
Spring全家桶实战案例
Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
内容 832
粉丝 0
Spring全家桶实战案例 Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
总阅读7
粉丝0
内容832