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)