提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
本片文章主要切入SpirngFrameWork源码的概览,我会以一个学习者的身份来记录我在源码学习(Spring源码)过程中的一些初衷和收获,同时会把一些自己觉得不错的姿势分享给大家。
tip:学习积累就像是画圆,积累的东西越多,⚪就越大,同时我们接触到的边缘盲区也就越多;只有越学越多才真正走对了方向!
一、Spring概览?
大家已经开始接触源码相关的知识点了,那肯定也对Spring这个架构有了一定的了解。这里还是大概简述下我对Spring的理解;
提到Spring都绕不过IOC,AOP;我们在没有使用Spring之前,编程时会将各个类的依赖关系使用手动实例化的方式实现(new一个对象),但是随着项目越做越复杂,导致项目运行起来越来越重,依赖越来越复杂。这就涉及到了开发者进阶性的难点:“解耦”,这是一个思想,Spring IOC就是在这个实现上来帮助我们实现这一设计思路;简单来说就是将我们new对象的过程通过”单例模式“(当然也可以使用其它实现 后续文章中会详解)来管理对象。AOP则利用”动态代理“来提升我们开发过程中的代码拓展性和复用性,将传统动态代理做了封装,增加了项目代码块的可拔插性;
二、示例代码
1.XML配置
代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<bean class="com.mashibing.MyBeanFactoryPostProcessorBySelf" ></bean>
<bean id="logUtil" class="com.mashibing.aop.xml.util.LogUtil" ></bean>
<bean id="myCalculator" class="com.mashibing.aop.xml.service.MyCalculator" ></bean>
<aop:config>
<aop:aspect ref="logUtil">
<aop:pointcut id="myPoint" expression="execution( Integer com.mashibing.aop.xml.service.MyCalculator.* (..))"/>
<aop:around method="around" pointcut-ref="myPoint"></aop:around>
<aop:before method="start" pointcut-ref="myPoint"></aop:before>
<aop:after method="logFinally" pointcut-ref="myPoint"></aop:after>
<aop:after-returning method="stop" pointcut-ref="myPoint" returning="result"></aop:after-returning>
<aop:after-throwing method="logException" pointcut-ref="myPoint" throwing="e"></aop:after-throwing>
</aop:aspect>
</aop:config>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
2.Calculator
代码如下:
public interface Calculator {
public Integer add(Integer i,Integer j) throws NoSuchMethodException;
public Integer sub(Integer i,Integer j) throws NoSuchMethodException;
public Integer mul(Integer i,Integer j) throws NoSuchMethodException;
public Integer div(Integer i,Integer j) throws NoSuchMethodException;
}
3.MyCalculator
代码如下:
public class MyCalculator /*implements Calculator */{
public Integer add(Integer i, Integer j) throws NoSuchMethodException {
Integer result = i+j;
return result;
}
public Integer sub(Integer i, Integer j) throws NoSuchMethodException {
Integer result = i-j;
return result;
}
public Integer mul(Integer i, Integer j) throws NoSuchMethodException {
Integer result = i*j;
return result;
}
public Integer div(Integer i, Integer j) throws NoSuchMethodException {
Integer result = i/j;
return result;
}
public Integer show(Integer i){
System.out.println("show .....");
return i;
}
@Override
public String toString() {
return "super.toString()";
}
}
4.SecondCalculator
代码如下:
public class SecondCalculator implements Calculator {
public Integer add(Integer i, Integer j) throws NoSuchMethodException {
return null;
}
public Integer sub(Integer i, Integer j) throws NoSuchMethodException {
return null;
}
public Integer mul(Integer i, Integer j) throws NoSuchMethodException {
return null;
}
public Integer div(Integer i, Integer j) throws NoSuchMethodException {
return null;
}
}
5.LogUtil
代码如下:
public class LogUtil {
// @Pointcut("execution(public Integer com.mashibing.service.MyCalculator.*(Integer,Integer))")
public void myPointCut(){}
// @Pointcut("execution(* *(..))")
public void myPointCut1(){}
// @Before(value = "myPointCut()")
private int start(JoinPoint joinPoint){
//获取方法签名
Signature signature = joinPoint.getSignature();
//获取参数信息
Object[] args = joinPoint.getArgs();
System.out.println("log---"+signature.getName()+"方法开始执行:参数是"+Arrays.asList(args));
return 100;
}
// @AfterReturning(value = "myPointCut()",returning = "result")
public static void stop(JoinPoint joinPoint,Object result){
Signature signature = joinPoint.getSignature();
System.out.println("log---"+signature.getName()+"方法执行结束,结果是:"+result);
}
// @AfterThrowing(value = "myPointCut()",throwing = "e")
public static void logException(JoinPoint joinPoint,Exception e){
Signature signature = joinPoint.getSignature();
System.out.println("log---"+signature.getName()+"方法抛出异常:"+e.getMessage());
}
// @After("myPointCut()")
public static void logFinally(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
System.out.println("log---"+signature.getName()+"方法执行结束。。。。。over");
}
// @Around("myPointCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Signature signature = pjp.getSignature();
Object[] args = pjp.getArgs();
Object result = null;
try {
System.out.println("log---环绕通知start:"+signature.getName()+"方法开始执行,参数为:"+Arrays.asList(args));
//通过反射的方式调用目标的方法,相当于执行method.invoke(),可以自己修改结果值
result = pjp.proceed(args);
// result=100;
System.out.println("log---环绕通知stop"+signature.getName()+"方法执行结束");
} catch (Throwable throwable) {
System.out.println("log---环绕异常通知:"+signature.getName()+"出现异常");
throw throwable;
}finally {
System.out.println("log---环绕返回通知:"+signature.getName()+"方法返回结果是:"+result);
}
return result;
}
}
6.TestAop
代码如下:
public class TestAop {
public static void main(String[] args) throws Exception {
saveGeneratedCGlibProxyFiles(System.getProperty("user.dir")+"/proxy");
ApplicationContext ac = new ClassPathXmlApplicationContext("aop.xml");
MyCalculator bean = ac.getBean(MyCalculator.class,1);
System.out.println(bean.toString());
bean.add(1,1);
bean.sub(1,1);
}
public static void saveGeneratedCGlibProxyFiles(String dir) throws Exception {
Field field = System.class.getDeclaredField("props");
field.setAccessible(true);
Properties props = (Properties) field.get(null);
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, dir);//dir为保存文件路径
props.put("net.sf.cglib.core.DebuggingClassWriter.traceEnabled", "true");
}
}
三、refresh()源码详解
Spring源码的主要流程包含了大量的设计模式(后续也考虑出一个关于Spring设计模式相关的文章),比如我们下面要讲到的refresh(),大家在debugger源码时很轻松可以进到这个方法,该方法就是典型的“模板方法”,其包含了15个模板逻辑方法,提供了对Spring生命周期,已经框架拓展的支持,同时也包含了整个Sping源码的精华;
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
/**
* 前戏,做容器刷新前的准备工作
* 1、设置容器的启动时间
* 2、设置活跃状态为true
* 3、设置关闭状态为false
* 4、获取Environment对象,并加载当前系统的属性值到Environment对象中
* 5、准备监听器和事件的集合对象,默认为空的集合
*/
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 创建容器对象:DefaultListableBeanFactory
// 加载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.
// 子类覆盖方法做额外的处理,此处我们自己一般不做任何扩展工作,但是可以查看web中的代码,是有具体实现的
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 调用各种beanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 注册bean处理器,这里只是注册功能,真正调用的是getBean方法
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 为上下文初始化message源,即不同语言的消息体,国际化处理,在springmvc的时候通过国际化的代码重点讲
initMessageSource();
// Initialize event multicaster for this context.
// 初始化事件监听多路广播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 留给子类来初始化其他的bean
onRefresh();
// Check for listener beans and register them.
// 在所有注册的bean中查找listener bean,注册到消息广播器中
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 初始化剩下的单实例(非懒加载的)
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
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.
// 为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件bean
destroyBeans();
// Reset 'active' flag.
// 重置active标志
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();
}
}
}
在refresh()模板方法中有一些并不是特别重要的模块 比如prepareRefresh(),prepareBeanFactory()等一些准备工作或留给后续拓展框架的方法就不带大家一一解读了,但其中也包含了很多巧妙的设计逻辑大家有兴趣可以debugger一下。下面我将带大家详解一下几个Spring源码中比较重要的几个方法;
前缀知识点
- BeanFactory:Spring框架“最重要”的一个容器,spring帮我们自动装配的所有对象都会塞入此容器中;
- BeanDefinition:重复利用了java万物皆对象的原则,会将所有的从配置文件或注解等渠道解析过来的数据封装成一个个对象,beanDefinition就是等待被实例化的一个对象,beanFactory中解析过配置文件后会往BeanDefinitionMap和BeanDefinitionNames两个缓存对象中放入一个个等待实例化的BeanDefinition对象;
- BeanFactoryPostProcessor:就是BeanFactory的增强器,就是对BeanFactory进行拓展使用的,比如使用@Bean注解方法定义Bean,xml文件中解析不到对应的bean就是利用BeanFatroaryPostProcessor的方式进行解析然后塞入BeanFactory中的,可以把它理解为对BeanFactory中对象的增删改查处理器;
- BeanPostProcessor:顾名思义,此类和BeanFactoryPostProcessor意思大致相同,只不过此类增强的不是BeanFactory对象,而是直接对Bean对象进行正确,比如在使用AOP后,需要对对应的bean对象进行动态代理。
- Aware:Aware接口是Spring中提供的一组标记接口,用于在Bean装配的过程中获取Spring容器中提供的一些核心组件或运行时上下文等信息。通过使用Aware接口,我们可以在Bean实例化和初始化过程中获取到Spring容器中其他组件,方便Bean类实现更复杂的业务逻辑。比如在每个bean中需要获取整个Spring容器的BeanFactory对象就可以使用实现BeanFactoryAware来掉对应方法获取;
1、obtainFreshBeanFactory
在原生的Spirng框架中,该方法主要做了两件事:完成BeanFactory工厂对象的初始化,完成对配置文件的解析。以下是该模块抽象流程图:
2、prepareBeanFactory
该方法主要对初始化好的BeanFactory额外的一些属性进行填充,也是准备工作,主要做了这几件事:
-
为BeanFactory设置classloader对象;
-
设置beanfactory的表达式语言处理器(SpelExpressionParser处理SPEL语句,比如在引入配置文件中常量值时${msg}等运用范围,有兴趣可以深入学习下SPEL)
-
为beanFactory增加一个默认的propertyEditor,处理常见的属性值如URL,File等类型(propertyEditor可以理解成一个属性值处理器,比如你需要把一个String类型的地址切割组装成一个地址类,就可以自定义一个propertyEditor进行实现,具体实现可以借鉴这篇文章)
-
添加一个默认的beanPostProcessor,ApplicationContextAwareProcessor此类用来完成某些Aware对象的注入
-
设置要忽略自动装配的接口,设置要忽略自动装配的接口,原因非常简单,这些接口的实现是由容器通过set方法进行注入的,不需要容器再自动注入了
-
设置几个自动装配的特殊规则,当在进行ioc初始化的如果有多个实现,那么就使用指定的对象进行注入
-
添加一个BeanPostProcessor进BeanFctory中,ApplicationListenerDetector此类主要通过对Bean的增强来使用观察者模式来监听bean单例对象的起止生命周期
-
增加对AspectJ的支持,在java中织入分为三种方式,分为编译器织入,类加载器织入,运行期织入,编译器织入是指在java编译器,采用特殊的编译器,将切面织入到java类中
-
注册默认的系统环境bean到一级缓存中
3、postProcessBeanFactory
默认是一个空实现,留给子类拓展使用,但是可以查看springMVC中的代码,是有具体实现的,当然我们也可以自定义拓展;
4、invokeBeanFactoryPostProcessors
此处开始调用/处理BeanFactoryPostProcessors,源码比较复杂,直接上流程图:
此处处理BeanFactoryPostProcessors:
- 在beanFactory对象层面会先对实现了BeanDefinitionRegistry接口的对象进行处理,处理实现了该接口的对象会过滤出实现了BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessors对象执行该对象中postProcessBeanDefinitionRegistry方法对BeanDefinition进行增删改查的操作(当然在执行之前会先对BeanFactoryPostProcessors对象进行getBean()实例化操作);
- 在BeanFactoryPostProcessors层面会分为实现了PriorityOrdered,Ordered和其它未实现以上了个接口的BeanFactoryPostProcessors对象,前两者都会按照排序条件对BFPP(BeanFactoryPostProcessors)进行排序后执行,最后执行剩余的BFPP对象;
- 总体看该方法的执行流程是:先处理实现了BeanDefinitionRegistry接口的对象,找对对应的实现了BeanDefinitionRegistryPostProcessor的DFPP对象(PriorityOrdered->Ordered->其它)执行该对象中postProcessBeanDefinitionRegistry方法,最后循环执行BFPP的postProcessBeanFactory方法完成所有BFPP的增强操作;
4、registerBeanPostProcessors
该方法是对BeanPostProcessors进行注册处理,及扫描到BeanFactory中的BPP的BeanDefinition然后注册到BeanFactory的beanPostProcessors集合对象中,扫描注册的顺序与上述处理BeanFactoryPostProcessors的执行顺序一致(PriorityOrdered->Ordered->其它)注意:此处仅为对BPP的注册操作,并没有真正执行;
5、initMessageSource
上下文初始化message源,即不同语言的消息体,国际化处理,在springmvc的时候通过国际化的代码用处比较大;此处默认会在BeanFactory的消息源对象中放一个空的DelegatingMessageSource对象;
6、初始化事件监听多路广播器
在Spring上下文对象中初始化实现多路广播器,用户事件的调用,可以通过实现ApplicationEventMulticaster接口自定义事件多播器,此处默认使用的是SimpleApplicationEventMulticaster对象作为事件多播器
7、onRefresh
默认空实现,留给子类拓展
8、registerListeners
此方法会在Spring上下文对象中扫描applicationListeners应用程序中存在的监听器集合,并将对应的监听器添加到监听器的多路广播器中,之后从BeanFactory容器中获取所有实现了ApplicationListener接口的bd放入ApplicationListenerBeans集合中,并且会执行earlyApplicationEvents(在实例化对象前就执行发布的监听器,可以自定义拓展)发布早期的监听器集合;
9、finishBeanFactoryInitialization
初始化剩下的,非懒加载的BeanDefinition对象,此处是整个Spring流程的最重要的一个方法
-
为啥使用三级缓存:
主要为了解决循环依赖问题,例如A依赖B,在实例化A时调用popultar给参数赋值时会调用getBean实例化B,但是如果B中又依赖了A就 造成了循环依赖的问题;为了解决这个问题就使用了多级缓存,一级缓存中存放的是以完成实例化并完成属性赋值的对象,二级缓存中存放了已完成实例化但未完成初始化的对象。通常情况下二级缓存就可以优雅的解决循环依赖的问题了,B在调用A的实例化方法时,A已经完成了实例化但未完成初始化,所以可以响应对应“不完整”的A实例,后续A初始化属性值也是往同一个引用赋值;三级缓存实际存放的时lamdba回调函数,该回调函数可以完成对象的实例化,如果时动态代理的类的话,会响应会动态代理后的结果,主要为了解决AOP动态代理; -
bean对象有哪几种实例化方式:
1,通过Factory来实现复杂Bean的创建工作,MyBatis中就使用了SqlSessionFactoryBean创建自定义的Bean
2,在实例化之前Spring会给BeanPostProcess一个机会来实例化bean,用户可以自定义拓展
3,使用Supplier,java1.8新特性,及向beanDefinition中注入supplier形式为lambda的回调函数
4,使用FactoryMethod工厂方法:静态工厂-使用静态方法创建对应的bean,直接调用该方法即可; 实例工厂-需要先创建出该工厂的实例,然后在创建bean实例时调用指定工厂的方法;
5.使用有参构造实例化Bean:ByName根据属性名称实例化,ByType根据属性类型实例化
6,使用无参构造反射的方式实例化对象 -
AOP动态代理详解:
1,动态代理操作发生在bpp的applyBeanPostProcessorsAfterInitialization方法中,在执行完自定义初始化方法后调用生成;
2,首先判断正在执行实例化的Bean是否需要执行动态代理canApply方法
3,判断是否实现接口且支持使用JDK动态代理
4,进行动态代理,和原生的动态代理逻辑基本相同,做了一些Spring的定制化开发
总结
本文对原生的Spring源码流程做了较为全面的讲解,但是对一些细节点并没有过多赘述,需要大家踏下心来认真专研。学习源码的过程枯燥且乏味,但是收获也一定是成正比的。加油!