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容器开始初始化DispatcherServlet。DispatcherServlet会建立自己的上下文来持有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把获得的模型数据交给特定的视图对象,完成这些数据的视图呈现工作,具体由视图对象View的render方法来完成。
8.将渲染结果返回给客户端。
相关推荐
Spring源码学习概述 Spring是Java生态系统中的一种流行的开源框架,由Rod Johnson创立于2003年。Spring框架的主要目标是使Java应用程序的开发变得更加简洁、灵活和可维护。Spring框架的核心思想是基于依赖注入...
1、spring 的整体架构 2、spring的基本实现(xml的加载原理、标签解析、bean加载) 3、容器扩展 4、ioc和aop 5、事务 6、springmvc 7、dispatcherServlet
在"Spring源码学习"的过程中,你需要阅读和理解上述各个模块的核心类和接口,观察它们如何协同工作。Spring框架的设计思想和实现方式对于提升Java开发技能和理解软件设计原则非常有帮助。通过分析源码,你可以更深入...
struts+hibernate+spring源码学习:hr考勤管理系统.rar
Spring源码学习是深入理解这个框架工作原理的重要途径。这份"Spring源码学习工具"包含了编译版和未编译版的源码,以及相关辅助工具,旨在帮助开发者更有效地探索Spring的内部机制。 首先,让我们谈谈编译版和未编译...
spring源码中文注释方便学习,spring源码学习加注释。 spring源码中文注释方便学习,spring源码学习加注释。 spring源码中文注释方便学习,spring源码学习加注释。 spring源码中文...
本资料“Spring学习笔记&源码”是基于网易云课堂黑马程序员的Spring四天精通课程,旨在帮助学习者深入理解和实践Spring框架。 笔记部分可能会涵盖以下内容: 1. **Spring概述**:介绍Spring框架的历史、特点和主要...
struts+hibernate+spring源码学习:BBS系统 注意:此资源已经更新,请大家下载 struts+hibernate+spring源码学习:BBS系统(6月5日上传版) 页面: http://download.csdn.net/source/1383098
在标题中提到的"Spring源码学习附注释(Version 4.2.0)",这部分内容指的是Spring框架的核心源代码,版本为4.2.0。源码学习对于深入理解Spring的工作原理和扩展自定义功能至关重要。注释的加入使得源码更易于阅读和...
总之,Spring源码学习是一个深化编程技能,理解设计模式,以及提高问题解决能力的过程。通过深入研究,程序员不仅可以优化自己的代码,还能更高效地利用Spring框架提供的功能,提升项目的可维护性和扩展性。
Spring源代码解析(一)Spring中的事务处理.doc Spring源代码解析(二):ioc容器在Web容器中的启动.doc Spring源代码分析(三):Spring JDBC.doc Spring源代码解析(四):Spring MVC.doc Spring源代码解析(五):Spring ...
马士兵老师是知名的Java教育专家,他的Spring框架学习笔记深入浅出,对于初学者和进阶者来说都是一份宝贵的资源。这份笔记涵盖了Spring的核心概念、配置、AOP(面向切面编程)、DI(依赖注入)等关键知识点。 1. **...
通过分析和学习这个BBS系统的源码,你可以深入了解Struts的请求处理流程,Hibernate的对象映射机制,以及Spring如何协调各个组件工作。这对于提升Java Web开发能力,尤其是企业级应用开发技能,是非常有价值的实践。...
spring自带的JPetStore,我已经配置好(数据库也配置好,用的是hsqldb),可以直接导 入eclipse中运行。共3个压缩包
Spring框架 这是Spring框架的所在地:所有的基础。 总体来说,Spring框架和Spring项目系列通常简称为“ Spring”。 Spring提供了Java编程语言以外的所有所需内容,可用于为各种场景和体系结构创建企业应用程序。 请...
spring自带的JPetStore,我已经配置好(数据库也配置好,用的是hsqldb),可以直接导入eclipse中运行。共3个压缩包
spring自带的JPetStore,我已经配置好(数据库也配置好,用的是hsqldb),可以直接导 入eclipse中运行。共3个压缩包
"spring源码" 是指 Spring 框架的源代码。 部分内容解释 1. 下载 GitHub 客户端安装 下载 GitHub 客户端是因为 Spring 源码托管在 GitHub 上,所以我们需要下载 GitHub 客户端来 clone Spring 源码。安装成功后,...
Spring 源码注释中文版的提供,使得开发者能够更加深入地理解 Spring 的工作原理,无需经过复杂的编译过程,可以直接阅读源码注释来学习。 Spring 框架主要由以下几个关键模块组成: 1. **Core Container(核心...