likes
comments
collection
share

spring自定义切面实现拦截类全方法

作者站长头像
站长
· 阅读数 66

前言

我先问一个spring aop是否生效问题,伪代码如下

// 父类
class P {
  
  public void pr(){
    print('is pr')
  }
  
}

// 子类
@compont
class S{
  
}

@compont
class D{
  @Autowired
  S s;
  
  void ss(){
    s.pr()
  }
}

你有啥办法能切到pr() 方法?

这里有朋友应该会说直接把父类的所有方法做个切面

这样能实现,但是不好

因为我只是想切S类的所有方法,包括其父类,甚至父类的父类

代码思路

本来我也是放弃了,这两天在学习事务注解源码的过程中我又找到了新的思路

BeanFactoryTransactionAttributeSourceAdvisor继承了AbstractPointcutAdvisor将切面逻辑注入到spring 中

TransactionAttributeSourcePointcut最主要就是判断bean在调用方法时切面是否符合

TransactionInterceptor是切面的主要运行逻辑

那么BeanFactoryTransactionAttributeSourceAdvisor 如何注入spring的,之前的文章没有讲,这里我补充下

spring自定义切面实现拦截类全方法

我们从org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
  // 所有切面增强器
  // 最终调用 BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
  // 所有符合条件的增强器
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
  // 扩展
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
      // 排序
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}
public List<Advisor> findAdvisorBeans() {
		// 缓存数据
		String[] advisorNames = this.cachedAdvisorBeanNames;
		if (advisorNames == null) {
			//  从当前容器中找所有 实现 Advisor 的类
			advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Advisor.class, true, false);
			this.cachedAdvisorBeanNames = advisorNames;
		}
		if (advisorNames.length == 0) {
			return new ArrayList<>();
		}

		List<Advisor> advisors = new ArrayList<>();
		for (String name : advisorNames) {
			if (isEligibleBean(name)) {
				if (this.beanFactory.isCurrentlyInCreation(name)) {
					....
				}
				else {
					try {
						advisors.add(this.beanFactory.getBean(name, Advisor.class));
					}
					catch (BeanCreationException ex) {
						.....
					}
				}
			}
		}
		return advisors;
	}

那么此时在看看BeanFactoryTransactionAttributeSourceAdvisor 定义,其实最终也是实现了Advisor接口

spring自定义切面实现拦截类全方法

而其中AbstractBeanFactoryPointcutAdvisor 这个实现了大部分方法,我们也只需要集成这个抽象类即可

代码实现

切面判断

@Slf4j
public class DbSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

	@Override
	public boolean matches(Method method, @Nullable Class<?> targetClass) {
		if (targetClass == null) {
			return false;
		}

		Class<?> declaringClass = method.getDeclaringClass();
		String superClassName = declaringClass.getName();
    if (!targetClass.getName().equals("com.impl.S")){
      // 不是S类的方法不进切面
      return false;
    }
			

		return superClassName.equals("com.impl.S")  || superClassName.equals("com.impl.P");
	}

}

拦截器

/**
 * 系统数据库操作处理切面
 */
@Slf4j
public class SysDbInterceptor implements MethodInterceptor, Serializable {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

      // 增强处理
        Object proceed;
        try {
            proceed = invocation.proceed();
        } finally {
            .......
        }

        return proceed;
    }

}

注入spiring

@Component
public class DbPointcutAdvisor extends AbstractBeanFactoryPointcutAdvisor {
 
    DbSourcePointcut pointcut = new DbSourcePointcut();

    public DbPointcutAdvisor() {
        setAdvice(new SysDbInterceptor());
    }

    public void setClassFilter(ClassFilter classFilter) {
        this.pointcut.setClassFilter(classFilter);
    }


    @Override
    public Pointcut getPointcut() {
        return pointcut;
    }
}
转载自:https://juejin.cn/post/7000240324140351525
评论
请登录