spring源码之切面收集

AOP中有三个重要的概念:advisor(切面)、切点(pointcut)、advice(通知)
spring会对有切面的类生成代理

@EnableAspectJAutoProxy开启切面功能

图一
AspectJAutoProxyRegistrar中注册aop的入口类
在这里插入图片描述
在这里插入图片描述

上图将AnnotationAwareAspectJAutoProxyCreator注册到BeanDefinitionRegistry中,看继承关系如图所示:
在这里插入图片描述

所以AnnotationAwareAspectJAutoProxyCreator继承了AbstractAutoProxyCreator。在进行属性注入完成之后,会走到initializeBean方法,这是aop的入口在这里插入图片描述

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
	.....
	//这里不是合成的
	if (mbd == null || !mbd.isSynthetic()) {
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	}
	.....
	}

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
		throws BeansException {

	Object result = existingBean;
	for (BeanPostProcessor processor : getBeanPostProcessors()) {
		Object current = processor.postProcessAfterInitialization(result, beanName);
		if (current == null) {
			return result;
		}
		result = current;
	}
	return result;
}

获取BeanPostProcessor ,并循环调用postProcessAfterInitialization方法,通过继承关系知道AnnotationAwareAspectJAutoProxyCreator继承了AbstractAutoProxyCreator,所以这里会调到AbstractAutoProxyCreator的postProcessAfterInitialization方法,如图所示:

在这里插入图片描述

在这里插入图片描述

找切面的过程:

看getAdvicesAndAdvisorsForBean方法,找到合适的切面

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();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
	//寻找有@Aspectj注解,把所有有这个注解的类封装成Advisor返回
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
	//判断候选的切面是否作用在当前beanClass上面,匹配判断
	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
	//对有@Aspect注解切面添加了一个默认的切面 DefaultPointcutAdvisor
	extendAdvisors(eligibleAdvisors);
	if (!eligibleAdvisors.isEmpty()) {
		//对有@Order@Priority进行排序
		eligibleAdvisors = sortAdvisors(eligibleAdvisors);
	}
	return eligibleAdvisors;
}

寻找合格切面的过程:
1、寻找有@Aspectj注解的类,把所有有这个注解的类封装成Advisor返回
2、判断候选的切面是否作用在当前beanClass上面
3、对有@Aspect注解切面添加了一个默认的切面 DefaultPointcutAdvisor
4、对切面进行排序


看寻找合格切面过程的第一步findCandidateAdvisors方法
在这里插入图片描述
上图中第一步找自定义advisor过程比较简单,这里不赘述,看收集有@Aspect注解的buildAspectJAdvisors方法

public List<Advisor> buildAspectJAdvisors() {
		List<String> aspectNames = this.aspectBeanNames;

		if (aspectNames == null) {
			synchronized (this) {
				aspectNames = this.aspectBeanNames;
				if (aspectNames == null) {
					.....
					//获取spring容器中的所有bean的名称BeanName
					String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
							this.beanFactory, Object.class, true, false);
					for (String beanName : beanNames) {
						.....
						//判断类上是否有@Aspect注解
						if (this.advisorFactory.isAspect(beanType)) {
							aspectNames.add(beanName);
							AspectMetadata amd = new AspectMetadata(beanType, beanName);
							if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {

								//创建获取有@Aspect注解类的实例工厂,负责获取有@Aspect注解类的实例
								MetadataAwareAspectInstanceFactory factory =
										new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);

								//创建切面advisor对象
								List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
								.....
								advisors.addAll(classAdvisors);
							}
							.....
						}
					}
					this.aspectBeanNames = aspectNames;
					return advisors;
				}
			}
		}

		.....
		return advisors;
	}

上面代码中创建获取有@Aspect注解类的实例工厂,负责获取有@Aspect注解类的实例。
看这个方法具体的实现:
看这个this.advisorFactory.getAdvisors(factory)方法
在这里插入图片描述
上图中循环的是类中没有pointcut的方法,也就是advice方法,将advice方法传入构造advisor切面,看上图的构造切面的getAdvisor方法
在这里插入图片描述
看上图获取pointcut对象的方法
在这里插入图片描述
至此,pointcut创建好了,由之前传过来的advice方法构造完成advisor切面,如图所示:在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在getAdvice方法中会获取advice方法上面的注解,根据注解的不同,构造不同的advice
在这里插入图片描述
至此,切面中两个必备的元素pointcut和advice都已经具备,一个advisor切面就创建好了。

创建完所有的切面后,接下来过滤出匹配的切面,如图所示:
在这里插入图片描述
在这里插入图片描述
会调到canApply方法中进行类和方法的匹配:

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
		Assert.notNull(pc, "Pointcut must not be null");
		//调用ClassFilter的matches方法,判断类是否匹配
		if (!pc.getClassFilter().matches(targetClass)) {
			return false;
		}

		MethodMatcher methodMatcher = pc.getMethodMatcher();
		if (methodMatcher == MethodMatcher.TRUE) {
			// No need to iterate the methods if we're matching any method anyway...
			return true;
		}

		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
		if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
			introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
		}

		Set<Class<?>> classes = new LinkedHashSet<>();
		if (!Proxy.isProxyClass(targetClass)) {
			classes.add(ClassUtils.getUserClass(targetClass));
		}
		classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

		//判断类中方法是否匹配,,有些可能是方法上面有注解的拦截,所以需要判断方法是否匹配
		for (Class<?> clazz : classes) {
			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
			for (Method method : methods) {
				if (introductionAwareMethodMatcher != null ?
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
						methodMatcher.matches(method, targetClass)) {
					return true;
				}
			}
		}

		return false;
	}

过滤出匹配的切面之后,接下来针对@Aspect注解切面添加了一个默认的切面 DefaultPointcutAdvisor。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后对所有匹配到的advisor进行排序后返回。

如果返回的advisor数组不为空,则生成代理对象。
在这里插入图片描述

切面收集总结:

  1. 寻找有@Aspect注解,把所有有这个注解的类封装成Advisor返回。

    1)找到自定义的advisor,即实现了Advisor接口的
    2)找到有@Aspect注解的类,生成切面
    	构造切面:循环没有@PoinitCut注解的方法,也就是advice方法
    		构造pointcut对象,从advice方法的注解中拿到表达式,
    		并设置到pointcut对象中
    		构造advice对象,根据advice方法上不同的注解类型,构造不同的advice
    
  2. 判断候选的切面是否作用在当前beanClass上面,匹配判断,需要类和方法都匹配。

    循环找到的所有切面,判断当前beanClass上是否有切面,先进行类的匹配
    	1)类匹配
     		在进行类匹配的时候,
     		根据advice中的表达式,找到@PointCut注解的方法,
     		然后将pointcut对象中的表达式的值替换成@PointCut注解中的表达式,
     		然后判断当前beanClass是否在表达式中
    	2)方法匹配
    
  3. 对有@Aspect注解切面添加了一个默认的切面 DefaultPointcutAdvisor

  4. 对切面进行排序

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP中,可以使用切面编程来实现动态数据源的切换。下面是一个简单的示例: 首先,创建一个注解`DataSource`,用于标识需要切换数据源的方法: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DataSource { String value() default "default"; } ``` 然后,创建一个切面`DataSourceAspect`,在该中定义切点和切面逻辑: ```java @Aspect @Component public class DataSourceAspect { @Around("@annotation(dataSource)") public Object switchDataSource(ProceedingJoinPoint joinPoint, DataSource dataSource) throws Throwable { try { // 获取要切换的数据源名称 String dataSourceName = dataSource.value(); // 根据数据源名称切换数据源 switchDataSource(dataSourceName); // 执行目标方法 return joinPoint.proceed(); } finally { // 切换回默认数据源 switchDataSource("default"); } } // 实际切换数据源的逻辑 private void switchDataSource(String dataSourceName) { // 根据传入的数据源名称进行数据源切换逻辑的实现 // ... } } ``` 在上述代码中,`@Around("@annotation(dataSource)")`表示拦截带有`@DataSource`注解的方法,并执行切面逻辑。在切面逻辑中,首先获取切换的数据源名称,然后根据该名称进行数据源的切换操作。在目标方法执行完毕后,切面逻辑会将数据源切换回默认的数据源。 最后,使用`@DataSource`注解标识需要切换数据源的方法: ```java @Service public class UserService { @DataSource("db1") public void addUser(User user) { // 执行添加用户的逻辑 } @DataSource("db2") public void updateUser(User user) { // 执行更新用户的逻辑 } } ``` 在上述示例中,`addUser`方法使用名为"db1"的数据源,`updateUser`方法使用名为"db2"的数据源。 通过以上步骤,就可以使用Spring AOP实现动态数据源的切换。当调用带有`@DataSource`注解的方法时,切面会根据注解中指定的数据源名称进行数据源切换,从而实现动态切换数据源的效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值