`
ruijin5566
  • 浏览: 4576 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring源码学习

 
阅读更多

IoC容器

我们总结一下IoC容器初始化的基本步骤:Spring技术内幕P28

1.Resource的定位过程

这个Resource的定位指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一接口。比如,文件系统中的Bean定义信息可以使用FileSystemResource来进行抽象;类路径中的Bean定义信息可以使用ClassPathResource来进行抽象。初始化的入口在容器实现中的refresh()调用来完成。不管是ClassPathXmlApplicationContext,还是FileSystemXmlApplicationContext,都调用AbstractApplicationContext的refresh()方法。

以FileSystemXmlApplicationContext为例,在初始化FileSystemXmlApplicationContext的过程中,通过IoC容器的初始化的refresh来启动整个调用,使用的IoC容器是DefaultListableBeanFactory。具体资源的载入在XmlBeanDefinitionReader读入BeanDefinition时完成,对载入过程的启动可以在AbstractRefreshableApplicationContext的loadBeanDefinitions方法中看到,最终会调用DefaultResourceLoader的getResource方法,它先会处理带有classpath标识的Resource,再处理URL标识的资源定位,如果既不是classpath,也不是URL标识的资源定位,则把getResource的任务交给getResourceByPath方法,这个一个protected的方法,默认返回一个ClassPathContextResource,getResourceByPath方法会被FileSystemXmlApplicationContext实现,这个方法返回的是一个FileSystemResource,通过这个对象,Spring可以进行相关的I/O操作,完成BeanDefinition的定位。

 

2.BeanDefinition的载入

这个载入过程是把用户定义好的Bean表示成IoC容器内部的数据结构BeanDefinition。

AbstractRefreshableApplicationContext的loadBeanDefinitions方法是一个抽象方法,在子类AbstractXmlApplicationContext的方法中初始化了读取器XmlBeanDefinitionReader,然后把这个读取器在IoC容器中设置好(实际使用的IoC容器是DefaultListableBeanFactory),最后启动读取器来完成BeanDefinition在IoC容器中的载入。在读取器中需要得到代表XML文件的Resource,因为这个Resource对象封装了对XML文件的I/O操作,所以读取器可以在打开I/O流后得到XML文件对象。有了这个文件对象后,就可以按照Spring的Bean定义规则来对这个XML文档树进行解析了,解析交给BeanDefinitionParserDelegate来完成。

BeanDefinition的载入分成两个部分,首先通过XML的解析器(DefaultDocumentLoader)得到Document对象,然后按照Spring的Bean规则进行解析。按照Spring的Bean规则进行解析是在默认的DefaultBeanDefinitionDocumentReader中实现的,处理的结果由BeanDefinitionHolder对象持有,除了持有BeanDefinition对象外,还持有Bean的名字,别名集合等。这个BeanDefinitionHolder的生成是通过对Document文档树的内容进行解析来完成的,这个解析过程是由BeanDefinitionParserDelegate来实现的,这个类包含了对各种Spring Bean定义规则的处理,

 

3.IoC容器注册BeanDefinition

这个过程通过调用BeanDefinitionRegistry接口的实现来完成,把载入过程中的BeanDefinition向Ioc容器进行注册,在Ioc容器内部将BeanDefinition注入到一个Map中去,Ioc容器就是通过这个Map来持有这些BeanDefinition数据的。

在DefaultListableBeanFactory中是通过一个Map来持有载入的BeanDefinition。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,这个接口的实现完成BeanDefinition向容器的注册,将解析得到的BeanDefinition想IoC容器中的beanDefinitionMap注册的过程是在载入

BeanDefinition完成后进行的,这个注册过程不复杂,即时把解析得到的BeanDefinition设置到Map中去。

 

4.IoC容器的依赖注入

依赖注入的过程是用户第一次向IoC容器索要Bean时触发的,当然也有例外,也就是我们可以再BeanDefinition信息中通过控制lazy-init属性来让容器完成对Bean的预实例化。重点来说,getBean是依赖注入的起点,之后会调用createBean,createBean不但生成了需要的Bean,还对Bean初始化进行了处理,比如实现了在BeanDefinition中的init-method属性定义,Bean的后置处理器等。与依赖注入关系特别密切的方法有createBeanInstance和populateBean,在createBeanInstance中生成了Bean所包含的Java对象,这个对象的生成有很多不同的方式,可以通过工厂方法生成,也可以通过容器的autowire特性生成。默认的实例化策略是CglibSubclassingInstantiationStrategy,如果有构造器,则使用构造器实例化,否则使用cglib对Bean进行实例化。

Bean对象生成以后,需要把这些Bean对象的依赖关系设置好,完成整个依赖注入过程。这个过程涉及对各种Bean对象的属性处理过程(及依赖关系的处理过程),这些依赖关系处理的依据就是已经解析得到的BeanDefinition。具体在AbstractAutowireCapableBeanFactory的populateBean方法中,通过BeanDefinitionValueResolver来对BeanDefinition进行解析,然后注入到property中。BeanDefinitionValueResolver的resolveValueIfNecessary这个方法包含了所有对注入类型的处理,包括RuntimeBeanReference、RuntimeBeanNameReference、BeanDefinitionHolder、BeanDefinition、ManagedArray、ManagedList、ManagedSet、ManagedMap、ManagedProperties、TypedStringValue等。

在完成这个解析过程后,为依赖注入准备好了条件,依赖注入的发生是在BeanWrapper的setPropertyValues中,具体的完成是在其子类BeanWrapperImpl的setPropertyValue方法中,完成对Array、List、Map和其他非集合类的注入,主要依靠反射机制实现。

 

AOP

Joinpoint连接

拦截点,如某个业务方法

Pointcut切点

Joinpoint的表达式,表示拦截哪些方法。一个Pointcut对应多个Joinpoint。 

Pointcut(切点)决定Advice通知应该作用于哪个连接点,也就是说通过Pointcut来定义需要增强的方法集合。

Advice通知

要切入的逻辑

BeforeAdvice:MethodBeforeAdvice,在方法执行前切入。

AfterAdvice:AfterReturningAdvice,方法正常返回后切入,抛出异常则不切入。

ThrowsAdvice:没有指定需要实现的接口方法,在抛出异常时切入。

Advisor通知器

通过Advisor,可以定义应该使用哪个通知并在哪个关注点使用它,也就是说通过Advisor,把Advice和Pointcut结合起来,这个结合为使用IoC容器配置AOP应用,提供了便利。

 

首先,需要配置相关的Bean定义,为了让AOP起作用,需要完成一系列过程,比如,需要为目标对象建立代理对象,这个代理对象可以通过使用JDK的Proxy来完成,也可以通过第三方的类生成器Cglib来完成。然后还需要启动代理对象的拦截器来完成各种横切面的织入,这一系列的织入设计是通过一系列Adapter来实现的。通过一系列Adapter的设计,可以把AOP的横切面设计和Proxy模式有机结合起来,从而实现在AOP中定义好的各种织入方式。

 

ProxyFactoryBean为例

先配置ProxyFactoryBean。P110

ProxyFactoryBean中获取对象,是以getObject()方法作为入口完成的,该方法是FactoryBean需要实现的接口。ProxyFactoryBean把需要对target目标对象增加的增强处理,都通过getObject方法进行封装了,这些增强处理是为AOP功能的实现提供服务的。

getObject()方法首先对通知器链进行初始化,通知器链封装了一系列的拦截器,这些拦截器都要从配置中读取,然后为代理对象的生成做好准备。

关于AopProxy代理对象的生成,需要考虑使用哪种方式:如果目标对象是接口类,那么用JDK来生成代理对象(JdkDynamicAopProxy),否则使用Cglib来生成目标对象的代理对象(Cglib2AopProxy)。

生成AopProxy代理对象

JDK生成AopProxy代理对象:

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

Cglib生成AopProxy代理对象:

Enhancer.create()Enhancer.create(Class[] argumentTypes, Object[] arguments)

拦截器调用的实现

JdkDynamicAopProxy的invoke拦截,P120

获取目标对象、拦截器链,同时把这些对象作为输入,创建了ReflectiveMethodInvocation,这个类的proceed()方法中,包含了一个完整的拦截器链对目标对象的拦截过程,逐个运行拦截器链里的拦截增强,直到最后对目标对象方法的运行。

Cglib2AopProxy的intercept拦截,P121

DynamicAdvisedInterceptor的intercept方法里,构造CglibMethodInvocation对象来完成拦截器链的调用。它们对拦截器链的调用都是在ReflectiveMethodInvocation.proceed()方法实现的。

拦截器链的调用

proceed()方法中,先进行判断,如果现在已经运行到拦截器链的末尾,那么就会直接调用目标对象的实现方法;否则,沿着拦截器链继续进行,得到下一个拦截器,通过这个拦截器进行matches判断是否适用于横切增强的场合,如果是,启动拦截器的invoke方法进行切面增强,否则执行下一个拦截器。在这个过程以后,会迭代调用proceed方法,直到拦截器链中的拦截器都完成以上的拦截过程为止。

配置通知器

ProxyFactoryBean的getObject方法中对通知器链进行初始化时(initializeAdvisorChain),从XML配置中获取Advisor通知器是通过IoC容器的getBean方法。它能获得IoC容器,是因为实现了BeanFactoryAware接口。在使用DefaultListableBeanFactory作为IoC容器的时候,它的基类是AbstractAutowireCapableBeanFactory,在这个基类中可以看到对Bean进行初始化的initializeBean方法,对IoC容器在Bean中的回调进行了设置。首先,判断这个Bean类型是不是实现了BeanFactoryAware接口,如果是,通过接口方法setBeanFactory把IoC容器设置到Bean定义的一个属性中去。设置好BeanFactory以后,ProxyFactoryBean就可以通过回调容器的getBean方法去获取配置在Bean定义文件中的通知器了。在调用时,ProxyFactoryBean需要给出通知器的名字,而这些名字都是在interceptorNames已经配置好的。

Advice通知的实现

DefaultAdvisorChainFactory负责生成拦截器链,在它的

getInterceptorsAndDynamicInterceptionAdvice方法中,有一个适配和注册过程。

在DefaultAdvisorAdapterRegistry中,设置了一系列的Adapter适配器,正是这些适配器的实现,为Spring AOP的Advice提供了编织能力。

这些适配器的使用体现在2个方面:

<!--[if !supportLists]-->1.<!--[endif]-->调用Adapter的supportsAdvice方法,判断取得的Advice属于什么类型的Advice通知,从而根据不同的Advice类型来注册不同的AdviceInterceptor。

MethodBeforeAdviceAdapter将MethodBeforeAdvice适配成MethodBeforeAdviceInterceptor;

AfterReturningAdviceAdapter将AfterReturningAdvice适配成AfterReturningAdviceInterceptor;

ThrowsAdviceAdapter将ThrowsAdvice适配成ThrowsAdviceInterceptor。

2.这些AdviceInterceptor都是Spring AOP框架设计好了的,是为实现不同的Advice功能提供服务的。正是这些AdviceInterceptor最终实现了Advice通知在AopProxy代理对象中的织入功能。

 

AopProxy代理对象触发的ReflectiveMethodInvocation的proceed()方法中,在取得了拦截器后,启动了对拦截器的invoke调用,最终会根据不同的Advice类型,触发Spring对不同的Advice的拦截器封装,比如对MethodBeforeAdvice,最终会触发MethodBeforeAdviceInterceptor的invoke方法,它会先调用Advice的before方法,然后才是MethodInvocation的proceed方法调用,这就是MethodBeforeAdvice所需要的对目标对象的增强效果:在方法调用之前完成通知增强。

 

MVC

上下文在Web容器中的启动

ContextLoaderListener监听器负责完成IoC容器在Web环境中的启动工作,ServletContext为Spring的IoC容器提供了一个宿主环境。由ContextLoaderListener启动的上下文为根上下文,在根上下文的基础上,还有一个与Web MVC相关的上下文用来保存控制器(DispatcherServlet)需要的MVC对象,作为跟上下文的子上下文,构成一个层次化的上下文体系。在Web容器中启动Spring应用程序时,首先建立跟上下文,然后建立这个上下文体系,具体由ContextLoader来完成。

ContextLoaderListener实现了ServletContextListener接口,这个接口里的函数会结合Web容器的生命周期被调用。因为ServletContextListener是ServletContext的监听者,如果ServletContext发生变化,会触发相应的事件,而监听器一直在对这些事件进行监听,如果接收到了监听的事件,就会做出预先设计好的响应动作。对应这些事件及Web容器状态的变化,在监听器中定义了对应的事件响应的回调方法。由于ServletContext的变化而触发的监听器的响应具体包括:在服务器启动时,ServletContext被创建的时候ServletContextListener的contextInitialized()方法被调用;服务器关闭时,ServletContext被销毁的时候ServletContextListener的contextDestroyed()方法被调用。

ContextLoaderListener的contextInitialized方法中,会调用ContextLoader的初始化方法initWebApplicationContext来完成根上下文在Web容器中的创建。根上下文创建成功后,会被存到Web容器的ServletContext中去,供需要时使用。根上下文路径默认在接口WebApplicationContext设置为:

String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";默认的IoC容器是XmlWebApplicationContext。

 

SpringMVC的设计与实现

DispatcherServlet的启动和初始化 P160

在完成对ContextLoaderListener的初始化以后,Web容器开始初始化DispatcherServletDispatcherServlet会建立自己的上下文来持有SpringMVC的Bean对象,在建立这个自己持有的IoC容器时,会从ServletContext中得到根上下文作为DispatcherServlet持有上下文的双亲上下文。有了自己的上下文,再对自己持有的上下文进行初始化,最后把自己持有的这个上下文保存到ServletContext,供以后检索和使用。

初始化调用流程:HttpServletBean的init(),FrameworkServlet的initServletBean(),DispatcherServlet的initStrategies(ApplicationContext context)方法。在这个initStrategies方法里启动整个SpringMVC框架的初始化。

 

SpringMVC工作流程

(主要在doDispatch方法里)

1.用户向服务器发送请求,请求被Spring前端控制器DispatcherServlet捕获;

2.DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;

3.DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)

4.提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

HttpMessageConveter:将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息;

数据转换:对请求消息进行数据转换。如String转换成Integer、Double等;

数据格式化:对请求消息进行数据格式化。如将字符串转换成格式化数字或格式化日期等;

数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。

5.Handler执行完成后,向DispatcherServlet返回一个ModelAndView对象;执行行拦截器的postHandle(...)方法;

6.根据返回的ModelAndView,选择一个适合的ViewResolver解析得到一个View(必须是已经注册到Spring容器中的ViewResolver);

7.DispatcherServlet把获得的模型数据交给特定的视图对象,完成这些数据的视图呈现工作,具体由视图对象Viewrender方法来完成。

8.将渲染结果返回给客户端。

分享到:
评论

相关推荐

    Spring源码学习一:源码分析概述1

    Spring源码学习概述 Spring是Java生态系统中的一种流行的开源框架,由Rod Johnson创立于2003年。Spring框架的主要目标是使Java应用程序的开发变得更加简洁、灵活和可维护。Spring框架的核心思想是基于依赖注入...

    spring源码学习之思维导图

    1、spring 的整体架构 2、spring的基本实现(xml的加载原理、标签解析、bean加载) 3、容器扩展 4、ioc和aop 5、事务 6、springmvc 7、dispatcherServlet

    spring源码学习

    在"Spring源码学习"的过程中,你需要阅读和理解上述各个模块的核心类和接口,观察它们如何协同工作。Spring框架的设计思想和实现方式对于提升Java开发技能和理解软件设计原则非常有帮助。通过分析源码,你可以更深入...

    struts+hibernate+spring源码学习:hr考勤管理系统.rar

    struts+hibernate+spring源码学习:hr考勤管理系统.rar

    Spring源码学习工具(包括编译版源码、未编译版源码和相关工具)

    Spring源码学习是深入理解这个框架工作原理的重要途径。这份"Spring源码学习工具"包含了编译版和未编译版的源码,以及相关辅助工具,旨在帮助开发者更有效地探索Spring的内部机制。 首先,让我们谈谈编译版和未编译...

    Spring源码学习加注释,方便学习.zip

    spring源码中文注释方便学习,spring源码学习加注释。 spring源码中文注释方便学习,spring源码学习加注释。 spring源码中文注释方便学习,spring源码学习加注释。 spring源码中文...

    Spring学习笔记&源码

    本资料“Spring学习笔记&源码”是基于网易云课堂黑马程序员的Spring四天精通课程,旨在帮助学习者深入理解和实践Spring框架。 笔记部分可能会涵盖以下内容: 1. **Spring概述**:介绍Spring框架的历史、特点和主要...

    struts+hibernate+spring源码学习:BBS系统

    struts+hibernate+spring源码学习:BBS系统 注意:此资源已经更新,请大家下载 struts+hibernate+spring源码学习:BBS系统(6月5日上传版) 页面: http://download.csdn.net/source/1383098

    SpringMVC精品资源--spring源码学习附注释(Version 4.2.0),the second deb.zip

    在标题中提到的"Spring源码学习附注释(Version 4.2.0)",这部分内容指的是Spring框架的核心源代码,版本为4.2.0。源码学习对于深入理解Spring的工作原理和扩展自定义功能至关重要。注释的加入使得源码更易于阅读和...

    Spring高级源码学习笔记.zip

    总之,Spring源码学习是一个深化编程技能,理解设计模式,以及提高问题解决能力的过程。通过深入研究,程序员不仅可以优化自己的代码,还能更高效地利用Spring框架提供的功能,提升项目的可维护性和扩展性。

    Spring源码学习文档,绝对值得好好研究~~

    Spring源代码解析(一)Spring中的事务处理.doc Spring源代码解析(二):ioc容器在Web容器中的启动.doc Spring源代码分析(三):Spring JDBC.doc Spring源代码解析(四):Spring MVC.doc Spring源代码解析(五):Spring ...

    马士兵老师spring框架学习笔记

    马士兵老师是知名的Java教育专家,他的Spring框架学习笔记深入浅出,对于初学者和进阶者来说都是一份宝贵的资源。这份笔记涵盖了Spring的核心概念、配置、AOP(面向切面编程)、DI(依赖注入)等关键知识点。 1. **...

    struts+hibernate+spring源码学习:BBS系统(6月5日上传版)

    通过分析和学习这个BBS系统的源码,你可以深入了解Struts的请求处理流程,Hibernate的对象映射机制,以及Spring如何协调各个组件工作。这对于提升Java Web开发能力,尤其是企业级应用开发技能,是非常有价值的实践。...

    Spring源码学习-JPetStore.part3

    spring自带的JPetStore,我已经配置好(数据库也配置好,用的是hsqldb),可以直接导 入eclipse中运行。共3个压缩包

    SpringStudy:GitHub下载的Spring源码,用于Spring源码学习

    Spring框架 这是Spring框架的所在地:所有的基础。 总体来说,Spring框架和Spring项目系列通常简称为“ Spring”。 Spring提供了Java编程语言以外的所有所需内容,可用于为各种场景和体系结构创建企业应用程序。 请...

    Spring源码学习-JPetStore.part1

    spring自带的JPetStore,我已经配置好(数据库也配置好,用的是hsqldb),可以直接导入eclipse中运行。共3个压缩包

    Spring源码学习-JPetStore.part2

    spring自带的JPetStore,我已经配置好(数据库也配置好,用的是hsqldb),可以直接导 入eclipse中运行。共3个压缩包

    spring 源码环境搭建

    "spring源码" 是指 Spring 框架的源代码。 部分内容解释 1. 下载 GitHub 客户端安装 下载 GitHub 客户端是因为 Spring 源码托管在 GitHub 上,所以我们需要下载 GitHub 客户端来 clone Spring 源码。安装成功后,...

    spring源码注释中文

    Spring 源码注释中文版的提供,使得开发者能够更加深入地理解 Spring 的工作原理,无需经过复杂的编译过程,可以直接阅读源码注释来学习。 Spring 框架主要由以下几个关键模块组成: 1. **Core Container(核心...

Global site tag (gtag.js) - Google Analytics