Spring总结

一、SpringIOC

1、IOC

Spring ioc即控制反转,ioc容器负责实例化、定位、配置应用程序中的对象及建立这些对象的依赖,交给spring容器统一进行管理,从而达到解耦的目的

2、IOC实现原理及源码分析

(1)、整体流程图

(2)、源码分析

首先进行源码搭建,搭建过程在上一篇博客请查看:


在源码工程创建一个测试demo

BizSpring源码_后端

两种方式一种xml一种注解的方式两种都可以,我是采用xml配置的方式进行的通过断点我们首先进行

BizSpring源码_java_02

上面红框标注的两个方法不重要就是获取xml文件做一些准备工作,有兴趣可以进行详细了解。重点在下面AbstractApplicationContext类中的refresh方法,此方法有13个重点的入口调用方法,接下来一个一个去看
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//准备上下文环境 打印开始执行时间
prepareRefresh();

// Tell the subclass to refresh the internal bean factory.
		//创建BeanFactory 工厂 并且加载xml信息到 BeanDefinition
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		//BeanFactory 工厂准备信息设置
		prepareBeanFactory(beanFactory);

		try {
			// Allows post-processing of the bean factory in context subclasses.
			//目前是空方法留着扩展用 springboot就在这做的扩展
			postProcessBeanFactory(beanFactory);

			// Invoke factory processors registered as beans in the context.
			//正在用来做处理了方法
			invokeBeanFactoryPostProcessors(beanFactory);

			// Register bean processors that intercept bean creation.
			//注册BeanPostProcessor后置处理器
			registerBeanPostProcessors(beanFactory);

			// Initialize message source for this context.
			//国际化
			initMessageSource();

			// Initialize event multicaster for this context.
			//设置多例事件
			initApplicationEventMulticaster();

			// Initialize other special beans in specific context subclasses.
           //目前为空方法留着扩展
			onRefresh();

			// Check for listener beans and register them.
			//目前为空留给监听器扩展
			registerListeners();

			// Instantiate all remaining (non-lazy-init) singletons.
			//bean 实例化初始化
			finishBeanFactoryInitialization(beanFactory);

			// Last step: publish corresponding event.
			//清除容器
			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}

			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();

			// Reset 'active' flag.
			cancelRefresh(ex);

			// Propagate exception to caller.
			throw ex;
		}

		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}
}

1、(环境准备和BeanFactory创建,xml文件解析等,BeanDefinition生成)

1、prepareRefresh方法准备上下文环境 打印开始执行时间,其实也没有做很重要的处理。

BizSpring源码_BizSpring源码_03

2、obtainFreshBeanFactory方法重点做了进行BeanFactory工厂创建,并且通过BeanDefinitionReader解析xml文件解析将bean信息映射到BeanDefinition中

工厂创建

BizSpring源码_java_04


BizSpring源码_java_05

BeanDefinitionReader解析xml文件解析将bean信息映射到BeanDefinition中

通过路径获取xml文件流然后传递文件流到XmlBeanDefinitionReader中的doLoadBeanDefinitions方法中,在doLoadBeanDefinitions方法中获取Document对象,然后通过层层解析最终传递Element到DefaultBeanDefinitionDocumentReader的parseBeanDefinitions进行xml节点解析,当判断节点是bean对象时调用BeanDefinitionParserDelegate的parseBeanDefinitionElement方法经过处理映射为beanDefinition对象,然后封装成BeanDefinitionHolder对象返回,然后对BeanDefinitionHolder进行处理最终DefaultListableBeanFactory中的registerBeanDefinition方法将BeanDefinition添加到BeanDefinitionMap中共后续使用

BizSpring源码_初始化_06

BizSpring源码_spring_07


BizSpring源码_后端_08

BizSpring源码_初始化_09

BizSpring源码_后端_10


BizSpring源码_初始化_11

2、设置BeanFactoryPostProcessors后置增强,注册一些监听器

BeanFactoryPostProcessors进行后置增强,测试可以进行属性值得改变等处理sprinboot就是在这个地方进行的扩展里面逻辑相对比较多此处不做分析

3、Bean对象实例化和初始化
1、对象生命周期
初始化、填充属性、执行Aware接口方法、before、init-method、after、完成对象创建、销毁流程

通过DefaultListableBeanFactory类中的preInstantiateSingletons进行循环对象创建

BizSpring源码_spring_12

首先判断单例对象是否存在

BizSpring源码_spring_13

进行对象创建

BizSpring源码_spring_14

获取构造函数通过反射进行对象创建

BizSpring源码_后端_15

通过构造函数调用java反射进行对象创建

BizSpring源码_spring_16

进行对象缓存,此处采用了三级缓存,使用三级缓存是为了解决对象循环依赖,三级缓存解决循环依赖是这样的,

1问题:

例如有两个类A,B。其中A类有个属性时B对象,B类有个属性时A对象,这样在创建对象的时候,比如先去创建A对象,当A对象实例化以后在给他填充属性的时候由于B对象不存,如果先创建B对象,B对象也不存在,这样就产生了循环依赖的问题

2.解决

Spring是这样去解决循环依赖问题的,在A对象实例化完成以后,在填充属性B的时候由于B不存在,他会先把A对象实例化完的一个不完整对象存储在三级缓存中,然后他开始去创建B对象,当B对象实例化完成以后,进行对象填充属性A的时候此时A对应已经存在于三级缓存,然后他会那到三级缓存中的A对象赋值给B,然后清除三级缓存中的A对象到二级缓存。然后B完成整个实例化和初始化,然后把B对象也放入一级缓存。此时去二级缓存中读取A对象进行再次填充,此时由于B对象在一级缓存所有可以读取到,直接赋值给A对象,然后清除二级缓存中的A对象到一级缓存,这样就完美的解决了循环依赖。此处有个问题,为什么要用三级缓存,用一级或者二级不行吗答案肯定是不行,只用一级缓存不好去区分对象在创建的那个阶段,容易混乱,如果只有二级缓存,会存在对象没有被初始化完成就可能被移入一级缓存被使用

BizSpring源码_初始化_17


BizSpring源码_后端_18


BizSpring源码_java_19

进行aware处理,aware其实就是比如我们自定义类要使用BeanFactory那么就要去实现BeanFactoryAware接口,那个这个类的自动注入就在这个地方完成

BizSpring源码_spring_20

此处进行初始化的before处理也就是BeanPostProcessor处理

BizSpring源码_spring_21

此处进行初始化的init-method阶段处理

BizSpring源码_java_22

此处进行初始化的after处理最后整个对象创建完成

BizSpring源码_后端_23

3、IOC流程图

BizSpring源码_java_24

二、SpringAOP
1、AOP
aop是一种编程思想,包含切面(Aspect)、切点(Pointcut)、连接点(Joint point)、通知(Advice)、目标对象(Target)、织入(Weaving)。
2、名称解释
(1)、切面(Aspect):
切面有切点、连接点通知等组成。
(2)、连接点(Joint point):

表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
(3)、切点(Pointcut):
表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 通知Advice 将要发生的地方

(4)、通知(Advice):
Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after afterReturing、afterThroeing、和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。

(5)、织入(Weaving):
将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
(6)、目标对象(Target):
织入 Advice 的目标对象.

3、AOP链式调用流程图

BizSpring源码_BizSpring源码_25

4、AOP方法执行顺序

1、图例说名

BizSpring源码_BizSpring源码_26

2、源码说明

测试类代码

BizSpring源码_初始化_27

可以看到执行System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, “C:\class”);代码会包CGlib代理类代码拿到,我是放在c盘,

打开代理类代码可以看到把我们自定义的方法也动态生成了并且在里面加入了很多代码

BizSpring源码_BizSpring源码_28

我们通过观察可以去猜一下他在执行我们测试类user.getName();这一行代码的时候其实调用的就是代理类中的这个方法,接着往下执行他会调用

接着我们通过断点可以看到我们代理类中有这么一个数组,其中有个名称为 CGLIB$CALLBACK_0的他对应的类是DynamicAdvisedInterceptor这个类,我们可以从代理类看到代码在执行过程中会调用DynamicAdvisedInterceptor类中的intercept方法。

BizSpring源码_spring_29

我们进入DynamicAdvisedInterceptor中的intercept方法去看可以拿到所有通知的一个List集合这个集合中包含了我们配置的所有通知,那么这个集合是怎么来的,其实在spring容器初始化的时候就会被创建和填入值。那么有了集合以后接着往下执行

BizSpring源码_spring_30

进入CglibMethodInvocation这个类

BizSpring源码_后端_31

然后调用这个类的ReflectiveMethodInvocation父类的proceed方法进行责任链调用

BizSpring源码_spring_32

这个方法在执行过程中定义了一个索引号currentInterceptorIndex,默认值为-1,然后每次加一进行执行