Spring 5.x 源码之旅-46AOP代理细节一wrapIfNecessary

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

postProcessAfterInitialization初始化之后进行代理

AOP的真正代理是在实例创建了,初始化之后进行代理,也就是在这个里面由AbstractAutoProxyCreator进的postProcessAfterInitialization方法进行处理:


首先查看是否在earlyProxyReferences里存在,也就是已经处理过了,不存在就考虑是否要包装,也就是代理。

    	@Override
    	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;
    	}

earlyProxyReferences是什么

这个其实就是前面讲过的为了解决单例的循环依赖,在实例化bean之后,需要把一个工厂方法加到singletonFactories集合里:


getEarlyBeanReference这个方法其实是处理器处理的,一般的处理器不做什么,直接返回,只是AOP的处理器会去做处理:

AbstractAutoProxyCreator的getEarlyBeanReference

就是放入集合里,然后判断要不要包装,其实就是在循环依赖注入属性的时候如果有AOP代理的话,也会进行代理,然后返回。

wrapIfNecessary代理

这个跟前面讲Aspect注解解析差不多,会先进行判断是否是已经处理过,是否需要跳过,跳过的话直接就放进advisedBeans里,表示不进行代理,但是这个bean处理过了,否则获取通知拦截器,然后才开始进行代理。

    	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
    			return bean;
    		}
    		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
    			return bean;
    		}
    		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
    			this.advisedBeans.put(cacheKey, Boolean.FALSE);
    			return bean;
    		}
    
    		// Create proxy if we have advice. 获取拦截器
    		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    		if (specificInterceptors != DO_NOT_PROXY) {
    			this.advisedBeans.put(cacheKey, Boolean.TRUE);//要代理的就添加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

其实就是检查前面切面解析是否有通知器advisors创建,有就返回,没有就是null

    	@Override
    	@Nullable
    	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,因为切面解析上篇讲过,在第一个自定义的bean实例化之前就解析出来了,所以这个时候一般就是直接从缓存里获取了。然后找出适用于beanClass的Advisor,然后进行扩展,会增加一个ExposeInvocationInterceptor的内部实例DefaultPointcutAdvisor,为了暴露AOP调用,以便于一些AspectJ类型的切点匹配,最后进行排序,这里的排序很重要,直接影响后面通知的方法调用顺序,然后返回。

    	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    		List<Advisor> candidateAdvisors = findCandidateAdvisors();//获取所有切面中的Advisor
    		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    		extendAdvisors(eligibleAdvisors);
    		if (!eligibleAdvisors.isEmpty()) {
    			eligibleAdvisors = sortAdvisors(eligibleAdvisors);//排序
    		}
    		return eligibleAdvisors;
    	}
findAdvisorsThatCanApply
    	protected List<Advisor> findAdvisorsThatCanApply(
    			List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
    
    		ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    		try {
    			return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    		}
    		finally {
    			ProxyCreationContext.setCurrentProxiedBeanName(null);
    		}
    	}
AopUtils的findAdvisorsThatCanApply

前面是先判断IntroductionAdvisor 类型的,这个是跟introduction功能相关的,会用到DeclareParents注解,说的就是把某一个包下的所有类都实现某个接口,具体的实行方法会用到另外一个实现类,可以把一个类变成一个接口类型的,而且还实现了,不需要改动原来的类,很神奇,当然我们暂时不说这个。我们说一般的。一般的就是遍历每一个Advisor ,然后判断是否可以应用到目标类clazz上,可以的话就加入候选列表。

    	public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    		if (candidateAdvisors.isEmpty()) {
    			return candidateAdvisors;
    		}
    		List<Advisor> eligibleAdvisors = new ArrayList<>();
    		for (Advisor candidate : candidateAdvisors) {
    			if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
    				eligibleAdvisors.add(candidate);
    			}
    		}
    		boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    		for (Advisor candidate : candidateAdvisors) {
    			if (candidate instanceof IntroductionAdvisor) {
    				// already processed
    				continue;
    			}
    			if (canApply(candidate, clazz, hasIntroductions)) {
    				eligibleAdvisors.add(candidate);
    			}
    		}
    		return eligibleAdvisors;
    	}
AopUtils的canApply是否可以应用

其实能不能用,切点上都定义了呀,所以这里就只要拿出切点来匹配下就可以啦,确实他也是这么做的,具体内部的代码涉及到aspectj,有兴趣的去看吧,现在知道大致流程就好了,就是我切点知道要在什么包下什么类,什么方法上用通知,所以你传进来的类,我都要检查一下,看advisor能不能用:

    	public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    		if (advisor instanceof IntroductionAdvisor) {
    			return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    		}
    		else if (advisor instanceof PointcutAdvisor) {
    			PointcutAdvisor pca = (PointcutAdvisor) advisor;
    			return canApply(pca.getPointcut(), targetClass, hasIntroductions);//是否匹配切点表达式信息
    		}
    		else {
    			
    			return true;
    		}
    	}
    
    
    	public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    		Assert.notNull(pc, "Pointcut must not be null");
    		if (!pc.getClassFilter().matches(targetClass)) {//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)) {//不是JDK的代理类
    			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;
    	}

获取的结果:

好了,获取通知器基本讲完了,其实就是去匹配你传入的类和切点表达式中的定义,符合的话就会返回相应的通知器,准备进行代理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值