Spring源码核心剖析 | 京东云技术团队

news2024/10/1 1:20:45

前言

SpringAOP作为Spring最核心的能力之一,其重要性不言而喻。然后需要知道的是AOP并不只是Spring特有的功能,而是一种思想,一种通用的功能。而SpringAOP只是在AOP的基础上将能力集成到SpringIOC中,使其作为bean的一种,从而我们能够很方便的进行使用。

一、SpringAOP的使用方式

1.1 使用场景

当我们在日常业务开发中,例如有些功能模块是通用的(日志、权限等),或者我们需要在某些功能前后去做一些增强,例如在某些方法执行后发送一条mq消息等。

如果我们将这些通用模块代码与业务代码放在一块,那么每个业务代码都要写这些通用模块,维护成本与耦合情况都十分严重。

因此,我们可以将此模块抽象出来,就有了”切面“的概念。

1.2 常用方式

AOP的使用方式相对比较简单,首先我们需要完成业务代码

@Service
public class AopDemo implements AopInterface{

    public Student start(String name) {
        System.out.println("执行业务逻辑代码.....");
        return new Student(name);
    }

}

业务逻辑比较简单,接收一个name参数。

接下来我们需要创建其对应的切面

//将该切面加入spring容器
@Service
//声明该类为一个切面
@Aspect
class AopAspect {

    //声明要进行代理的方法
    @Pointcut("execution(* com.example.demo.aop.AopInterface.start(..))")
    public void startAspect() {
    }

    //在方法执行之前的逻辑
    @Before(value = "startAspect()")
    public void beforeAspect() {
        System.out.println("业务逻辑前代码.....");
    }

    //在方法执行之后的逻辑
    @After(value = "startAspect()")
    public void afterAspect() {
        System.out.println("业务逻辑后代码.....");
    }

    //围绕方法前后的逻辑
    @Around("startAspect()")
    public Object aroundAspect(ProceedingJoinPoint point) throws Throwable {
        Object[] requestParams = point.getArgs();
        String name = requestParams[0].toString();
        System.out.println("传入参数:" + name);
        requestParams[0] = "bob";
        return point.proceed(requestParams);
    }

}

可以看到,首先需要我们指明要代理的对象及方法,然后根据需要选择不同的注解即可实现代理对象。

传入参数:tom
业务逻辑前代码.....
执行业务逻辑代码.....
业务逻辑后代码.....

二、SpringAOP源码解析

2.1 被代理对象的开始initializeBean

根据上面的使用情况,我们知道只需要声明对应的注解即可,不需要其他额外的配置,然后我们获得的bean对象就已经是被代理的了,那么我们可以推断代理对象的过程一定是发生在bean创建的过程的。

我们回顾一下创建bean的流程

  1. 实例化bean
  2. 装配属性
  3. 初始化bean

只有第三步初始化bean的时候才会有机会进行代理。

找到对应的代码位置:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
      //前置处理器
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }
	 //...
   try {
      //对象的初始化方法
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   if (mbd == null || !mbd.isSynthetic()) {
      //后置处理器,AOP开始的地方
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }

   return wrappedBean;
}

2.2 后置处理器applyBeanPostProcessorsAfterInitialization

后置处理器会执行那些实现了后置处理器接口的代码:

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

而AOP的后置处理器就是其中的一个: AbstractAutoProxyCreator

其对应的方法为(以下代码不为同一个类,而是对应的执行顺序):

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

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		// 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;
}

protected Object createProxy(Class beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		//获取advisors
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// Use original ClassLoader if bean class not locally loaded in overriding class loader
		ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
			classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
		}
    //通过代理工厂创建代理对象
		return proxyFactory.getProxy(classLoader);
}

public Object getProxy(@Nullable ClassLoader classLoader) {
    //首先获取对应的代理
		return createAopProxy().getProxy(classLoader);
}

//该方法根据要被代理的类选择使用jdk代理还是cglib代理
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (!NativeDetector.inNativeImage() &&
				(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
			Class targetClass = config.getTargetClass();
      //如果被代理的类是一个接口则使用jdk代理
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) 			{
				return new JdkDynamicAopProxy(config);
			}
      //否则使用cglib代理
			return new ObjenesisCglibAopProxy(config);
		}
		else {
      //根据配置选择强制使用jdk代理
			return new JdkDynamicAopProxy(config);
		}
}

我们知道,代理方式有jdk动态代理与cglib动态代理两种方式,而我们一个bean使用那种代理方式则由上述的方法决定。

至此,我们已经确定了使用那种代理方式获取代理对象。

2.3 获取代理对象

从上文中,我们已经确定了选用何种方式构建代理对象。接下来就是通过不同的方式是如何获取代理对象的。

看懂本章需要实现了解jdk动态代理或者cglib动态代理的方式。

2.3.1 JDK代理

首先在获取代理对象时选择 JdkDynamicAopProxy

public Object getProxy(@Nullable ClassLoader classLoader) {
   if (logger.isTraceEnabled()) {
      logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
   }
   //这里通过反射创建代理对象
   return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}

当被代理对象执行被代理的方法时,会进入到此方法。(jdk动态代理的概念)

JDK通过反射创建对象,效率上来说相对低一些。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

		try {
			// 获取被代理对象的所有切入点
			List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// 如果调用链路为空说明没有需要执行的切入点,直接执行对应的方法即可
			if (chain.isEmpty()) {
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// 如果有切入点的话则按照切入点顺序开始执行
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				retVal = invocation.proceed();
			}
			
			return retVal;
		}
}

invocation.proceed();这个方法就是通过递归的方式执行所有的调用链路。

public Object proceed() throws Throwable {
   // We start with an index of -1 and increment early.
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }

   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      Class targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
      if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
         // 继续执行
         return proceed();
      }
   }
   else {
      // 如果调用链路还持续的话,下一个方法仍会调用proceed()
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

2.3.2 cglib代理

public Object getProxy(@Nullable ClassLoader classLoader) {

   try {
      //配置CGLIB Enhancer...
      Enhancer enhancer = createEnhancer();
      if (classLoader != null) {
         enhancer.setClassLoader(classLoader);
         if (classLoader instanceof SmartClassLoader &&
               ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
            enhancer.setUseCache(false);
         }
      }
      enhancer.setSuperclass(proxySuperClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

      //1.获取回调函数,对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法
      Callback[] callbacks = getCallbacks(rootClass);
      Class[] types = new Class[callbacks.length];
      for (int x = 0; x < types.length; x++) {
         types[x] = callbacks[x].getClass();
      }
      // fixedInterceptorMap only populated at this point, after getCallbacks call above
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
            this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);

      //2.创建代理对象
      return createProxyClassAndInstance(enhancer, callbacks);
   }
   catch (CodeGenerationException | IllegalArgumentException ex) {
      throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
            ": Common causes of this problem include using a final class or a non-visible class",
            ex);
   }
   catch (Throwable ex) {
      // TargetSource.getTarget() failed
      throw new AopConfigException("Unexpected AOP exception", ex);
   }
}

可以看到我们在创建代理对象前会先获取代理对象的所有回调函数:

首先可以看到我们一共有7个回调方法,其中第一个为AOP相关的方法,其他的为spring相关。

在第一个对调对象中持有的 advised 对象中有 advisors 属性,就是对应我们的代理类中四个切片,@Before等等。

然后我们看一下 createProxyClassAndInstance()都做了什么。

//CglibAopProxy类的创建代理对象方法
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
   enhancer.setInterceptDuringConstruction(false);
   enhancer.setCallbacks(callbacks);
   return (this.constructorArgs != null && this.constructorArgTypes != null ?
         enhancer.create(this.constructorArgTypes, this.constructorArgs) :
         enhancer.create());
}

//ObjenesisCglibAopProxy继承了CglibAopProxy类,并覆写了其方法
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
		Class proxyClass = enhancer.createClass();
		Object proxyInstance = null;

    //1.尝试使用objenesis创建对象
		if (objenesis.isWorthTrying()) {
			try {
				proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
			}
			catch (Throwable ex) {
				logger.debug("Unable to instantiate proxy using Objenesis, " +
						"falling back to regular proxy construction", ex);
			}
		}

    //2.根据commit的提交记录发现,objenesis有可能创建对象失败,如果失败的话则选用放射的方式创建对象
		if (proxyInstance == null) {
			// Regular instantiation via default constructor...
			try {
				Constructor ctor = (this.constructorArgs != null ?
						proxyClass.getDeclaredConstructor(this.constructorArgTypes) :
						proxyClass.getDeclaredConstructor());
				ReflectionUtils.makeAccessible(ctor);
				proxyInstance = (this.constructorArgs != null ?
						ctor.newInstance(this.constructorArgs) : ctor.newInstance());
			}
			catch (Throwable ex) {
				throw new AopConfigException("Unable to instantiate proxy using Objenesis, " +
						"and regular proxy instantiation via default constructor fails as well", ex);
			}
		}

    //
		((Factory) proxyInstance).setCallbacks(callbacks);
		return proxyInstance;
	}

2.3.3 cglib

此处有个遇到的问题,当我在debug的时候,发现怎么都进不去 createProxyClassAndInstance(),百思不得其解,然后看到IDEA旁边有一个向下的箭头,代表该方法可能其子类被覆写了。然后在其子类处打断点果然发现是其子类的实现。

此处在2.2中也可看到:

可以看到返回的是其子类的对象,而不是CglibAopProxy本身的对象。

作者:京东科技 韩国凯

来源:京东云开发者社区

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/670213.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法程序设计 之 循环赛日程表(2/8)

一、实验目的&#xff1a; 理解并掌握分治算法的基本思想和设计步骤。 二、实验内容 设有n个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表&#xff1a; &#xff08;1&#xff09;每个选手必须与其他n-1个选手各赛一次&#xff1b; &#xff08;2&#xff0…

HOOPS Web SDK 2023 Crack

在 HOOPS WEB 平台上释放 3D 的力量 HOOPS Web 平台加速 Web 应用程序开发&#xff0c;提供先进的 3D Web 可视化、准确快速的 CAD 数据访问和 3D 数据发布软件开发工具包 &#xff08;SDK&#xff09;。 构建 3D WEB 应用程序 借助 HOOPS Web 平台&#xff0c;快速构建适用于…

值得收藏的 10个 Android 手机恢复丢失文件的工具榜单

尽管我们尽可能避免这种情况&#xff0c;但有时我们还是会不小心删除 Android 设备上的重要文件。无论是照片、视频、文档还是任何其他形式的数据&#xff0c;数据丢失都会带来巨大的痛苦。不幸的是&#xff0c;Android 设备没有内置恢复工具。但是&#xff0c;有一些第三方恢复…

里程碑式突破!关键的薛定谔猫编码能带来更好的量子比特

​ 薛定谔的猫编码插图&#xff08;图片来源&#xff1a;网络&#xff09; 来自瑞士洛桑联邦理工学院&#xff08;EPFL&#xff09;的科学家提出了一种突破性的量子计算容错方案&#xff0c;称为“关键的薛定谔猫编码”。这种新颖的系统在混合状态下运行&#xff0c;具有强大的…

容灾与备份区别、灾备技术、容灾体系规划

1.容灾备份的区别 容灾 &#xff08;Disaster Tolerance&#xff09;&#xff1a;就是在上述的灾难发生时&#xff0c;在保证生产系统的数据尽量少丢失的情况下&#xff0c;保持生存系统的业务不间断地运行。 容错 &#xff08;Fault Tolerance&#xff09;&#xff1a;指在计…

激光显示技术路线之争:超级全色激光技术ALPD5.0更先进

5月以来,智能投影市场爆发的激光显示技术路线之争愈演愈烈,各厂家带领自有的技术路线你方唱罢我登场,犹如一出愈演愈烈的大戏,吸引了业内外各界的目光。 从极米在5月10日2023春季新品发布会上率先向三色激光技术发难,再到坚果投影首席产品官在朋友圈发文炮轰极米的技术路线,随…

MarkDown常用功能

快捷键 撤销&#xff1a;Ctrl/Command Z 重做&#xff1a;Ctrl/Command Y 加粗&#xff1a;Ctrl/Command B 斜体&#xff1a;Ctrl/Command I 标题&#xff1a;Ctrl/Command Shift H 无序列表&#xff1a;Ctrl/Command Shift U 有序列表&#xff1a;Ctrl/Command Shif…

【二叉树part01】| 二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代遍历

目录 ✿二叉树的递归遍历❀ ☞LeetCode144.前序遍历 ☞LeetCode145.二叉树的后序遍历 ☞LeetCode94.二叉树的中序遍历 ✿二叉树的迭代遍历❀ ☞LeetCode144.前序遍历 ☞LeetCode145.二叉树的后序遍历 ☞LeetCode94.二叉树的中序遍历 ✿二叉树的统一迭代遍历❀ ☞Lee…

CTFshow-pwn入门-前置基础pwn32-pwn34

FORTIFY_SOURCE FORTIFY_SOURCE(源码增强)&#xff0c;这个其实有点类似与Windows中用新版Visual Studio进行开发的时候&#xff0c;当你用一些危险函数比如strcpy、sprintf、strcat&#xff0c;编译器会提示你用xx_s加强版函数。 FORTIFY_SOURCE本质上一种检查和替换机制&am…

算法程序设计 之 矩阵连乘(3/8)

一、实验目的&#xff1a; 理解动态规划算法的基本思想和设计步骤&#xff1b; 掌握动态规划算法的典型应用范例——矩阵连乘。 二、实验内容 矩阵连乘 给定n个可乘的数字矩阵A1,…,An&#xff0c;以及矩阵的阶p0* p1, p1* p2,…, pn-1* pn,求给定矩阵链的最优计算次序使得所需…

JavaWeb之文件的上传和下载

文章目录 文件的上传基本介绍文件上传的HTTP协议的说明commons-fileupload.jar 常用API介绍说明fileupload类库的使用 文件的下载基本介绍和使用说明中文名乱码问题解决方案 文件的上传和下载&#xff0c;是非常常见的功能。很多的系统中&#xff0c;或者软件中都经常使用文件的…

使用spacy做分词的示例

下载数据&#xff1a; aws s3 cp s3://applied-nlp-book/data/ data --recursive --no-sign-request aws s3 cp s3://applied-nlp-book/models/ag_dataset/ models/ag_dataset --recursive --no-sign-request 上面第一份数据接近1GB&#xff0c;第二份接近3GB&#xff1b; 示…

买了一年CSDN年VIP,用着实在太爽

买一年CSDN的年VIP有多爽及使用攻略&#xff01; 一、前言 这段时间&#xff0c;一旦打开CSDN就不断的弹出618活动&#xff0c;在电脑网上打开&#xff0c;一股白嫖之的气息吹来&#xff0c;让人直接忍不住剁手 最后经过近5天的挣扎&#xff0c;我还是受不了CSDN的蛊惑&#…

【工具分享】批量多目录图片如何转换PDF,一次性转换多级目录批量的PDF的转换,合并,输出另存等问题

在工作中我们经常要对图片进行批量转换PDF&#xff0c;由于文件量比较多&#xff0c;目录比较深&#xff0c;工作量比较大比较耗时费力&#xff0c;今天我们分享的主要解决以下问题&#xff1a; 1、单张图片的转换PDF&#xff1a;一张图临时转一下 2、多张图片转换成PDF&…

(二叉树) 1382. 将二叉搜索树变平衡 ——【Leetcode每日一题】

❓1382. 将二叉搜索树变平衡 难度&#xff1a;中等 给你一棵二叉搜索树&#xff0c;请你返回一棵 平衡后 的二叉搜索树&#xff0c;新生成的树应该与原来的树有着相同的节点值。如果有多种构造方法&#xff0c;请你返回任意一种。 如果一棵二叉搜索树中&#xff0c;每个节点…

whistle 使用介绍

什么是 whistle 来自 whistle 官网&#xff1a;http://wproxy.org/whistle/ 的介绍&#xff1a; whistle(读音[ˈwɪsəl]&#xff0c;拼音[wēisǒu])基于Node实现的跨平台web调试代理工具&#xff0c;类似的工具有Windows平台上的Fiddler&#xff0c;主要用于查看、修改HTTP…

在 Python 中对日期和时间进行排序

文章目录 在 Python 中对日期和时间进行排序Python 中的日期时间模块sorted() 方法 使用 sorted() 方法对日期进行排序使用 sorted() 方法对时间进行排序使用 sorted() 方法对日期和时间进行排序总结 Python 是全世界程序员都在使用的一种高级解释型编程语言。 它最著名的是面向…

C++指针对象和异常(10)

异常(exception) 为什么有异常 异常在C用于错误处理&#xff0c;C语言中一般使用返回值表示错误&#xff0c;C对错误处理进行了扩展&#xff0c;统一使用异常机制来处理程序中发生的错误。 C的异常处理包括两个部分 ----- 抛出异常和捕获异常&#xff0c;如果抛出的异常被捕…

BW生成HANA视图权限配置

目录 1 操作步骤1.1 SAP HANA端1、创建用户2、常规信息3、配置角色4、配置系统权限5、配置对象权限 1.2 BW端1、SM30配置数据库连接参数2、SU01创建账户&#xff08;与SAP HANA数据库账户名一致&#xff09;3、使用RS2HANA_VIEW查看配置Assignment TypeDB Connection NameLimit…

如何解决亚马逊、ebay砍单、封号问题?稳定测评方案分析

很多卖家和工作室朋友询问我为什么在测评过程中经常遇到砍单和封号的问题。实际上&#xff0c;这并不难理解&#xff0c;因为测评所涉及的技术问题很多&#xff0c;并不能仅通过解决IP或环境的单一因素来实现稳定的测评。 目前市面上存在许多技术方案&#xff0c;例如指纹浏览…