Spring源码系列:SpringAop中的NameMatchMethodPointcutAdvisor和RegexpMethodPointcutAdvisor

上一节我们讲到了SpringAop基于注解版本的织入和创建代理的流程。这一节我们来讲两个切面类。从前面知道 一个切面是有两部分构成 增强切点 。上节我们也列举了一些常用的切面 下面我们就来
介绍两个功能比较相近的切面类。

NameMatchMethodPointcutAdvisor的使用

启动配置类


@SpringBootApplication
public class App {
  public static void main(String[] args) {
    SpringApplication application = new SpringApplicationBuilder(App.class).application();
    ApplicationContext context = application.run(args);
    UserService userServiceImpl = (UserService)context.getBean("userServiceImpl");
    userServiceImpl.findUser();
    
  }
  @Configuration
  public static class AppConfig{
    /**
     * 定义切面
     * @return
     */
    @Bean
    public MethodBeforeAdvice methodBeforeAdvice(){
      return new com.coledy.aop.advice.MethodBeforeAdvice();
    }
    /**
     * 创建一个NameMatchMethodPointcutAdvisor 切面
     * @return
     */
    @Bean
    public NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor(){
      NameMatchMethodPointcutAdvisor matchMethodPointcutAdvisor = new NameMatchMethodPointcutAdvisor();
      matchMethodPointcutAdvisor.setAdvice(methodBeforeAdvice());
      matchMethodPointcutAdvisor.setMappedNames("find*");
      return matchMethodPointcutAdvisor;
    }
  }
}

被代理对象 和增强

public interface UserService {
  void findUser();
}

@Component
public class UserServiceImpl implements UserService {
  @Override
  public void findUser() {
    System.out.println("findUser....");
  }
}

public class MethodBeforeAdvice implements org.springframework.aop.MethodBeforeAdvice {
  @Override
  public void before(Method method, Object[] args, Object target) throws Throwable {
    System.out.println("我是一个前置增强 ");
  }
}

启动app 输出
我是一个前置增强
findUser…

可以看到到这里 我们看到了我们想要看到的结果。对象被代理了。值得注意的是NameMatchMethodPointcutAdvisor这个切面类 的setMappedNames 需要配置的是方法名字。支持通配符。


RegexpMethodPointcutAdvisor的使用

我们只在上面的例子的基础上加一个后置增强 和一个切面配置

@SpringBootApplication
//@Import(RegisterAspectJAutoProxyRegisterar.class)
public class App {

  public static void main(String[] args) {
    SpringApplication application = new SpringApplicationBuilder(App.class).application();
    ApplicationContext context = application.run(args);
    UserService userServiceImpl = (UserService)context.getBean("userServiceImpl");
    userServiceImpl.findUser();
  }
  @Configuration
  public static class AppConfig{
 
   @Bean
    public MethodBeforeAdvice methodBeforeAdvice(){
      return new com.coledy.aop.advice.MethodBeforeAdvice();
    }
    @Bean
    public RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor(){
      RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor = new RegexpMethodPointcutAdvisor();
      regexpMethodPointcutAdvisor.setPatterns("*.findGoods");
      regexpMethodPointcutAdvisor.setAdvice(methodBeforeAdvice());
      return regexpMethodPointcutAdvisor;
    }
  }
}

public class CommonMethodAfterAdvice implements AfterReturningAdvice {

  @Override
  public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    System.out.println("common after return");

  }
}

同样也达到了代理的目的。下面我们就看一下为什么我们只是简单的在容器中定义了一个切面 Spring就帮我们织入了被代理的对象里面。

SpringAop非注解式的源码分析

看过上一篇文章的应该有印象 getAdvicesAndAdvisorsForBean 这个方法是AbstractAutoProxyCreator类唯一一个留给子类必须实现的方法 (获取所有的切面由于不同子类的实现 获取切面的方式不同 所以这里交给了子类实现 模板方法。如BeanNameAutoProxyCreator就和我们经常使用的AnnotationAwareAspectJAutoProxyCreator 这个织入类的实现方式不一样)。

	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


	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 重写了这个方法
//但是在子类的方法有行代码很关键List<Advisor> advisors = super.findCandidateAdvisors();
//先回去父类获取Advisor 然后再用自己的逻辑获取 父类是获取实现了Advisor接口的切面 
//子类是获取有AspectJ注解的并且方法中有@Pointcut的方法 封装成切面。
	protected List<Advisor> findCandidateAdvisors() {
		Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
		return this.advisorRetrievalHelper.findAdvisorBeans();
	}

下面我们继续看看 父类获取Advisor的逻辑。其实和子类的获取逻辑很类似。父类的这个更简单因为本来就是Advisor不需要解析包装。

public List<Advisor> findAdvisorBeans() {
		String[] advisorNames = this.cachedAdvisorBeanNames;
		if (advisorNames == null) {
		    //获取beanFactory中实现了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)) {
   			     //判断当前bean是不是正在创建。 正在创建则不做任何处理。这里需要注意的是 即使当前正在
   			     //创建的bean是Advisor的子类 也不必担心 这个类无法加入到advisors中因为这个方法不止调用一次。每一个bean实例化的时候都会调用。
				if (this.beanFactory.isCurrentlyInCreation(name)) {
					if (logger.isTraceEnabled()) {
						logger.trace("Skipping currently created advisor '" + name + "'");
					}
				}
				else {
					try {
					   //这里如果没有创建就从beanFactory获取bean 如果没创建会创建 继而加入进缓存中
					   //bean的创建流程请参考https://blog.csdn.net/xiaomujiang_dafangzi/article/details/105183005
						advisors.add(this.beanFactory.getBean(name, Advisor.class));
					}
					catch (BeanCreationException ex) {
						Throwable rootCause = ex.getMostSpecificCause();
						if (rootCause instanceof BeanCurrentlyInCreationException) {
							BeanCreationException bce = (BeanCreationException) rootCause;
							String bceBeanName = bce.getBeanName();
							if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
								if (logger.isTraceEnabled()) {
									logger.trace("Skipping advisor '" + name +
											"' with dependency on currently created bean: " + ex.getMessage());
								}
								// Ignore: indicates a reference back to the bean we're trying to advise.
								// We want to find advisors other than the currently created bean itself.
								continue;
							}
						}
						throw ex;
					}
				}
			}
		}
		return advisors;
	}

到这里查找的过程就完了、、其实还是很简单的,就是如果有实现Advisor接口的类。至于 是如何织入到代理类中的请参考上一篇https://blog.csdn.net/xiaomujiang_dafangzi/article/details/105217189

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值