当前位置: 首页 > article >正文

Spring 源码分析

Spring 源码版本 4.2.8.RELEASE

Bean 生命周期

Spring Bean生命周期

动态代理

代理模式

  • 优点: 在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
  • 缺点: 代理对象需要与目标对象实现一样的接口,所以会有很多代理类,一旦接口增加方法,目标对象与代理对象都要维护

JDK 代理

Java虚拟机类加载过程主要分为五个阶段:加载、验证、准备、解析、初始化。其中加载阶段需要完成以下3件事情:

  1. 通过一个类的全限定名来获取定义此类的二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据访问入口

其中的运行时计算生成,这种场景使用最多的是动态代理技术,在java.lang.reflect.Proxy类中,就是用了ProxyGenerator.generateProxyClass来为特定接口生成形式为*$Proxy的代理类的二进制字节流

动态代理就是想办法,根据接口或目标对象,计算出代理类的字节码,然后再加载到 JVM 中使用

JDK 动态代理流程

使用 arthas 查看源码

public final class $Proxy0 extends Proxy implements Landlord {
    private static Method m3;
    
    // $Proxy0 类的构造方法
    // 参数为 invocationHandler
    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    static {
        m3 = Class.forName("com.company.proxy.Landlord").getMethod("apartmentToRent", new Class[0]);
    }

    public final void apartmentToRent() {
        this.h.invoke(this, m3, null);
        return;
    }
}

我们的代理类实际上是实现了 Landlord 的接口,然后重写了 Landlord 接口中的 apartmentToRent 方法
当外界调用代理类的 apartmentToRent() 方法时,实际上是调用的我们自定义的 new InvocationHandler() 类里面的 invoke 方法

return Proxy.newProxyInstance(ClassLoader,Interfaces,new InvocationHandler() {});
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){
    // cl = class com.sun.proxy.$Proxy0
    Class<?> cl = getProxyClass0(loader, intfs);
    // cons = public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    // 根据构造参数实例化对象
    return cons.newInstance(new Object[]{h});
}

$Proxy0 的构造入参 InvocationHandler 为自定义的 InvocationHandler

  1. 拿到 $Proxy0 的 Class
  2. 根据 Class 拿到其构造方法
  3. 根据构造方法传入参数进行实例化

Cglib 代理

cglib (Code Generation Library ) 是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。cglib 为没有实现接口的类提供代理,为 JDK 的动态代理提供了很好的补充

Spring、Mybatis、Dubbo等都是使用cglib实现动态代理

  • Spring 框架使用CGLIB实现了动态代理、延迟加载、Bean初始化和销毁回调以及字节码增强等功能
  • MyBatis使用CGLIB实现延迟加载和脏数据检查
  • Dubbo使用CGLIB来实现服务接口的动态代理,以便在远程调用时添加额外的功能,如负载均衡、容错处理等

  • 最底层是字节码
  • ASM 是操作字节码的工具
  • cglib 基于 ASM 字节码工具操作字节码(即动态生成代理,对方法进行增强)
  • SpringAOP 基于 cglib 进行封装,实现 cglib 方式的动态代理
cglib 代理流程
public class UserServiceImpl$$EnhancerByCGLIB$$cd9788d extends UserServiceImpl implements Factory {
    final List findUserList() {
        // 是否设置了回调
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            UserServiceImpl$$EnhancerByCGLIB$$cd9788d.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        // 设置回调,需要调用 intercept 方法
        if (methodInterceptor != null) {
            return (List) methodInterceptor.intercept(this, CGLIB$findUserList$0$Method, CGLIB$emptyArgs, CGLIB$findUserList$0$Proxy);
        }
        // 无回调,调用父类的 findUserList 即可
        return super.findUserList();
    }
    final List CGLIB$findUserList$0() {
        return super.findUserList();
    }
}

  • 在 JVM 编译期间,我们的 Enhancer 会根据目标类的信息去动态的生成 动态代理类并设置回调
  • 当用户在通过上述的动态代理类执行 findUserList() 方法时,有两个执行选项
    • 若设置了回调接口,则直接调用UserLogProxy 中的 intercept ,然后通过 FastClass 类调用动态代理类,执行CGLIB$findUserList$0 方法,调用父类的 findUserList() 方法
    • 若没有设置回调接口,则直接调用父类的 findUserList() 方法

IOC

Spring 启动流程

obtainFreshBeanFactory

整体简介: 创建容器,并且完成配置文件的加载

refreshBeanFactory:解析我们的 application.xml 文件并生成 BeanDefinition 注册至 DefaultListableBeanFactory 的 beanDefinitionMap 中

refreshBeanFactory 的业务:

  • 通过我们传递的xml 文件的路径,利用 documentLoader 将其封装成 Document 格式
  • 创建 BeanDefinitionDocumentReader 来正式解析 xml 文件并找到文件的 root
  • 根据 root 扫描遍历,对不同配置的标签(import、alias、bean、beans)走不同的逻辑判断
  • 将当前的标签各属性进行组装成 beanDefinition,调用 DefaultListableBeanFactory 进行注册
  • 根据 BeanName 查询该 beanDefinition 是否被注册过,如果被注册过,则直接抛出异常(Spring不允许覆盖)
  • 如果没有注册过,则将 BeanName 与 beanDefinition 注册至 DefaultListableBeanFactory 的 beanDefinitionMap 中
  • 如果该 beanDefinition 含有别名,也要将别名进行注册,至于为什么注册别名

finishBeanFactoryInitialization

整体简介:完成所有非懒加载的单例对象的实例化操作,从此方法开始进行对象的创建,包含了实例化,初始化,循环依赖,AOP等核心逻辑的处理过程,此步骤是最最核心且关键的点,要对其中的细节最够清楚

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   // 实例化剩下的单例对象
   beanFactory.preInstantiateSingletons();
}

public void preInstantiateSingletons(){
    	// 拿到我们之前存储的所有beanDefinition的名字
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);		
		// 触发单例bean的初始化,遍历集合的对象
		for (String beanName : beanNames) {
            // 如果beanName对应的bean不是FactoryBean,只是普通的bean,通过beanName获取bean实例
            getBean(beanName);
        }
}

public Object getBean(String name) throws BeansException {
    // 此方法是实际获取bean的方法,也是触发依赖注入的方法
    return doGetBean(name, null, null, false);
}

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){
    // 这里需要一步转换,这里的原因我们附录1提到过,这里不再过多讨论
    String beanName = transformedBeanName(name);
    
    // 提前检查单例缓存中是否有手动注册的单例对象,剧透一下(和循环依赖有关联)
    Object sharedInstance = getSingleton(beanName);
    
    // 当对象都是单例的时候会尝试解决循环依赖的问题,但是原型模式下如果存在循环依赖的情况,那么直接抛出异常
    if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
    
    if (mbd.isSingleton()) {
        // 返回以beanName的(原始)单例对象,如果尚未注册,则使用singletonFactory创建并注册一个对象:
        sharedInstance = getSingleton(beanName, () -> {
            try {
                // 为给定的合并后BeanDefinition(和参数)创建一个bean实例
                // 这也是我们的核心方法
                return createBean(beanName, mbd, args);
            }
        });
}
          
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
    // 实际创建bean的调用
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
}
    
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
    // 根据执行bean使用对应的策略创建新的实例,如,工厂方法,构造函数主动注入、简单初始化
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    
    // 对bean的属性进行填充,将各个属性值注入,其中,可能存在依赖于其他bean的属性,则会递归初始化依赖的bean
    populateBean(beanName, mbd, instanceWrapper);
    
    // 执行初始化逻辑
    exposedObject = initializeBean(beanName, exposedObject, mbd);
}

创建实例的步骤

  • 拿到我们之前注册的 beanDefinitionNames,遍历整个 beanDefinitionNames,每一个 BeanName 生成一个对象
  • 我们需要进行名称转化,防止传入的是一个别名或其他的名称,利用转换后的别名去调用
  • 查询我们的单例缓存中是否已经存在该实例,如果存在直接返回即可
  • 如果不存在,则需要去根据该 beanDefinition 去生成对应的实例

对于生成实例共有三个步骤:

  1. 创建实例
  2. 属性填充
  3. 初始化逻辑
    • 实现 BeanPostProcessor 的前置方法
    • 对象的初始化方法
    • 实现 BeanPostProcessor 的后置方法

AOP

AOP 组件

  • Pointcut:定义切面的匹配点,主要是类和方法
  • Advice:定义切面的行为,即在匹配点执行的操作。
  • Advisor:将 Pointcut 和 Advice 组合成一个对象,表示一个完整的切面
  • Aspect:使用注解或 XML 配置方式定义切面,通常包含多个 Advisor

在Spring AOP中,拦截器链的概念是通过一系列顺序执行的通知(Advice)来实现的。这些通知可以是:

  • 前置通知(Before advice):在方法执行之前执行。
  • 后置通知(After advice):在方法执行之后执行。
  • 环绕通知(Around advice):包围方法的执行,可以决定是否继续执行方法,或者替换方法的返回值。

AOP 核心设计

public Object proceed() throws Throwable {
    return super.proceed();
}

public Object proceed() {
	// 从索引为-1的拦截器开始调用,并按序递增,如果拦截器链中的拦截器迭代调用完毕,开始调用target的函数,这个函数是通过反射机制完成的
	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
		return invokeJoinpoint();
	}

	// 获取下一个要执行的拦截器,沿着定义好的interceptorOrInterceptionAdvice链进行处理
	Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    // 普通拦截器,直接调用拦截器,将this作为参数传递以保证当前实例中调用链的执行
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}

核心实现思路,递归 + 拦截器链

整个proceed()方法的逻辑是递归的,它按照拦截器链中的顺序,逐个检查并调用拦截器。如果遇到动态方法匹配器,还会进行运行时的匹配检查。如果匹配失败,就会跳过当前拦截器,继续执行链中的下一个拦截器。这种递归调用的方式直到链中的最后一个拦截器或实际的连接点被执行。

事务

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行

MySQL 事务与InnoDB的MVCC实现机制

Spring 事务管理

Spring事务管理的实现步骤大致如下:

  1. 配置事务管理器:根据所使用的数据访问技术(如JDBC、Hibernate等),配置相应的事务管理器。
  2. 配置事务增强(Transaction Advice):创建一个事务增强(一个带有事务语义的Advice),并将其与切入点(Pointcut)关联起来。切入点定义了哪些方法需要事务管理。
  3. 创建代理:Spring AOP会为目标对象创建一个代理,该代理会根据配置的事务属性在方法调用前后添加事务管理逻辑。
  4. 事务的创建和结束:在代理对象的方法被调用时,Spring会根据事务属性创建或加入一个事务,并在方法正常结束或发生异常时提交或回滚事务。
  5. 事务的传播:Spring事务管理支持多种事务传播行为,如支持嵌套事务、独立事务等。
  6. 事务的回滚:Spring允许你定义哪些异常会导致事务回滚,哪些异常不会。
  7. 事务同步:Spring提供了事务同步机制,允许你在事务的开始、结束或回滚时执行一些资源清理工作。
  • 获取事务:TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
  • 提交事务:void commit(TransactionStatus status)
  • 回滚事务:void rollback(TransactionStatus status)

事务 AOP 实现

@EnableTransactionManagement会开启事务配置,TransactionManagementConfigurationSelector选择代理或ASPECTJ,在ProxyTransactionManagementConfiguration注入AutoProxyRegistrar注册AOP处理器及ProxyTransactionManagementConfiguration代理事务配置

本质使用TransactionInterceptor,在AOP的拦截器链里执行事务代理操作

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor {
   @Override
	@Nullable
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// 获取我们的代理对象的class属性
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
		/**
		 * 以事务的方式调用目标方法
		 * 在这埋了一个钩子函数 用来回调目标方法的
		 */
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}
}

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation){
	   // 获取我们的事务属性源对象
		TransactionAttributeSource tas = getTransactionAttributeSource();
		// 通过事务属性源对象获取到当前方法的事务属性信息
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
		// 获取我们配置的事务管理器对象
		final TransactionManager tm = determineTransactionManager(txAttr);
    
    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // 【重点】创建TransactionInfo
		  TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
        try {
				// 执行被增强方法,调用具体的处理逻辑【我们实际的方法】
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// 异常回滚
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				//清除事务信息,恢复线程私有的老的事务信息
				cleanupTransactionInfo(txInfo);
			}
        //成功后提交,会进行资源储量,连接释放,恢复挂起事务等操作
		  commitTransactionAfterReturning(txInfo);
		return retVal;
    }
}

// 创建连接 + 开启事务
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
    // 获取TransactionStatus事务状态信息
    status = tm.getTransaction(txAttr);
	
    // 根据指定的属性与status准备一个TransactionInfo,
	return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

// 存在异常时回滚事务
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
    // 进行回滚
    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}

// 调用事务管理器的提交方法
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo){
    txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}

整体流程

多级缓存

多级缓存能解决属性注入导致的循环依赖问题,不能解决有参构造注入的循环依赖

  • 一级缓存(Singleton Objects):存储已经创建好的单例Bean实例。当一个Bean被成功创建后,它会被存储在这个缓存中,以供后续的请求直接使用,避免重复创建。
  • 二级缓存(Early Singleton Objects):存储早期的Bean引用。在Bean的创建过程中,如果需要引用其他Bean,Spring会尝试从二级缓存中获取。
  • 三级缓存(Singleton Factories):存储Bean工厂对象。在Bean的创建过程中,如果Spring需要为某个Bean创建一个工厂对象(例如,当使用@Bean注解时指定了一个工厂方法),这些工厂对象会被存储在三级缓存中。

源码

查询缓存
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){
    // Step1:查询MyDemo1缓存是否存在
    Object sharedInstance = getSingleton(beanName);
    
    // 如果是单例的bean
    if (mbd.isSingleton()) {
        // 直接创建bean即可,注意 getSingleton 方法
        sharedInstance = getSingleton(beanName, () -> {
            return createBean(beanName, mbd, args);
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
}

// Step1:从三级缓存中查询 MyDemo1 是否被缓存
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    	// 一级缓存查询
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            // 二级缓存查询
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
                            // 三级缓存查询
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

// 这里记住一个操作:在我们创建bean结束之后,会调用 addSingleton 该方法
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    finally {
        if (recordSuppressedExceptions) {
            this.suppressedExceptions = null;
        }
        afterSingletonCreation(beanName);
    }
    if (newSingleton) {
        addSingleton(beanName, singletonObject);
    }
    return singletonObject;
}

解决动态代理
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                // 【重点】
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    // 这里会生成动态代理类
    return wrapIfNecessary(bean, beanName, cacheKey);
}

二级缓存主要用于解决已经实例化但尚未初始化的Bean之间的循环依赖问题。而三级缓存则用于更复杂的情况,比如需要提前暴露Bean的引用(通过代理对象或工厂对象)来解决循环依赖。

三级缓存目的:属性注入的阶段在执行初始方法(AOP)之前,缓存池中的半实例化对象不是代理对象


参考资料:

  1. 图片转自爱敲代码的小黄 Spring系列

http://www.kler.cn/news/315477.html

相关文章:

  • C++独立开发开源大数计算库 CBigNum
  • MySQL之内置函数
  • 【笔记】第三节 组织与性能
  • 搜维尔科技:Unity中的A.R.T.测量工具
  • 金仓数据库 KingbaseES参考手册 (8. 函数(九))
  • C++标准库容器类——string类
  • KTH5762系列 低功耗、高精度 3D 霍尔角度传感器 电子手表旋钮应用
  • 机器翻译之Bahdanau注意力机制在Seq2Seq中的应用
  • 【计网】从零开始掌握序列化 --- JSON实现协议 + 设计 传输\会话\应用 三层结构
  • 对时间序列SOTA模型Patch TST核心代码逻辑的解读
  • 基于区块链的相亲交易系统源码解析
  • vue3 本地windows下的字体的引用
  • 分布式锁优化之 使用lua脚本改造分布式锁保证判断和删除的原子性(优化之LUA脚本保证删除的原子性)
  • FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
  • Java 入门指南:JVM(Java虚拟机)——类的生命周期与加载过程
  • web基础—dvwa靶场(八)SQL Injection(Blind)
  • 众数信科AI智能体政务服务解决方案——寻知智能笔录系统
  • ‌内网穿透技术‌总结
  • SpringCloud config native 配置
  • mysql性能优化- 数据库配置优化
  • java面试题第一弹
  • Mybatis Plus分页查询返回total为0问题
  • PostgreSQL技术内幕10:PostgreSQL事务原理解析-日志模块介绍
  • 若依框架多次list查询时,分页失效
  • 使用Apache SeaTunnel高效集成和管理SftpFile数据源
  • 伊犁云计算22-1 apache 安装rhel8
  • C++:tinyxml2用于解析、操作和生成XML文件
  • Git基本用法总结
  • ZYNQ学习--AXI总线协议
  • 001.docker30分钟速通版