修缮之前的博文

pull/28/head
AmyliaY 6 years ago
parent bf5bf3603e
commit 289fa63ab4

File diff suppressed because it is too large Load Diff

@ -1,43 +1,42 @@
最近在看spring AOP部分的源码所以对JDK动态代理具体是如何实现的这件事产生了很高的兴趣而且能从源码上了解这个原理的话也有助于对spring-aop模块的理解。话不多说上代码。 最近在看 Spring AOP 部分的源码所以对JDK动态代理具体是如何实现的这件事产生了很高的兴趣而且能从源码上了解这个原理的话也有助于对 spring-aop 模块的理解。话不多说,上代码。
```java ```java
/** /**
* 一般会使用实现了 InvocationHandler 的类作为代理对象的生产工厂, * 一般会使用实现了 InvocationHandler接口 的类作为代理对象的生产工厂,
* 并且通过持有被代理对象target来在invoke()方法中对被代理对象的目标方法进行调用和增强, * 并且通过持有 被代理对象target来在 invoke()方法 中对被代理对象的目标方法进行调用和增强,
* 这些我们都能通过下面这段代码看懂但代理对象是如何生成的invoke()方法又是如何被调用的呢? * 这些我们都能通过下面这段代码看懂但代理对象是如何生成的invoke()方法 又是如何被调用的呢?
*/ */
public class ProxyFactory implements InvocationHandler{ public class ProxyFactory implements InvocationHandler {
private Object target = null; private Object target = null;
public Object getInstanse(Object target){ public Object getInstanse(Object target){
this.target = target; this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this); target.getClass().getInterfaces(), this);
} }
@Override @Override
public Object invoke(Object proxy, Method method, Object[] args) public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable { throws Throwable {
Object ret = null; Object ret = null;
System.out.println("前置增强"); System.out.println("前置增强");
ret = method.invoke(target, args); ret = method.invoke(target, args);
System.out.println("后置增强"); System.out.println("后置增强");
return ret; return ret;
} }
} }
/** /**
* 实现了接口MyInterface和接口的play()方法,可以作为被代理类 * 实现了 接口MyInterface 和接口的 play()方法,可以作为被代理类
*/ */
public class TargetObject implements MyInterface { public class TargetObject implements MyInterface {
@Override @Override
public void play() { public void play() {
System.out.println("妲己,陪你玩 ~"); System.out.println("妲己,陪你玩 ~");
}
}
} }
/** /**
@ -45,58 +44,57 @@ public class TargetObject implements MyInterface {
*/ */
public class ProxyTest { public class ProxyTest {
public static void main(String[] args) { public static void main(String[] args) {
TargetObject target = new TargetObject(); TargetObject target = new TargetObject();
// ProxyFactory 实现了 InvocationHandler接口其中的 getInstanse() 方法利用 Proxy // ProxyFactory 实现了 InvocationHandler接口其中的 getInstanse()方法 利用 Proxy类
// 生成了target目标对象的代理对象并返回且ProxyFactory持有对target的引用,可以在 // 生成了 target目标对象 的代理对象,并返回;且 ProxyFactory 持有对 target 的引用,可以在
// invoke() 中完成对 target 相应方法的调用,以及目标方法前置后置的增强处理 // invoke() 中完成对 target 相应方法的调用,以及目标方法前置后置的增强处理
ProxyFactory proxyFactory = new ProxyFactory(); ProxyFactory proxyFactory = new ProxyFactory();
// 这个mi就是JDK的 Proxy 类动态生成的代理类 $Proxy0 的实例,该实例中的方法都持有对 // 这个 mi 就是 JDK 的 Proxy类 动态生成的代理类 $Proxy0 的实例,该实例中的方法都持有对
// invoke() 方法的回调,所以当调用其方法时,就能够执行 invoke() 中的增强处理 // invoke()方法 的回调,所以当调用其方法时,就能够执行 invoke() 中的增强处理
MyInterface mi = (MyInterface)proxyFactory.getInstanse(target); MyInterface mi = (MyInterface) proxyFactory.getInstanse(target);
// 这样可以看到 mi 的 Class 到底是什么 // 这样可以看到 mi 的 Class 到底是什么
System.out.println(mi.getClass()); System.out.println(mi.getClass());
// 这里实际上调用的就是 $Proxy0代理类中对 play() 方法的实现,结合下面的代码可以看到 // 这里实际上调用的就是 $Proxy0代理类 中对 play()方法 的实现,结合下面的代码可以看到
// play() 方法通过 super.h.invoke() 完成了对 InvocationHandler对象(proxyFactory)中 // play()方法 通过 super.h.invoke() 完成了对 InvocationHandler对象(proxyFactory)中
// invoke()方法的回调,所以我们才能够通过 invoke() 方法实现对 target 对象方法的 // invoke()方法 的回调,所以我们才能够通过 invoke()方法 实现对 target对象 方法的
// 前置后置增强处理 // 前置后置增强处理
mi.play(); mi.play();
// 总的来说就是在invoke()方法中完成target目标方法的调用,及前置后置增强, // 总的来说,就是在 invoke()方法 中完成 target目标方法 的调用,及前置后置增强,
// JDK动态生成的代理类中对 invoke() 方法进行了回调 // JDK 动态生成的代理类中对 invoke()方法 进行了回调
} }
/** /**
* 将ProxyGenerator生成的动态代理类的输出到文件中利用反编译工具luyten等就可 * 将 ProxyGenerator 生成的动态代理类的输出到文件中,利用反编译工具 luyten 等就可
* 以看到生成的代理类的源码咯,下面给出了其反编译好的代码实现 * 以看到生成的代理类的源码咯,下面给出了其反编译好的代码实现
*/ */
@Test @Test
public void generatorSrc(){ public void generatorSrc(){
byte[] bytesFile = ProxyGenerator.generateProxyClass("$Proxy0", TargetObject.class.getInterfaces()); byte[] bytesFile = ProxyGenerator.generateProxyClass("$Proxy0", TargetObject.class.getInterfaces());
FileOutputStream fos = null; FileOutputStream fos = null;
try{ try{
String path = System.getProperty("user.dir") + "\\$Proxy0.class"; String path = System.getProperty("user.dir") + "\\$Proxy0.class";
File file = new File(path); File file = new File(path);
fos = new FileOutputStream(file); fos = new FileOutputStream(file);
fos.write(bytesFile); fos.write(bytesFile);
fos.flush(); fos.flush();
} catch (Exception e){ } catch (Exception e){
e.printStackTrace(); e.printStackTrace();
} finally{ } finally{
try { try {
fos.close(); fos.close();
} catch (IOException e) { } catch (IOException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
} }
} }
} }
/** /**
* Proxy生成的代理类可以看到其继承了Proxy并且实现了被代理类的接口 * Proxy 生成的代理类,可以看到,其继承了 Proxy并且实现了 被代理类的接口MyInterface
*/ */
public final class $Proxy0 extends Proxy implements MyInterface public final class $Proxy0 extends Proxy implements MyInterface {
{
private static Method m1; private static Method m1;
private static Method m0; private static Method m0;
private static Method m3; private static Method m3;
@ -106,7 +104,7 @@ public final class $Proxy0 extends Proxy implements MyInterface
try { try {
$Proxy0.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); $Proxy0.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
$Proxy0.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]); $Proxy0.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]);
//实例化MyInterface的play方法 // 实例化 MyInterface play()方法
$Proxy0.m3 = Class.forName("com.shuitu.test.MyInterface").getMethod("play", (Class<?>[])new Class[0]); $Proxy0.m3 = Class.forName("com.shuitu.test.MyInterface").getMethod("play", (Class<?>[])new Class[0]);
$Proxy0.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]); $Proxy0.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]);
} }
@ -124,9 +122,9 @@ public final class $Proxy0 extends Proxy implements MyInterface
public final void play() { public final void play() {
try { try {
// 这个 h 其实就是我们调用 Proxy.newProxyInstance() 方法时传进去的ProxyFactory(InvocationHandler对象) // 这个 h 其实就是我们调用 Proxy.newProxyInstance()方法 时传进去的 ProxyFactory对象(它实现了
// 该对象的 invoke() 方法中实现了对目标对象的目标方法的增强。看到这里,利用动态代理实现方法增强的 // InvocationHandler接口),该对象的 invoke()方法 中实现了对目标对象的目标方法的增强。
// 实现原理就全部理清咯 // 看到这里,利用动态代理实现方法增强的实现原理就全部理清咯
super.h.invoke(this, $Proxy0.m3, null); super.h.invoke(this, $Proxy0.m3, null);
} }
catch (Error | RuntimeException error) { catch (Error | RuntimeException error) {

@ -1,116 +1,133 @@
这篇文章分享一下 spring IoC 容器初始化第三部分的代码,也就是将前面解析得到的 BeanDefinition 注册进 IoC 容器,其实就是存入一个 ConcurrentHashMap<String, BeanDefinition> 中。 ## 前言
这篇文章分享一下 spring IoC 容器初始化第三部分的代码,也就是将前面解析出来的 BeanDefinition对象 注册进 IoC 容器,其实就是存入一个 ConcurrentHashMap<String, BeanDefinition> 中。
PS可以结合我 GitHub 上对 spring 框架源码的翻译注解一起看,会更有助于各位同学理解,地址:
spring-beans https://github.com/AmyliaY/spring-beans-reading
PS可以结合我 GitHub 上对 Spring 框架源码的翻译注释一起看,会更有助于各位同学理解,地址:
spring-beans https://github.com/AmyliaY/spring-beans-reading
spring-context https://github.com/AmyliaY/spring-context-reading spring-context https://github.com/AmyliaY/spring-context-reading
## 1、回过头看一下前面在 DefaultBeanDefinitionDocumentReader 中实现的 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法
## 正文
回过头看一下前面在 DefaultBeanDefinitionDocumentReader 中实现的 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法。
```java ```java
// 解析 Bean 定义资源 Document 对象的普通元素 /**
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { * 将 .xml 文件中的元素解析成 BeanDefinition对象并注册到 IoC容器 中
*/
// BeanDefinitionHolder 是对 BeanDefinition 的封装,即 BeanDefinition 的封装类 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 对 Document 对象中 <Bean> 元素的解析由 BeanDefinitionParserDelegate 实现
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); // BeanDefinitionHolder 是对 BeanDefinition 的进一步封装,它持有一个 BeanDefinition 对象 及其对应
if (bdHolder != null) { // 的 beanName、aliases别名。
// 对 bdHolder 进行包装处理 // 对 Document 对象中 <Bean> 元素的解析由 BeanDefinitionParserDelegate 实现
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
try { if (bdHolder != null) {
/** // 对 bdHolder 进行包装处理
* bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
* 向 Spring IoC 容器注册解析 BeanDefinition这是 BeanDefinition 向 IoC 容器注册的入口 try {
* /**
*/ *
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); * 向 IoC 容器注册解析完成的 BeanDefinition对象这是 BeanDefinition 向 IoC 容器注册的入口
} *
catch (BeanDefinitionStoreException ex) { */
getReaderContext().error("Failed to register bean definition with name '" + BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
bdHolder.getBeanName() + "'", ele, ex); }
} catch (BeanDefinitionStoreException ex) {
// 在完成向 Spring IOC 容器注册解析得到的 Bean 定义之后,发送注册事件 getReaderContext().error("Failed to register bean definition with name '" +
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); bdHolder.getBeanName() + "'", ele, ex);
} }
} // 在完成向 IOC容器 注册 BeanDefinition对象 之后,发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
``` ```
## 2、BeanDefinitionReaderUtils 的 registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 方法 接着看一下 BeanDefinitionReaderUtils 的 registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 方法
```java ```java
// 将解析的 BeanDefinitionHold 注册到容器中 /**
public static void registerBeanDefinition( * 将解析到的 BeanDefinition对象 注册到 IoC容器
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) */
throws BeanDefinitionStoreException { public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 获取解析的 BeanDefinition 的名称 // 获取解析的 <bean>元素 的名称 beanName
String beanName = definitionHolder.getBeanName(); String beanName = definitionHolder.getBeanName();
/** /**
* *
* 开始向 IOC 容器注册 BeanDefinition * 开始向 IoC容器 注册 BeanDefinition对象
* *
*/ */
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 如果解析的 BeanDefinition 有别名,向容器为其注册别名 // 如果解析的 <bean>元素 有别名alias向容器中注册别名
String[] aliases = definitionHolder.getAliases(); String[] aliases = definitionHolder.getAliases();
if (aliases != null) { if (aliases != null) {
for (String aliase : aliases) { for (String aliase : aliases) {
registry.registerAlias(beanName, aliase); registry.registerAlias(beanName, aliase);
} }
} }
} }
``` ```
## 3、BeanDefinitionRegistry 中的 registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法在 DefaultListableBeanFactory 实现类中的具体实现 BeanDefinitionRegistry 中的 registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法在 DefaultListableBeanFactory 实现类中的具体实现
```java ```java
// 向 IoC 容器注册解析的 BeanDefinito public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty"); /** 按注册顺序排列的 beanDefinition名称列表(即 beanName) */
Assert.notNull(beanDefinition, "BeanDefinition must not be null"); private final List<String> beanDefinitionNames = new ArrayList<String>();
// 校验解析的 BeanDefiniton /** IoC容器 的实际体现key --> beanNamevalue --> BeanDefinition对象 */
if (beanDefinition instanceof AbstractBeanDefinition) { private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
try {
((AbstractBeanDefinition) beanDefinition).validate(); /**
} * 向 IoC容器 注册解析的 beanName 和 BeanDefinition对象
catch (BeanDefinitionValidationException ex) { */
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
"Validation of bean definition failed", ex); throws BeanDefinitionStoreException {
}
} Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
// 注册的过程中需要线程同步,以保证数据的一致性
synchronized (this.beanDefinitionMap) { // 校验解析的 BeanDefiniton对象
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 检查是否有同名的 BeanDefinition 已经在 IOC 容器中注册,如果已经注册, ((AbstractBeanDefinition) beanDefinition).validate();
// 并且不允许覆盖已注册的 BeanDefinition则抛出注册失败异常 }
// allowBeanDefinitionOverriding 默认为 true catch (BeanDefinitionValidationException ex) {
if (oldBeanDefinition != null) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
if (!this.allowBeanDefinitionOverriding) { "Validation of bean definition failed", ex);
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, }
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + }
"': There is already [" + oldBeanDefinition + "] bound.");
} // 注册的过程中需要线程同步,以保证数据的一致性
else {// 如果允许覆盖,则同名的 Bean后注册的覆盖先注册的 synchronized (this.beanDefinitionMap) {
if (this.logger.isInfoEnabled()) { Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); // 检查是否有同名(beanName)的 BeanDefinition 存在于 IoC容器 中,如果已经存在,且不允许覆盖
} // 已注册的 BeanDefinition则抛出注册异常allowBeanDefinitionOverriding 默认为 true
} if (oldBeanDefinition != null) {
} if (!this.allowBeanDefinitionOverriding) {
else {// IOC 容器中没有已经注册同名的 Bean按正常注册流程注册 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
this.beanDefinitionNames.add(beanName); "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
this.frozenBeanDefinitionNames = null; "': There is already [" + oldBeanDefinition + "] bound.");
} }
this.beanDefinitionMap.put(beanName, beanDefinition); // 如果允许覆盖同名的 bean后注册的会覆盖先注册的
} else {
// 重置所有已经注册过的 BeanDefinition 的缓存 if (this.logger.isInfoEnabled()) {
resetBeanDefinition(beanName); this.logger.info("Overriding bean definition for bean '" + beanName +
} "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
``` }
## 最后看一下 spring 的 IoC 容器在代码中最直接的体现 }
```java }
// 存储注册信息的 BeanDefinition 集合,也就是所谓的 IoC 容器 // 若该 beanName 在 IoC容器 中尚未注册,将其注册到 IoC容器中
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64); else {
// 将 beanName 注册到 beanDefinitionNames列表
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
// beanDefinitionMap 是 IoC容器 的最主要体现,他是一个 ConcurrentHashMap
// 直接存储了 bean的唯一标识 beanName及其对应的 BeanDefinition对象
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// 重置所有已经注册过的 BeanDefinition 的缓存
resetBeanDefinition(beanName);
}
}
``` ```

File diff suppressed because it is too large Load Diff

@ -1,9 +1,9 @@
## 1 Web环境中的SpringMVC ## 1 Web环境中的SpringMVC
Web环境中SpringMVC是建立在IoC容器基础上的。了解SpringMVC首先要了解Spring的IoC容器是如何在Web环境中被载人并起作用的。 Web环境 中SpringMVC 是建立在 IoC容器 基础上的。了解 SpringMVC首先要了解 Spring 的 IoC容器 是如何在 Web环境 中被载入并起作用的。
Spring的IoC是一个独立模块它并不直接在Web容器中发挥作用如果要在Web环境中使用IoC容器需要Spring为IoC设计一个启动过程把IoC容器导入并在Web容器中建立起来。具体说来这个启动过程是和Web容器的启动过程集成在一起的。在这个过程中一方面处理Web容器的启动另一方面通过设计特定的Web容器拦截器将IoC容器载人到Web环境中来并将其初始化。在这个过程建立完成以后IoC容器才能正常工作而SpringMVC是建立在IoC容器的基础上的这样才能建立起MVC框架的运行机制从而响应从Web容器传递的HTTP请求。 Spring IoC 是一个独立模块,它并不直接在 Web容器 中发挥作用,如果要在 Web环境 中使用 IoC容器需要 Spring IoC 设计一个启动过程,把 IoC容器 导入,并在 Web容器 中建立起来。具体说来,这个启动过程是和 Web容器 的启动过程集成在一起的。在这个过程中,一方面处理 Web容器 的启动,另一方面通过设计特定的 Web容器拦截器 IoC容器 载人到 Web环境 中来并将其初始化。在这个过程建立完成以后IoC容器 才能正常工作,而 SpringMVC 是建立在 IoC容器 的基础上的,这样才能建立起 MVC框架 的运行机制,从而响应从 Web容器 传递的 HTTP请求。
下面以Tomcat作为Web容器的例子进行分析。在Tomcat中web.xml是应用的部署描述文件。在web.xml中常常经常能看到与Spring相关的部署描述。 下面以 Tomcat 作为 Web容器 的例子进行分析。在 Tomcat web.xml 是应用的部署描述文件。在 web.xml 中常常经常能看到与 Spring 相关的部署描述。
```xml ```xml
<servlet> <servlet>
<servlet-name>sample</servlet-name> <servlet-name>sample</servlet-name>
@ -22,320 +22,318 @@ Spring的IoC是一个独立模块它并不直接在Web容器中发挥作用
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> </listener>
``` ```
web.xml是SpringMVC与Tomcat的接口部分。这个部署描述文件中首先定义了一个Servlet对象它是SpringMVC的DispatcherServlet。这个DispatcherServlet是MVC中很重要的一个类起着分发请求的作用。 web.xml SpringMVC Tomcat 的接口部分。这个部署描述文件中,首先定义了一个 Servlet对象它是 SpringMVC DispatcherServlet。这个 DispatcherServlet MVC 中很重要的一个类,起着分发请求的作用。
同时在部署描述中还为这个DispatcherServlet定义了对应的URL映射以指定这个Servlet需要处理的HTTP请求范围。context-param参数用来指定IoC容器读取Bean的XML文件的路径在这里这个配置文件被定义为WEB-INF/applicationContext.xml。其中可以看到Spring应用的Bean配置。 同时,在部署描述中,还为这个 DispatcherServlet 定义了对应的 URL映射以指定这个 Servlet 需要处理的 HTTP请求范围。context-param参数 用来指定 IoC容器 读取 Bean XML文件 的路径,在这里,这个配置文件被定义为 WEB-INF/applicationContext.xml。其中可以看到 Spring应用 Bean配置。
最后作为Spring MVC的启动类ContextLoaderListener被定义为一个监听器这个监听器是与Web服务器的生命周期相关联的由ContextLoaderListener监听器负责完成 IoC容器在Web环境中的启动工作。 最后,作为 Spring MVC 的启动类ContextLoaderListener 被定义为一个监听器,这个监听器是与 Web服务器 的生命周期相关联的,由 ContextLoaderListener监听器 负责完成 IoC容器 Web环境 中的启动工作。
DispatchServlet和ContextLoaderListener提供了在Web容器中对Spring的接口也就是说这些接口与Web容器耦合是通过ServletContext来实现的ServletContext是容器和应用沟通的桥梁从一定程度上讲ServletContext就是servlet规范的体现。这个ServletContext为Spring的IoC容器提供了一个宿主环境在宿主环境中Spring MVC建立起一个IoC容器的体系。这个IoC容器体系是通过ContextLoaderListener的初始化来建立的在建立IoC容器体系后把DispatchServlet作为Spring MVC处理Web请求的转发器建立起来从而完成响应HTTP请求的准备。有了这些基本配置建立在IoC容器基础上的SpringMVC就可以正常地发挥作用了。下面我们看一下loC容器在Web容器中的启动代码实现。 DispatchServlet ContextLoaderListener 提供了在 Web容器 中对 Spring 的接口,也就是说,这些接口与 Web容器 耦合是通过 ServletContext 来实现的ServletContext 是容器和应用沟通的桥梁,从一定程度上讲 ServletContext 就是 servlet规范 的体现)。这个 ServletContext Spring IoC容器 提供了一个宿主环境在宿主环境中Spring MVC 建立起一个 IoC容器 的体系。这个 IoC容器体系 是通过 ContextLoaderListener 的初始化来建立的,在建立 IoC容器体系 后,把 DispatchServlet 作为 Spring MVC 处理 Web请求 的转发器建立起来,从而完成响应 HTTP请求 的准备。有了这些基本配置,建立在 IoC容器 基础上的 SpringMVC 就可以正常地发挥作用了。下面我们看一下 IoC容器 在 Web容器 中的启动代码实现。
## 2 IoC容器启动的基本过程 ## 2 IoC容器启动的基本过程
IoC容器的启动过程就是建立上下文的过程该上下文是与ServletContext相伴而生的同时也是IoC容器在Web应用环境中的具体表现之一。由ContextLoaderListener启动的上下文为根上下文。在根上下文的基础上还有一个与Web MVC相关的上下文用来保存控制器(DispatcherServlet)需要的MVC对象作为根上下文的子上下文构成一个层次化的上下文体系。在Web容器中启动Spring应用程序时首先建立根上下文然后建立这个上下文体系这个上下文体系的建立是由ContextLoder来完成的其UML时序图如下图所示。 IoC容器 的启动过程就是建立上下文的过程,该上下文是与 ServletContext 相伴而生的,同时也是 IoC容器 Web应用环境 中的具体表现之一。由 ContextLoaderListener 启动的上下文为根上下文。在根上下文的基础上,还有一个与 Web MVC 相关的上下文用来保存控制器(DispatcherServlet)需要的 MVC对象作为根上下文的子上下文构成一个层次化的上下文体系。在 Web容器 中启动 Spring应用程序 时,首先建立根上下文,然后建立这个上下文体系,这个上下文体系的建立是由 ContextLoder 来完成的,其 UML时序图 如下图所示。
![avatar](/images/springMVC/Web容器启动spring应用程序过程图.png) ![avatar](/images/springMVC/Web容器启动spring应用程序过程图.png)
在web.xml中已经配置了ContextLoaderListener它是Spring提供的类是为在Web容器中建立IoC容器服务的它实现了ServletContextListener接口这个接口是在Servlet API中定义的提供了与Servlet生命周期结合的回调比如上下文初始化contextInitialized()方法和上下文销毁contextDestroyed()方法。而在Web容器中建立WebApplicationContext的过程是在contextInitialized()方法中完成的。另外ContextLoaderListener还继承了ContextLoader具体的载入IoC容器的过程是由ContextLoader来完成的。 web.xml 中,已经配置了 ContextLoaderListener它是 Spring 提供的类,是为在 Web容器 中建立 IoC容器 服务的,它实现了 ServletContextListener接口这个接口是在 Servlet API 中定义的,提供了与 Servlet生命周期 结合的回调,比如上下文初始化 contextInitialized()方法 上下文销毁 contextDestroyed()方法。而在 Web容器 中,建立 WebApplicationContext 的过程,是在 contextInitialized()方法 中完成的。另外ContextLoaderListener 还继承了 ContextLoader具体的载入 IoC容器 的过程是由 ContextLoader 来完成的。
在ContextLoader中完成了两个IoC容器建立的基本过程一个是在Web容器中建立起双亲IoC容器另一个是生成相应的WebApplicationContext并将其初始化。 ContextLoader 中,完成了两个 IoC容器 建立的基本过程,一个是在 Web容器 中建立起 双亲IoC容器另一个是生成相应的 WebApplicationContext 并将其初始化。
## 3 Web容器中的上下文设计 ## 3 Web容器中的上下文设计
先从Web容器中的上下文入手看看Web环境中的上下文设置有哪些特别之处然后再 先从 Web容器 中的上下文入手,看看 Web环境 中的上下文设置有哪些特别之处,然后再到 ContextLoaderListener 中去了解整个容器启动的过程。为了方便在 Web环境 中使用 IoC容器
到ContextLoaderListener中去了解整个容器启动的过程。为了方便在Web环境中使用IoC容器 Spring 为 Web应用 提供了上下文的扩展接口 WebApplicationContext 来满足启动过程的需要,其继承关系如下图所示。
Spring为Web应用提供了上下文的扩展接口WebApplicationContext来满足启动过程的需要其继承关系如下图所示。
![avatar](/images/springMVC/WebApplicationContext接口的类继承关系.png) ![avatar](/images/springMVC/WebApplicationContext接口的类继承关系.png)
在这个类继承关系中可以从熟悉的XmlWebApplicationContext入手来了解它的接口实现。在接口设计中最后是通过ApplicationContex接口与BeanFactory接口对接的而对于具体的功能实现很多都是封装在其基类AbstractRefreshableWebApplicationContext中完成的。 在这个类继承关系中,可以从熟悉的 XmlWebApplicationContext 入手来了解它的接口实现。在接口设计中,最后是通过 ApplicationContex接口 BeanFactory接口 对接的,而对于具体的功能实现,很多都是封装在其基类 AbstractRefreshableWebApplicationContext 中完成的。
同样在源代码中也可以分析出类似的继承关系在WebApplicationContext中可以看到相关的常量设计比如ROOT_ WEB_ APPLICATION_CONTEXT_ATTRIBUTE等这个常量是用来索引在ServletContext中存储的根上下文的。这个接口类定义的接口方法比较简单在这个接口中定义了一 同样,在源代码中,也可以分析出类似的继承关系,在 WebApplicationContext 中可以看到相关的常量设计,比如 ROOT_ WEB_ APPLICATION_CONTEXT_ATTRIBUTE 等,这个常量是用来索引在 ServletContext 中存储的根上下文的。这个接口类定义的接口方法比较简单,在这个接口中,定义了一
个getServletContext方法通过这个方法可以得到当前Web容器的Servlet上下文环境通过 getServletContext()方法,通过这个方法可以得到当前 Web容器 Servlet上下文环境通过
这个方法相当于提供了一个Web容器级别的全局环境。 这个方法,相当于提供了一个 Web容器级别的 全局环境。
```java ```java
public interface WebApplicationContext extends ApplicationContext { public interface WebApplicationContext extends ApplicationContext {
/** /**
* 该常量用于在ServletContext中存取根上下文 * 该常量用于在 ServletContext 中存取根上下文
*/ */
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
/** /**
* 对于WebApplicationContext来说需要得到Web容器的ServletContext * 对于 WebApplicationContext 来说,需要得到 Web容器 的 ServletContext
*/ */
ServletContext getServletContext(); ServletContext getServletContext();
} }
``` ```
在启动过程中Spring会使用一个默认的WebApplicationContext实现作为IoC容器这个默认使用的IoC容器就是XmlWebApplicationContext它继承了ApplicationContext在ApplicationContext的基础上增加了对Web环境和XML配置定义的处理。在XmlWebApplicationContext的初始化过程中Web容器中的IoC容器被建立起来从而在Web容器中建立起整个Spring应用。与前面博文中分析的IoC容器的初始化一样这个过程也有loadBeanDefinition对BeanDefinition的载入。在Web环境中对定位BeanDefinition的Resource有特别的要求这个要求的处理体现在对getDefaultConfigLocations方法的处理中。这里使用了默认的BeanDefinition的配置路径这个路径在XmlWebApplicationContext中作为一个常量定义好了即/WEB-INF/applicationContext.xml。 在启动过程中Spring 会使用一个默认的 WebApplicationContext 实现作为 IoC容器这个默认使用的 IoC容器 就是 XmlWebApplicationContext它继承了 ApplicationContext ApplicationContext 的基础上,增加了对 Web环境 XML配置定义 的处理。在 XmlWebApplicationContext 的初始化过程中Web容器 中的 IoC容器 被建立起来,从而在 Web容器 中建立起整个 Spring应用。与前面博文中分析的 IoC容器 的初始化一样,这个过程也有 loadBeanDefinition()方法 BeanDefinition 的载入。在 Web环境 中,对定位 BeanDefinition Resource 有特别的要求,这个要求的处理体现在对 getDefaultConfigLocations()方法 的处理中。这里使用了默认的 BeanDefinition 的配置路径,这个路径在 XmlWebApplicationContext 中作为一个常量定义好了,即 /WEB-INF/applicationContext.xml。
```java ```java
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext { public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
/** 若不指定其它文件spring默认从"/WEB-INF/applicationContext.xml"目录文件 初始化IoC容器 */ /** 若不指定其它文件Spring 默认从 "/WEB-INF/applicationContext.xml" 目录文件 初始化 IoC容器 */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"; public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
/** 默认的配置文件在 /WEB-INF/ 目录下 */ /** 默认的配置文件在 /WEB-INF/ 目录下 */
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/"; public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
/** 默认的配置文件后缀名为.xml */ /** 默认的配置文件后缀名为 .xml */
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml"; public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
/** /**
* 此加载过程在 容器refresh()时启动 * 此加载过程在容器 refresh() 时启动
*/ */
@Override @Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 使用XmlBeanDefinitionReader对指定的BeanFactory进行解析 // 使用 XmlBeanDefinitionReader 对指定的 BeanFactory 进行解析
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 初始化beanDefinitionReader的属性其中设置ResourceLoader是因为 // 初始化 beanDefinitionReader 的属性,其中,设置 ResourceLoader 是因为 XmlBeanDefinitionReader
// XmlBeanDefinitionReader是DefaultResource的子类所有这里同样会使用 // 是 DefaultResource 的子类,所有这里同样会使用 DefaultResourceLoader 来定位 BeanDefinition
// DefaultResourceLoader来定位BeanDefinition beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 该方法是一个空实现
// 该方法是一个空实现 initBeanDefinitionReader(beanDefinitionReader);
initBeanDefinitionReader(beanDefinitionReader); // 使用初始化完成的 beanDefinitionReader 来加载 BeanDefinitions
// 使用初始化完成的beanDefinitionReader来加载BeanDefinitions loadBeanDefinitions(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader); }
}
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) { }
}
/**
/** * 获取所有的配置文件,然后一个一个载入 BeanDefinition
* 获取所有的配置文件然后一个一个载入BeanDefinition */
*/ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { String[] configLocations = getConfigLocations();
String[] configLocations = getConfigLocations(); if (configLocations != null) {
if (configLocations != null) { for (String configLocation : configLocations) {
for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation);
reader.loadBeanDefinitions(configLocation); }
} }
} }
}
/**
/** * 获取默认路径 "/WEB-INF/***.xml" 下的配置文件,
* 获取默认路径"/WEB-INF/***.xml"下的配置文件, * 或者获取 "/WEB-INF/applicationContext.xml" 配置文件
* 或者获取"/WEB-INF/applicationContext.xml"配置文件 */
*/ @Override
@Override protected String[] getDefaultConfigLocations() {
protected String[] getDefaultConfigLocations() { if (getNamespace() != null) {
if (getNamespace() != null) { return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; }
} else {
else { return new String[] {DEFAULT_CONFIG_LOCATION};
return new String[] {DEFAULT_CONFIG_LOCATION}; }
} }
}
} }
``` ```
从上面的代码中可以看到在XmlWebApplicationContext中基本的上下文功能都已经通过类的继承获得这里需要处理的是如何获取Bean定义信息在这里就转化为如何在Web容器环境中获得Bean定义信息。在获得Bean定义信息之后后面的过程基本上就和前面分析的XmlFileSystemBeanFactory一样是通过XmlBeanDefinitionReader来载人Bean定义信息的,最终完成整个上下文的初始化过程。 从上面的代码中可以看到,在 XmlWebApplicationContext 中,基本的上下文功能都已经通过类的继承获得,这里需要处理的是,如何获取 BeanDefinition信息在这里就转化为如何在 Web容器环境 中获得 BeanDefinition信息。在获得 BeanDefinition信息 之后,后面的过程基本上就和前面分析的 XmlFileSystemBeanFactory 一样,是通过 XmlBeanDefinitionReader 来载入 BeanDefinition信息 的,最终完成整个上下文的初始化过程。
## 4 ContextLoader的设计与实现 ## 4 ContextLoader的设计与实现
对于Spring承载的Web应用而言可以指定在Web应用程序启动时载入IoC容器或者称为WebApplicationContext。这个功能是由ContextLoaderListener来完成的它是在Web容器中配置的监听器会监听Web容器的启动然后载入IoC容器。这个ContextLoaderListener通过使用ContextLoader来完成实际的WebApplicationContext也就是IoC容器的初始化工作。这个ContextLoader就像Spring应用程序在Web容器中的启动器。这个启动过程是在Web容器中发生的所以需要根据Web容器部署的要求来定义ContextLoader相关的配置在概述中已经看到了这里就不重复了。 对于 Spring 承载的 Web应用 而言,可以指定在 Web应用程序 启动时载入 IoC容器或者称为WebApplicationContext。这个功能是由 ContextLoaderListener 来完成的,它是在 Web容器 中配置的监听器,会监听 Web容器 的启动,然后载入 IoC容器。这个 ContextLoaderListener 通过使用 ContextLoader 来完成实际的 WebApplicationContext也就是 IoC容器 的初始化工作。这个 ContextLoader 就像 Spring应用程序 Web容器 中的启动器。这个启动过程是在 Web容器 中发生的,所以需要根据 Web容器 部署的要求来定义 ContextLoader相关的配置在概述中已经看到了这里就不重复了。
为了了解IoC容器在Web容器中的启动原理这里对启动器ContextLoaderListener的实现进行分析。**这个监听器是启动根IoC容器并把它载入到Web容器的主要功能模块也是整个Spring Web应用加载IoC的第一个地方**。从加载过程可以看到首先从Servlet事件中得到ServletContext然后可以读取配置在web.xml中的各个相关的属性值接着ContextLoader会实例化WebApplicationContext并完成其载人和初始化过程。这个被初始化的第一个上下文作为根上下文而存在这个根上下文载入后被绑定到Web应用程序的ServletContext上。任何需要访问根上下文的应用程序代码都可以从WebApplicationContextUtils类的静态方法中得到。 为了了解 IoC容器 Web容器 中的启动原理,这里对 启动器ContextLoaderListener 的实现进行分析。**这个监听器是启动 根IoC容器 并把它载入到 Web容器 的主要功能模块,也是整个 Spring Web应用 加载 IoC 的第一个地方**。从加载过程可以看到,首先从 Servlet事件 中得到 ServletContext然后可以读取配置在 web.xml 中的各个相关的属性值,接着 ContextLoader 会实例化 WebApplicationContext并完成其载人和初始化过程。这个被初始化的第一个上下文作为根上下文而存在这个根上下文载入后被绑定到 Web应用程序 ServletContext 上。任何需要访问根上下文的应用程序代码都可以从 WebApplicationContextUtils类 的静态方法中得到。
下面分析具体的根上下文的载人过程。在ContextLoaderListener中实现的是**ServletContextListener接口这个接口里的函数会结合Web容器的生命周期被调用**。因为ServletContextListener是ServletContext的监听者如果ServletContext发生变化会触发出相应的事件而监听器一直在对这些事件进行监听如果接收到了监听的事件就会做出预先设计好的响应动作。由于ServletContext的变化而触发的监听器的响应具体包括在服务器启动时ServletContext被创建的时候服务器关闭时ServletContext将被销毁的时候等。对应这些事件及Web容器状态的变化在监听器中定义了对应的事件响应的回调方法。比如在服务器启动时ServletContextListener的contextInitialized()方法被调用服务器将要关闭时ServletContextListener的contextDestroyed()方法被调用。了解了Web容器中监听器的工作原理下面看看服务器启动时 ContextLoaderListener的调用完成了什么。在这个初始化回调中创建了ContextLoader同时会利用创建出来的ContextLoader来完成IoC容器的初始化。 下面分析具体的根上下文的载人过程。在 ContextLoaderListener 中,实现的是 **ServletContextListener接口这个接口里的函数会结合 Web容器 的生命周期被调用**。因为 ServletContextListener ServletContext 的监听者,如果 ServletContext 发生变化,会触发出相应的事件,而监听器一直在对这些事件进行监听,如果接收到了监听的事件,就会做出预先设计好的响应动作。由于 ServletContext 的变化而触发的监听器的响应具体包括在服务器启动时ServletContext 被创建的时候服务器关闭时ServletContext 将被销毁的时候等。对应这些事件及 Web容器状态 的变化在监听器中定义了对应的事件响应的回调方法。比如在服务器启动时ServletContextListener contextInitialized()方法 被调用服务器将要关闭时ServletContextListener contextDestroyed()方法 被调用。了解了 Web容器 中监听器的工作原理,下面看看服务器启动时 ContextLoaderListener 的调用完成了什么。在这个初始化回调中,创建了 ContextLoader同时会利用创建出来的 ContextLoader 来完成 IoC容器 的初始化。
```java ```java
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
private ContextLoader contextLoader; private ContextLoader contextLoader;
/** /**
* 启动web应用的 根上下文 * 启动 web应用 的根上下文
*/ */
public void contextInitialized(ServletContextEvent event) { public void contextInitialized(ServletContextEvent event) {
// 由于本类直接继承了ContextLoader所以能直接使用ContextLoader来初始化IoC容器 // 由于本类直接继承了 ContextLoader所以能直接使用 ContextLoader 来初始化 IoC容器
this.contextLoader = createContextLoader(); this.contextLoader = createContextLoader();
if (this.contextLoader == null) { if (this.contextLoader == null) {
this.contextLoader = this; this.contextLoader = this;
} }
// 具体的初始化工作交给ContextLoader完成 // 具体的初始化工作交给 ContextLoader 完成
this.contextLoader.initWebApplicationContext(event.getServletContext()); this.contextLoader.initWebApplicationContext(event.getServletContext());
} }
} }
public class ContextLoader { public class ContextLoader {
public static final String CONTEXT_CLASS_PARAM = "contextClass"; public static final String CONTEXT_CLASS_PARAM = "contextClass";
public static final String CONTEXT_ID_PARAM = "contextId"; public static final String CONTEXT_ID_PARAM = "contextId";
public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses"; public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation"; public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector"; public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";
public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey"; public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties"; private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
private static final Properties defaultStrategies; private static final Properties defaultStrategies;
static { static {
// Load default strategy implementations from properties file. // Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized // This is currently strictly internal and not meant to be customized
// by application developers. // by application developers.
try { try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
} }
catch (IOException ex) { catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage()); throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
} }
} }
/** /**
* 由ContextLoader完成根上下文在Web容器中的创建。这个根上下文是作为Web容器中唯一的实例而存在的, * 由 ContextLoader 完成根上下文在 Web容器 中的创建。这个根上下文是作为 Web容器 中唯一的实例而存在的,
* 根上下文创建成功后 会被存到Web容器的ServletContext中,供需要时使用。存取这个根上下文的路径是由 * 根上下文创建成功后会被存到 Web容器 的 ServletContext 中,供需要时使用。存取这个根上下文的路径是由
* Spring预先设置好的WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE中进行了定义 * Spring 预先设置好的,在 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 中进行了定义
*/ */
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// 如果ServletContext中已经包含了根上下文,则抛出异常 // 如果 ServletContext 中已经包含了根上下文,则抛出异常
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException( throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " + "Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!"); "check whether you have multiple ContextLoader* definitions in your web.xml!");
} }
Log logger = LogFactory.getLog(ContextLoader.class); Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext"); servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started"); logger.info("Root WebApplicationContext: initialization started");
} }
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
try { try {
if (this.context == null) { if (this.context == null) {
// 这里创建在ServletContext中存储的根上下文 // 这里创建在 ServletContext 中存储的根上下文
this.context = createWebApplicationContext(servletContext); this.context = createWebApplicationContext(servletContext);
} }
if (this.context instanceof ConfigurableWebApplicationContext) { if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) { if (!cwac.isActive()) {
if (cwac.getParent() == null) { if (cwac.getParent() == null) {
// 载入根上下文的 双亲上下文 // 载入根上下文的 双亲上下文
ApplicationContext parent = loadParentContext(servletContext); ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent); cwac.setParent(parent);
} }
// 配置 并且初始化IoC容器看到Refresh应该能想到AbstractApplicationContext // 配置并初始化 IoC容器看到下面方法中的 Refresh单词 应该能想到
// 中的refresh()方法猜到它是前面介绍的IoC容器的初始化入口 // AbstractApplicationContext 中的 refresh()方法,猜到它是前面介绍的 IoC容器 的初始化入口
configureAndRefreshWebApplicationContext(cwac, servletContext); configureAndRefreshWebApplicationContext(cwac, servletContext);
} }
} }
// 将上面创建的WebApplicationContext实例 存到ServletContext中,注意同时被存入的常量 // 将上面创建的 WebApplicationContext实例 存到 ServletContext 中,注意同时被存入的常量
// ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE以后的应用都会根据这个属性获取根上下文 // ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE以后的应用都会根据这个属性获取根上下文
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader(); ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) { if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context; currentContext = this.context;
} }
else if (ccl != null) { else if (ccl != null) {
currentContextPerThread.put(ccl, this.context); currentContextPerThread.put(ccl, this.context);
} }
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
} }
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime; long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
} }
return this.context; return this.context;
} }
catch (RuntimeException ex) { catch (RuntimeException ex) {
logger.error("Context initialization failed", ex); logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex; throw ex;
} }
catch (Error err) { catch (Error err) {
logger.error("Context initialization failed", err); logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err; throw err;
} }
} }
/** /**
* 创建WebApplicationContext的实例化对象 * 创建 WebApplicationContext 的实例化对象
*/ */
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// 判断使用什么样的类在Web容器中作为IoC容器 // 判断使用什么样的类在 Web容器 中作为 IoC容器
Class<?> contextClass = determineContextClass(sc); Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() + throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
} }
// 直接实例化需要产生的IoC容器 // 直接实例化需要产生的 IoC容器
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
} }
/** /**
* 在确定使用何种IoC容器的过程中可以看到应用可以在部署描述符中指定使用什么样的IoC容器 * 在确定使用何种 IoC容器 的过程中可以看到,应用可以在部署描述符中指定使用什么样的 IoC容器
* 这个指定操作是通过CONTEXT_ CLASS_ PARAM参数的设置完成的。如果没有指定特定的IoC容器 * 这个指定操作是通过 CONTEXT_ CLASS_ PARAM参数 的设置完成的。如果没有指定特定的 IoC容器
* 将使用默认的IoC容器也就是XmlWebApplicationContext对象作为在Web环境中使用的IoC容器。 * 将使用默认的 IoC容器也就是 XmlWebApplicationContext对象 作为在 Web环境 中使用的 IoC容器。
*/ */
protected Class<?> determineContextClass(ServletContext servletContext) { protected Class<?> determineContextClass(ServletContext servletContext) {
// 获取servletContext中对CONTEXT_CLASS_PARAMcontextClass参数的配置 // 获取 servletContext 中对 CONTEXT_CLASS_PARAMcontextClass参数 的配置
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) { if (contextClassName != null) {
try { try {
// 获取配置的contextClassName对应的clazz对象 // 获取配置的 contextClassName 对应的 clazz对象
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
} }
catch (ClassNotFoundException ex) { catch (ClassNotFoundException ex) {
throw new ApplicationContextException( throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex); "Failed to load custom context class [" + contextClassName + "]", ex);
} }
} }
else { else {
// 如果没有配置CONTEXT_CLASS_PARAM则使用默认的ContextClass // 如果没有配置 CONTEXT_CLASS_PARAM则使用默认的 ContextClass
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try { try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
} }
catch (ClassNotFoundException ex) { catch (ClassNotFoundException ex) {
throw new ApplicationContextException( throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex); "Failed to load default context class [" + contextClassName + "]", ex);
} }
} }
} }
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value // The application context id is still set to its original default value
// -> assign a more useful id based on available information // -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) { if (idParam != null) {
wac.setId(idParam); wac.setId(idParam);
} }
else { else {
// Generate default id... // Generate default id...
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) { if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// Servlet <= 2.4: resort to name specified in web.xml, if any. // Servlet <= 2.4: resort to name specified in web.xml, if any.
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getServletContextName())); ObjectUtils.getDisplayString(sc.getServletContextName()));
} }
else { else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath())); ObjectUtils.getDisplayString(sc.getContextPath()));
} }
} }
} }
// 设置ServletContext 及配置文件的位置参数 // 设置 ServletContext 及配置文件的位置参数
wac.setServletContext(sc); wac.setServletContext(sc);
String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM); String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (initParameter != null) { if (initParameter != null) {
wac.setConfigLocation(initParameter); wac.setConfigLocation(initParameter);
} }
customizeContext(sc, wac); customizeContext(sc, wac);
// IoC容器初始化的入口想不起来的把前面IoC容器初始化的博文再读10遍 // IoC容器 初始化的入口,想不起来的把前面 IoC容器 初始化的博文再读10遍
wac.refresh(); wac.refresh();
} }
} }
``` ```
这就是IoC容器在Web容器中的启动过程 应用中启动IoC容器的方式相类似所不同的是这里需要考虑Web容器的环境特点比如各种参数的设置IoC容器与Web容器ServletContext的结合等。在初始化这个上下文以后该上下文会被存储到SevletContext中这样就建立了一个全局的关于整个应用的上下文。同时在启动Spring MVC时我们还会看到这个上下文被以后的DispatcherServlet在进行自己持有的上下文的初始化时设置为DispatcherServlet自带的上下文的双亲上下文。 这就是 IoC容器 Web容器 中的启动过程,与应用中启动 IoC容器 的方式相类似,所不同的是这里需要考虑 Web容器 的环境特点比如各种参数的设置IoC容器 Web容器 ServletContext 的结合等。在初始化这个上下文以后,该上下文会被存储到 SevletContext 中,这样就建立了一个全局的关于整个应用的上下文。同时,在启动 SpringMVC 时,我们还会看到这个上下文被以后的 DispatcherServlet 在进行自己持有的上下文的初始化时,设置为 DispatcherServlet 自带的上下文的双亲上下文。

@ -1,35 +1,36 @@
## 1 SpringMVC应用场景 ## 1 SpringMVC应用场景
在使用SpringMVC时除了要在web.xml中配置ContextLoaderListener外还要对DispatcherServlet进行配置。作为一个Servlet这个DispatcherServlet实现的是Sun的J2EE核心模式中的前端控制器模式(Front Controller) 作为一个前端控制器所有的Web请求都需要通过它来处理,进行转发、匹配、数据处理后,并转由页面进行展现因此这个DispatcerServlet可以看成是Spring MVC实现中最为核心的部分。 在使用 SpringMVC 时,除了要在 web.xml 中配置 ContextLoaderListener 外,还要对 DispatcherServlet 进行配置。作为一个 Servlet这个 DispatcherServlet 实现的是 Sun J2EE核心模式 中的 前端控制器模式(Front Controller) 作为一个前端控制器,所有的 Web请求 都需要通过它来进行转发、匹配、数据处理,然后转由页面进行展现,因此这个 DispatcerServlet 可以看成是 SpringMVC实现 中最为核心的部分。
在Spring MVC中对于不同的Web请求的映射需求Spring MVC提供了不同的HandlerMapping的实现可以让应用开发选取不同的映射策略。DispatcherSevlet默认了BeanNameUrlHandlerMapping作为映射策略实现。除了映射策略可以定制外Spring MVC提供了各种Controller的实现来供应用扩展和使用以应对不同的控制器使用场景这些Controller控制器需要实现handleRequest接口方法并返回ModelAndView对象。Spring MVC还提供了各种视图实现比如常用的JSP视图。除此之外Spring MVC还提供了拦截器供应用使用允许应用对Web请求进行拦截以及前置处理和后置处理。 SpringMVC 中,对于不同的 Web请求 的映射需求SpringMVC 提供了不同的 HandlerMapping 的实现可以让应用开发选取不同的映射策略。DispatcherSevlet 默认了 BeanNameUrlHandlerMapping 作为映射策略实现。除了映射策略可以定制外SpringMVC提供了各种 Controller 的实现来供应用扩展和使用,以应对不同的控制器使用场景,这些 Controller控制器 需要实现 handleRequest()接口方法,并返回 ModelAndView对象。SpringMVC 还提供了各种视图实现,比如常用的 JSP视图。除此之外SpringMVC 还提供了拦截器供应用使用,允许应用对 Web请求 进行拦截,以及前置处理和后置处理。
## 2 SpringMVC设计概览 ## 2 SpringMVC设计概览
在完成对ContextLoaderListener的初始化以后Web容器开始初始化DispatcherServlet这个初始化的启动与在web.xml中对载入次序的定义有关。DispatcherServlet会建立自己的上下文来持有Spring MVC的Bean对象在建立这个自己持有的IoC容器时会**从ServletContext中得到根上下文**作为DispatcherServlet持有上下文的双亲上下文。有了这个根上下文再对自己持有的上下文进行初始化最后把自己持有的这个上下文保存到ServletContext中供以后检索和使用。 在完成对 ContextLoaderListener 的初始化以后Web容器 开始初始化 DispatcherServlet这个初始化的启动与在 web.xml 中对载入次序的定义有关。DispatcherServlet 会建立自己的上下文来持有SpringMVC Bean对象在建立这个自己持有的 IoC容器 时,会**从 ServletContext 中得到根上下文**作为 DispatcherServlet 持有上下文的双亲上下文。有了这个根上下文,再对自己持有的上下文进行初始化,最后把自己持有的这个上下文保存到 ServletContext 中,供以后检索和使用。
为了解这个过程可以从DispatcherServlet的父类FrameworkServlet的代码入手去探寻DispatcherServlet的启动过程它同时也是SpringMVC的启动过程。ApplicationContext的创建过程和ContextLoader创建根上下文的过程有许多类似的地方。下面来看一下这个DispatcherServlet类的继承关系。 为了解这个过程,可以从 DispatcherServlet 的父类 FrameworkServlet 的代码入手,去探寻 DispatcherServlet 的启动过程,它同时也是 SpringMVC 的启动过程。ApplicationContext 的创建过程和 ContextLoader 创建根上下文的过程有许多类似的地方。下面来看一下这个 DispatcherServlet类 的继承关系。
![avatar](/images/springMVC/DispatcherServlet的继承关系.png) ![avatar](/images/springMVC/DispatcherServlet的继承关系.png)
DispatcherServlet通过继承FrameworkServlet和HttpServletBean而继承了HttpServlet通过使用Servlet API来对HTTP请求进行响应成为Spring MVC的前端处理器同时成为MVC模块与Web容器集成的处理前端。 DispatcherServlet 通过继承 FrameworkServlet HttpServletBean 而继承了 HttpServlet通过使用Servlet API 来对 HTTP请求 进行响应,成为 SpringMVC 的前端处理器,同时成为 MVC模块 Web容器 集成的处理前端。
DispatcherServlet的工作大致可以分为两个部分一个是初始化部分由initServletBean()启动通过initWebApplicationContext()方法最终调用DispatcherServlet的initStrategies()方法在这个方法里DispatcherServlet对MVC模块的其他部分进行了初始化比如handlerMapping、ViewResolver等另一个是对HTTP请求进行响应作为一个ServletWeb容器会调用Servlet的doGet()和doPost()方法在经过FrameworkServlet的processRequest()简单处理后会调用DispatcherServlet的doService()方法在这个方法调用中封装了doDispatch()这个doDispatch()是Dispatcher实现MVC模式的主要部分下图为DispatcherServlet的处理过程时序图。 DispatcherServlet 的工作大致可以分为两个部分:一个是初始化部分,由 initServletBean()方法 启动,通过 initWebApplicationContext()方法 最终调用 DispatcherServlet initStrategies()方法在这个方法里DispatcherServlet MVC模块 的其他部分进行了初始化,比如 handlerMapping、ViewResolver 等;另一个是对 HTTP请求 进行响应,作为一个 ServletWeb容器 会调用 Servlet 的doGet() doPost()方法,在经过 FrameworkServlet processRequest() 简单处理后,会调用 DispatcherServlet doService()方法,在这个方法调用中封装了 doDispatch(),这个 doDispatch() Dispatcher 实现 MVC模式 的主要部分,下图为 DispatcherServlet 的处理过程时序图。
![avatar](/images/springMVC/DispatcherServlet的处理过程.png) ![avatar](/images/springMVC/DispatcherServlet的处理过程.png)
## 3 DispatcherServlet的启动和初始化 ## 3 DispatcherServlet的启动和初始化
前面大致描述了Spring MVC的工作流程下面看一下DispatcherServlet的启动和初始化的代码设计及实现。 前面大致描述了 SpringMVC 的工作流程,下面看一下 DispatcherServlet 的启动和初始化的代码设计及实现。
作为ServletDispatcherServlet的启动与Servlet的启动过程是相联系的。在Servlet的初始化过程中Servlet的init()方法会被调用以进行初始化DispatcherServlet的基类HttpServletBean实现了该方法。在初始化开始时需要读取配置在ServletContext中的Bean属性参数这些属性参数设置在web.xml的Web容器初始化参数中。使用编程式的方式来设置这些Bean属性在这里可以看到对PropertyValues和BeanWrapper的使用。对于这些和依赖注人相关的类的使用在分析IoC容器的初始化时尤其是在依赖注入实现分析时有过“亲密接触”。只是这里的依赖注人是与Web容器初始化相关的。 作为 ServletDispatcherServlet 的启动与 Servlet 的启动过程是相联系的。在 Servlet 的初始化过程中Servlet init()方法 会被调用以进行初始化DispatcherServlet 的基类 HttpServletBean 实现了该方法。在初始化开始时,需要读取配置在 ServletContext 中的 Bean属性参数这些属性参数设置在 web.xml Web容器初始化参数 中。使用编程式的方式来设置这些 Bean属性在这里可以看到对 PropertyValues BeanWrapper 的使用。对于这些和依赖注人相关的类的使用,在分析 IoC容器 的初始化时,尤其是在依赖注入实现分析时,有过“亲密接触”。只是这里的依赖注人是与 Web容器 初始化相关的。
接着会执行DispatcherServlet持有的IoC容器的初始化过程在这个初始化过程中一个新的上下文被建立起来这个DispatcherServlet持有的上下文被设置为根上下文的子上下文。一个Web应用中可以容纳多个Servlet存在与此相对应对于应用在Web容器中的上下体系一个根上下文可以作为许多Servlet上下文的双亲上下文。了解IoC工作原理的读者知道在向IoC容器getBean()时IoC容器会首先向其双亲上下文去getBean()也就是说在根上下文中定义的Bean是可以被各个Servlet持有的上下文得到和共享的。DispatcherServlet持有的 上下文被建立起来以后也需要和其他IoC容器一样完成初始化这个初始化也是通过refresh()方法来完成的。最后DispatcherServlet给这个自己持有的上下文命名并把它设置到Web容器的上下文中这个名称和在web.xml中设置的DispatcherServlet的Servlet名称有关从而保证了这个上下文在Web环境上下文体系中的唯一性。 接着会执行 DispatcherServlet 持有的 IoC容器 的初始化过程,在这个初始化过程中,一个新的上下文被建立起来,这个 DispatcherServlet 持有的上下文被设置为根上下文的子上下文。一个 Web应用 中可以容纳多个 Servlet 存在;与此相对应,对于应用在 Web容器 中的上下体系,一个根上下文可以作为许多 Servlet上下文 的双亲上下文。了解 IoC 工作原理的读者知道,在向 IoC容器 getBean() IoC容器 会首先向其双亲上下文去 getBean(),也就是说,在根上下文中定义的 Bean 是可以被各个 Servlet 持有的上下文得到和共享的。DispatcherServlet 持有的 上下文被建立起来以后,也需要和其他 IoC容器 一样完成初始化,这个初始化也是通过 refresh()方法 来完成的。最后DispatcherServlet 给这个自己持有的上下文命名,并把它设置到 Web容器 的上下文中,这个名称和在 web.xml 中设置的 DispatcherServlet Servlet名称 有关,从而保证了这个上下文在 Web环境上下文体系 中的唯一性。
```java ```java
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware { public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
public final void init() throws ServletException { public final void init() throws ServletException {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'"); logger.debug("Initializing servlet '" + getServletName() + "'");
} }
// 获取Servlet的初始化参数对bean属性进行配置 // 获取 Servlet 的初始化参数,对 bean属性 进行配置
try { try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
@ -54,10 +55,11 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
public abstract class FrameworkServlet extends HttpServletBean { public abstract class FrameworkServlet extends HttpServletBean {
/** 此servlet的WebApplicationContext */
/** 此 servlet 的 WebApplicationContext */
private WebApplicationContext webApplicationContext; private WebApplicationContext webApplicationContext;
/** 我们是否应该将当前Servlet的上下文webApplicationContext设为ServletContext的属性 */ /** 我们是否应该将当前 Servlet 的上下文 webApplicationContext 设为 ServletContext 的属性 */
private boolean publishContext = true; private boolean publishContext = true;
public FrameworkServlet() { public FrameworkServlet() {
@ -68,7 +70,7 @@ public abstract class FrameworkServlet extends HttpServletBean {
} }
/** /**
* 覆盖了父类HttpServletBean的空实现 * 覆盖了父类 HttpServletBean 的空实现
*/ */
@Override @Override
protected final void initServletBean() throws ServletException { protected final void initServletBean() throws ServletException {
@ -100,16 +102,16 @@ public abstract class FrameworkServlet extends HttpServletBean {
} }
/** /**
* 为这个Servlet初始化一个公共的WebApplicationContext实例 * 为这个 Servlet 初始化一个公共的 WebApplicationContext实例
*/ */
protected WebApplicationContext initWebApplicationContext() { protected WebApplicationContext initWebApplicationContext() {
// 获取 根上下文 作为当前MVC上下文的双亲上下文这个根上下文保存在ServletContext中 // 获取根上下文作为当前 MVC上下文 的双亲上下文,这个根上下文保存在 ServletContext
WebApplicationContext rootContext = WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null; WebApplicationContext wac = null;
if (this.webApplicationContext != null) { if (this.webApplicationContext != null) {
// 可以在本对象被构造时注入一个webApplicationContext实例 // 可以在本对象被构造时注入一个 webApplicationContext实例
wac = this.webApplicationContext; wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) { if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
@ -126,24 +128,24 @@ public abstract class FrameworkServlet extends HttpServletBean {
} }
if (wac == null) { if (wac == null) {
// 在本对象被构造时没有注入上下文实例 -> // 在本对象被构造时没有注入上下文实例 ->
// 查看是否已在servlet上下文中注册了上下文实例。 // 查看是否已在 servlet上下文 中注册了上下文实例。
// 如果存在一个,则假定父上下文(如果有的话)已经被设置, // 如果存在一个,则假定父上下文(如果有的话)已经被设置,
// 并且用户已经执行了任何初始化例如设置上下文ID // 并且用户已经执行了任何初始化例如设置上下文ID
wac = findWebApplicationContext(); wac = findWebApplicationContext();
} }
if (wac == null) { if (wac == null) {
// 没有为此servlet定义上下文实例 -> 创建本地实例 // 没有为此 servlet 定义上下文实例 -> 创建本地实例
wac = createWebApplicationContext(rootContext); wac = createWebApplicationContext(rootContext);
} }
if (!this.refreshEventReceived) { if (!this.refreshEventReceived) {
// 上下文 不是支持刷新的ConfigurableApplicationContext或者 // 上下文不是支持刷新的 ConfigurableApplicationContext或者
// 在构造时注入的上下文已经完成刷新 -> 在此处手动触发onRefresh()方法 // 在构造时注入的上下文已经完成刷新 -> 在此处手动触发 onRefresh()方法
onRefresh(wac); onRefresh(wac);
} }
if (this.publishContext) { if (this.publishContext) {
// 把当前建立的上下文保存到ServletContext中使用的属性名是和当前servlet名相关的 // 把当前建立的上下文保存到 ServletContext 中,使用的属性名是和 当前servlet名 相关的
String attrName = getServletContextAttributeName(); String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac); getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) { if (this.logger.isDebugEnabled()) {
@ -156,7 +158,7 @@ public abstract class FrameworkServlet extends HttpServletBean {
} }
} }
``` ```
至此这个MVC的上下文就建立起来了具体取得根上下文的过程在WebApplicationContextUtils中实现。这个根上下文是ContextLoader设置到ServletContext中去的使用的属性是ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTEContextLoader还对这个IoC容器的Bean配置文件进行了设置默认的位置是在/WEB-INF/applicationContext.xml文件中。由于这个根上下文是DispatcherServlet建立的上下文的 双亲上下文所以根上下文中管理的Bean也可以被DispatcherServlet的上下文使用。通过getBean()向IoC容器获取Bean时容器会先到它的双亲IoC容器中获取。 至此,这个 MVC 的上下文就建立起来了,具体取得根上下文的过程在 WebApplicationContextUtils 中实现。这个根上下文是 ContextLoader 设置到 ServletContext 中去的,使用的属性是 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTEContextLoader 还对这个 IoC容器 Bean 配置文件进行了设置,默认的位置是在 /WEB-INF/applicationContext.xml文件 中。由于这个根上下文是 DispatcherServlet 建立的上下文的 双亲上下文,所以根上下文中管理的 Bean 也可以被 DispatcherServlet 的上下文使用。通过 getBean() IoC容器 获取 Bean 时,容器会先到它的 双亲IoC容器 中获取。
```java ```java
/** /**
* 这是一个封装了很多静态方法的抽象工具类,所以只能调用其静态方法, * 这是一个封装了很多静态方法的抽象工具类,所以只能调用其静态方法,
@ -164,8 +166,8 @@ public abstract class FrameworkServlet extends HttpServletBean {
*/ */
public abstract class WebApplicationContextUtils { public abstract class WebApplicationContextUtils {
/** /**
* 使用了WebApplicationContext的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性获取 * 使用了 WebApplicationContext ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性获取
* ServletContext中的根上下文这个属性代表的根上下文在ContextLoaderListener初始化的 * ServletContext 中的根上下文,这个属性代表的根上下文在 ContextLoaderListener 初始化的
* 过程中被建立 * 过程中被建立
*/ */
public static WebApplicationContext getWebApplicationContext(ServletContext sc) { public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
@ -173,7 +175,7 @@ public abstract class WebApplicationContextUtils {
} }
/** /**
* 查找此web应用程序的自定义WebApplicationContext * 查找此 web应用程序 的自定义 WebApplicationContext
*/ */
public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) { public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
Assert.notNull(sc, "ServletContext must not be null"); Assert.notNull(sc, "ServletContext must not be null");
@ -197,20 +199,20 @@ public abstract class WebApplicationContextUtils {
} }
) )
``` ```
回到FrameworkServlet的实现中来看一下DispatcherServlet的上下文是怎样建立的这个建立过程与前面建立根上下文的过程非常类似。建立DispatcherServlet的上下文需要把根上下文作为参数传递给它。然后使用反射技术来实例化上下文对象并为它设置参数。根据默认的配置这个上下文对象也是XmlWebApplicationContext对象这个类型是在DEFAULT_CONTEXT_CLASS参数中设置好并允许BeanUtilis使用的。在实例化结束后需要为这个上下文对象设置好一些基本的配置这些配置包括它的双亲上下文、Bean配置文件的位置等。完成这些配置以后最后通过调用IoC容器的refresh()方法来完成IoC容器的最终初始化这和前面我们对IoC容器实现原理的分析中所看到的IoC容器初始化的过程是一致的。 回到 FrameworkServlet 的实现中来看一下DispatcherServlet 的上下文是怎样建立的,这个建立过程与前面建立根上下文的过程非常类似。建立 DispatcherServlet 的上下文,需要把根上下文作为参数传递给它。然后使用反射技术来实例化上下文对象,并为它设置参数。根据默认的配置,这个上下文对象也是 XmlWebApplicationContext对象这个类型是在 DEFAULT_CONTEXT_CLASS参数 中设置好并允许 BeanUtilis 使用的。在实例化结束后需要为这个上下文对象设置好一些基本的配置这些配置包括它的双亲上下文、Bean配置文件 的位置等。完成这些配置以后,最后通过调用 IoC容器 refresh()方法 来完成 IoC容器 的最终初始化,这和前面我们对 IoC容器实现原理 的分析中所看到的 IoC容器初始化 的过程是一致的。
```java ```java
public abstract class FrameworkServlet extends HttpServletBean { public abstract class FrameworkServlet extends HttpServletBean {
/** /**
* 为此servlet实例化一个WebApplicationContext可以是默认的XmlWebApplicationContext * 为此 servlet 实例化一个 WebApplicationContext可以是默认的 XmlWebApplicationContext
* 也可以是用户设置的自定义Context上下文 * 也可以是用户设置的自定义 Context上下文
*/ */
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent); return createWebApplicationContext((ApplicationContext) parent);
} }
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
// 默认为XmlWebApplicationContext.class // 默认为 XmlWebApplicationContext.class
Class<?> contextClass = getContextClass(); Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) { if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() + this.logger.debug("Servlet with name '" + getServletName() +
@ -228,11 +230,11 @@ public abstract class FrameworkServlet extends HttpServletBean {
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment()); wac.setEnvironment(getEnvironment());
// 这里设置的 双亲上下文就是在ContextLoader中建立的根上下文 // 这里设置的 双亲上下文,就是在 ContextLoader 中建立的根上下文
wac.setParent(parent); wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation()); wac.setConfigLocation(getContextConfigLocation());
// 配置并且刷新wac // 配置并且刷新 WebApplicationContext对象
configureAndRefreshWebApplicationContext(wac); configureAndRefreshWebApplicationContext(wac);
return wac; return wac;
@ -240,15 +242,15 @@ public abstract class FrameworkServlet extends HttpServletBean {
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// 应用程序上下文id仍设置为其原始默认值如果该id不为空的话 // 应用程序上下文id 仍设置为其原始默认值,如果该 id 不为空的话
if (this.contextId != null) { if (this.contextId != null) {
wac.setId(this.contextId); wac.setId(this.contextId);
} }
else { else {
// 生成默认的id // 生成默认的 id
ServletContext sc = getServletContext(); ServletContext sc = getServletContext();
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) { if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// 当Servlet<=2.4如果有请使用web.xml中指定的名称。 // 当 Servlet <= 2.4:如果有,请使用 web.xml 中指定的名称。
String servletContextName = sc.getServletContextName(); String servletContextName = sc.getServletContextName();
if (servletContextName != null) { if (servletContextName != null) {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName + wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName +
@ -259,7 +261,7 @@ public abstract class FrameworkServlet extends HttpServletBean {
} }
} }
else { else {
// Servlet 2.5的getContextPath可用 // Servlet 2.5 getContextPath 可用!
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()) + "/" + getServletName()); ObjectUtils.getDisplayString(sc.getContextPath()) + "/" + getServletName());
} }
@ -272,8 +274,8 @@ public abstract class FrameworkServlet extends HttpServletBean {
wac.setNamespace(getNamespace()); wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// 在刷新上下文的任何情况下,都将会调用wac环境的initPropertySources()方法。 // 在刷新上下文的任何情况下,都将会调用 此wac 的 env的 initPropertySources()方法。
// 在此处执行此方法以确保在刷新上下文之前servlet属性源已准备就绪 // 在此处执行此方法以确保在刷新上下文之前servlet属性源 已准备就绪
ConfigurableEnvironment env = wac.getEnvironment(); ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) { if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(getServletContext(), getServletConfig()); ((ConfigurableWebEnvironment)env).initPropertySources(getServletContext(), getServletConfig());
@ -283,20 +285,20 @@ public abstract class FrameworkServlet extends HttpServletBean {
applyInitializers(wac); applyInitializers(wac);
// IoC容器都是通过该方法完成 容器初始化的 // IoC容器 都是通过该方法完成 容器初始化的
wac.refresh(); wac.refresh();
} }
} }
``` ```
这时候DispatcherServlet中的IoC容器已经建立起来了这个IoC容器是 根上下文 的子容器。如果要查找一个由DispatcherServlet所持有的IoC容器来管理的Bean系统会首先到 根上下文 中去查找。如果查找不到才会到DispatcherServlet所管理的IoC容器去进行查找这是由IoC容器getBean()的实现来决定的。通过一系列在Web容器中执行的动作在这个上下文体系建立和初始化完毕的基础上Spring MVC就可以发挥其作用了。下面来分析一下Spring MVC的具体实现。 这时候 DispatcherServlet 中的 IoC容器 已经建立起来了,这个 IoC容器 是 根上下文 的子容器。如果要查找一个由 DispatcherServlet 所持有的 IoC容器 来管理的 Bean系统会首先到 根上下文 中去查找。如果查找不到,才会到 DispatcherServlet 所管理的 IoC容器 去进行查找,这是由 IoC容器getBean() 的实现来决定的。通过一系列在 Web容器 中执行的动作在这个上下文体系建立和初始化完毕的基础上SpringMVC 就可以发挥其作用了。下面来分析一下 SpringMVC 的具体实现。
在前面分析DispatchServlet的初始化过程中可以看到DispatchServlet持有一个以自己的Servlet名称命名的IoC容器。这个IoC容器是一个WebApplicationContext对象这个IoC容器建立起来以后意味着DispatcherServlet拥有自己的Bean定义空间这为使用各个独立的XML文件来配置MVC中各个Bean创造了条件。由于在初始化结束以后与Web容器相关的加载过程实际上已经完成了SpringMVC的具体实现和普通的Spring应用程序的实现并没有太大的差别。 在前面分析 DispatchServlet 的初始化过程中可以看到DispatchServlet 持有一个以自己的 Servlet名称 命名的 IoC容器。这个 IoC容器 是一个 WebApplicationContext对象这个 IoC容器 建立起来以后,意味着 DispatcherServlet 拥有自己的 Bean定义空间这为使用各个独立的 XML文件 来配置 MVC 中各个 Bean 创造了条件。由于在初始化结束以后,与 Web容器 相关的加载过程实际上已经完成了SpringMVC 的具体实现和普通的 Spring应用程序 的实现并没有太大的差别。
在DispatcherServlet的初始化过程中以对HandlerMapping的初始化调用作为触发点了解SpringMVC模块初始化的方法调用关系。这个调用关系最初是由HttpServletBean的init()方法触发的这个HttpServletBean是HttpServlet的子类。接着会在HttpServletBean的子类FrameworkServlet中对IoC容器完成初始化在这个初始化方法中会调用DispatcherServlet的initStrategies()方法该方法包括对各种MVC框架的实现元素比如支持国际化的LocalResolver、支持request映射的HandlerMappings以及视图生成的ViewResolver等。由该方法启动整个Spring MVC框架的初始化。 DispatcherServlet 的初始化过程中,以对 HandlerMapping 的初始化调用作为触发点,了解 SpringMVC模块 初始化的方法调用关系。这个调用关系最初是由 HttpServletBean init()方法 触发的,这个 HttpServletBean HttpServlet 的子类。接着会在 HttpServletBean 的子类 FrameworkServlet 中对 IoC容器 完成初始化,在这个初始化方法中,会调用 DispatcherServlet initStrategies()方法,该方法包括对各种 MVC框架 的实现元素,比如支持国际化的 LocalResolver、支持 request 映射的 HandlerMappings以及视图生成的 ViewResolver 等。由该方法启动整个 SpringMVC框架 的初始化。
```java ```java
public class DispatcherServlet extends FrameworkServlet { public class DispatcherServlet extends FrameworkServlet {
/** /**
* 初始化此servlet使用的策略对象。 * 初始化此 servlet 使用的策略对象。
* 可以在子类中重写以便初始化进一步的策略对象U8C * 可以在子类中重写以便初始化进一步的策略对象U8C
*/ */
protected void initStrategies(ApplicationContext context) { protected void initStrategies(ApplicationContext context) {
@ -306,7 +308,7 @@ public class DispatcherServlet extends FrameworkServlet {
initLocaleResolver(context); initLocaleResolver(context);
// 主题view层 // 主题view层
initThemeResolver(context); initThemeResolver(context);
// 解析url和Method的对应关系 // 解析 url Method 的对应关系
initHandlerMappings(context); initHandlerMappings(context);
// 适配器匹配 // 适配器匹配
initHandlerAdapters(context); initHandlerAdapters(context);
@ -321,42 +323,42 @@ public class DispatcherServlet extends FrameworkServlet {
} }
} }
``` ```
对于具体的初始化过程根据上面的方法名称很容易理解。以HandlerMapping为例来说明这个initHandlerMappings()过程。这里的Mapping关系的作用是为HTTP请求找到相应的Controller控制器从而利用这些控制器Controller去完成设计好的数据处理工作。 对于具体的初始化过程,根据上面的方法名称,很容易理解。以 HandlerMapping 为例来说明这个 initHandlerMappings()过程。这里的 Mapping关系 的作用是,为 HTTP请求 找到相应的 Controller控制器从而利用这些 控制器Controller 去完成设计好的数据处理工作。
HandlerMappings完成对MVC中Controller的定义和配置只不过在Web这个特定的应用环境中这些控制器是与具体的HTTP请求相对应的。在HandlerMapping初始化的过程中把在Bean配置文件中配置好的HandlerMapping从IoC容器中取得。 HandlerMappings 完成对 MVC Controller 的定义和配置,只不过在 Web 这个特定的应用环境中,这些控制器是与具体的 HTTP请求 相对应的。在 HandlerMapping初始化 的过程中,把在 Bean配置文件 中配置好的 HandlerMapping IoC容器 中取得。
```java ```java
/** /**
* 初始化此类使用的HandlerMappings。 * 初始化此类使用的 HandlerMappings。
* 如果在BeanFactory中没有为此命名空间定义的HandlerMapping bean则默认为BeanNameUrlHandlerMapping * 如果在 BeanFactory 中没有为此命名空间定义的 HandlerMapping bean则默认为 BeanNameUrlHandlerMapping
*/ */
private void initHandlerMappings(ApplicationContext context) { private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null; this.handlerMappings = null;
// 这个detectAllHandlerMappings默认为true表示从所有的IoC容器中获取所有的HandlerMappings // 这个 detectAllHandlerMappings 默认为 true表示从所有的 IoC容器 中获取所有的HandlerMappings
if (this.detectAllHandlerMappings) { if (this.detectAllHandlerMappings) {
// 查找所有的HandlerMapping从应用上下文context及其双亲上下文中 // 查找所有的 HandlerMapping 应用上下文context 及其双亲上下文中
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerMapping.class, true, false); context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) { if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>( this.handlerMappings = new ArrayList<HandlerMapping>(
matchingBeans.values()); matchingBeans.values());
// 保持HandlerMappings的有序性 // 保持 HandlerMappings 的有序性
OrderComparator.sort(this.handlerMappings); OrderComparator.sort(this.handlerMappings);
} }
} }
else { else {
try { try {
// 根据名称从当前的IoC容器中通过getBean()获取HandlerMapping // 根据名称从当前的 IoC容器 中通过 getBean() 取HandlerMapping
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME,
HandlerMapping.class); HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm); this.handlerMappings = Collections.singletonList(hm);
} }
catch (NoSuchBeanDefinitionException ex) { catch (NoSuchBeanDefinitionException ex) {
// 忽略稍后将添加默认的HandlerMapping // 忽略,稍后将添加默认的 HandlerMapping
} }
} }
// 如果找不到其他映射请通过注册默认的HandlerMapping确保至少有一个HandlerMapping // 如果找不到其他映射,请通过注册默认的 HandlerMapping 确保至少有一个 HandlerMapping
if (this.handlerMappings == null) { if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
@ -366,20 +368,18 @@ HandlerMappings完成对MVC中Controller的定义和配置只不过在Web这
} }
} }
``` ```
经过以上读取过程handlerMappings变量就已经获取了在Bean中配置好的映射关系。其他的初始化过程和handlerMappings比较类似都是直接从IoC容器中读入配置所以这里的MVC初始化过程是建立在IoC容器已经初始化完成的基础上的。 经过以上读取过程handlerMappings变量 就已经获取了在 Bean 中配置好的映射关系。其他的初始化过程和 handlerMappings 比较类似,都是直接从 IoC容器 中读入配置,所以这里的 MVC初始化过程 是建立在 IoC容器 已经初始化完成的基础上的。
## 4 SpringMVC处理分发HTTP请求 ## 4 SpringMVC处理分发HTTP请求
### 4.1 HandlerMapping的配置和设计原理 ### 4.1 HandlerMapping的配置和设计原理
前面分析了DispatcherServlet对Spring MVC框架的初始化过程在此基础上我们再进一步分析HandlerMapping的实现原理看看这个MVC框架中比较关键的控制部分是如何实现的。 前面分析了 DispatcherServlet SpringMVC框架 的初始化过程,在此基础上,我们再进一步分析 HandlerMapping 的实现原理,看看这个 MVC框架 中比较关键的控制部分是如何实现的。
在初始化完成时在上下文环境中已定义的所有HandlerMapping都已经被加载了这些加载的handlerMappings被放在一个List中并被排序存储着HTTP请求对应的映射数据。这个List中的每一个元素都对应着一个具体handlerMapping的配置一般每一个handlerMapping 在初始化完成时,在上下文环境中已定义的所有 HandlerMapping 都已经被加载了,这些加载的 handlerMappings 被放在一个 List 中并被排序,存储着 HTTP请求 对应的映射数据。这个 List 中的每一个元素都对应着一个具体 handlerMapping 的配置,一般每一个 handlerMapping 可以持有一系列从 URL请求 到 Controller 的映射,而 SpringMVC 提供了一系列的 HandlerMapping 实现。
可以持有一系列从URL请求到Controller的映射而Spring MVC提供了一系列的HandlerMapping实现。
![avatar](/images/springMVC/HandlerMapping组件.png) ![avatar](/images/springMVC/HandlerMapping组件.png)
以SimpleUrlHandlerMapping这个handlerMapping为例来分析HandlerMapping的设计与实现。在SimpleUrlHandlerMapping中定义了一个map来 持有 一系列的映射关系。通过这些在HandlerMapping中定义的映射关系即这些URL请求和控制器的对应关系使Spring MVC 以 SimpleUrlHandlerMapping 为例来分析 HandlerMapping 的设计与实现。在 SimpleUrlHandlerMapping 中,定义了一个 Map 来持有一系列的映射关系。通过这些在 HandlerMapping 中定义的映射关系,即这些 URL请求 和控制器的对应关系,使 SpringMVC
应用可以根据HTTP请求确定一个对应的Controller。具体来说这些映射关系是通过接口HandlerMapping来封装的在HandlerMapping接 口中定义了一个getHandler方法通过这个方法可以获得与HTTP请求对应的HandlerExecutionChain在这个HandlerExecutionChain 应用 可以根据 HTTP请求 确定一个对应的 Controller。具体来说这些映射关系是通过 HandlerMapping接口 来封装的,在 HandlerMapping接口 中定义了一个 getHandler()方法,通过这个方法,可以获得与 HTTP请求 对应的 HandlerExecutionChain在这个 HandlerExecutionChain 中,封装了具体的 Controller对象。
封装了具体的Controller对象。
```java ```java
public interface HandlerMapping { public interface HandlerMapping {
@ -396,15 +396,13 @@ public interface HandlerMapping {
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes"; String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
/** /**
* 返回的这个HandlerExecutionChain不但持有handler本身还包括了处理这个HTTP请求的拦截器 * 返回的这个 HandlerExecutionChain 不但持有 handler本身还包括了处理这个 HTTP请求 的拦截器
*/ */
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
} }
``` ```
这个HandlerExecutionChain的实现看起来比较简洁它持有一个Interceptor链和一个handler对象这个handler对象实际上就是HTTP请求对应的Controller在持有这个handler对象的同时还在HandlerExecutionChain中设置了一个拦截器链通过这个拦截器链中的拦截器, 这个 HandlerExecutionChain 的实现看起来比较简洁,它持有一个 拦截器链(HandlerInterceptor对象列表) 和一个 handler对象这个 handler对象 实际上就是 HTTP请求 对应的 Controller在持有这个 handler对象 的同时,还在 HandlerExecutionChain 中设置了一个拦截器链,通过这个拦截器链中的拦截器,可以为 handler对象 提供功能的增强。要完成这些工作,需要对拦截器链和 handler 都进行配置,这些配置都是在 HandlerExecutionChain 的初始化函数中完成的。为了维护这个拦截器链和 handlerHandlerExecutionChain 还提供了一系列与拦截器链维护相关的操作,比如,为拦截器链增加拦截器的 addInterceptor()方法。
可以为handler对象提供功能的增强。要完成这些工作需要对拦截器链和handler都进行配置这些配置都是在HandlerExecutionChain的初始化函数中完成的。为了维护这个拦截器链和handlerHandlerExecutionChain还提供了一系列与拦截器链维护相关的操作比如为拦
截器链增加拦截器的addInterceptor()方法。
```java ```java
public class HandlerExecutionChain { public class HandlerExecutionChain {
@ -460,7 +458,7 @@ public class HandlerExecutionChain {
} }
/** /**
* 延迟初始化interceptorList和interceptors集合 * 延迟初始化 interceptorList interceptors集合
*/ */
private void initInterceptorList() { private void initInterceptorList() {
if (this.interceptorList == null) { if (this.interceptorList == null) {
@ -496,12 +494,13 @@ public class HandlerExecutionChain {
} }
} }
``` ```
HandlerExecutionChain中定义的Handler和Interceptor需要在定义HandlerMapping时配置好例如对具体的SimpleURLHandlerMapping要做的就是根据URL映射的方式注册Handler和Interceptor从而维护一个反映这种映射关系的handlerMap。当需要匹配HTTP请求时需要查询这个handlerMap中的信息来得到对应的HandlerExecutionChain。这些信息是什么时候配置好的呢?这里有一个注册过程这个注册过程在容器对Bean进行依赖注入时发生它实际上是通过一个Bean的postProcessor()来完成的。以SimpleHandlerMapping为例需要注意的是这里用到了对容器的回调只有SimpleHandlerMapping是ApplicationContextAware的子类才能启动这个注册过程。这个注册过程完成的是反映URL和Controller之间映射关系的handlerMap的建立。 HandlerExecutionChain 中定义的 Handler HandlerInterceptor[]属性 需要在定义 HandlerMapping 时配置好,例如对具体的 SimpleURLHandlerMapping要做的就是根据 URL映射 的方式,注册 Handler HandlerInterceptor[],从而维护一个反映这种映射关系的 handlerMap。当需要匹配 HTTP请求 时,需要查询这个 handlerMap 中的信息来得到对应的 HandlerExecutionChain。这些信息是什么时候配置好的呢?这里有一个注册过程,这个注册过程在容器对 Bean 进行依赖注入时发生,它实际上是通过一个 Bean postProcessor() 来完成的。以 SimpleHandlerMapping 为例,需要注意的是,这里用到了对容器的回调,只有 SimpleHandlerMapping ApplicationContextAware 的子类才能启动这个注册过程。这个注册过程完成的是反映 URL Controller 之间映射关系的 handlerMap 的建立。
![avatar](/images/springMVC/SimpleUrlHandlerMapping的继承关系.png) ![avatar](/images/springMVC/SimpleUrlHandlerMapping的继承关系.png)
```java ```java
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping { public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
@Override @Override
public void initApplicationContext() throws BeansException { public void initApplicationContext() throws BeansException {
super.initApplicationContext(); super.initApplicationContext();
@ -509,18 +508,18 @@ public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
} }
/** /**
* 为相应的路径注册URL映射中指定的所有handlers处理程序 * 为相应的路径注册 URL映射 中指定的所有 handlers处理程序
*/ */
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) { if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
} }
else { else {
// 这里对bean的配置进行解析然后调用父类的registerHandler()方法进行解析 // 这里对 bean 的配置进行解析,然后调用父类的 registerHandler()方法 进行解析
for (Map.Entry<String, Object> entry : urlMap.entrySet()) { for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
String url = entry.getKey(); String url = entry.getKey();
Object handler = entry.getValue(); Object handler = entry.getValue();
// 如果url没有斜线就在前面加上斜线 // 如果 url 没有斜线,就在前面加上斜线
if (!url.startsWith("/")) { if (!url.startsWith("/")) {
url = "/" + url; url = "/" + url;
} }
@ -535,12 +534,12 @@ public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
} }
} }
``` ```
这个SimpleUrlHandlerMapping注册过程的完成很大一部分需要它的基类来配合这个基类就是AbstractUrlHandlerMapping。在AbstractUrlHandlerMapping的处理过程中如果使用Bean的名称作为映射那么直接从容器中获取这个HTTP映射对应的Bean然后还要对不同的URL配置进行解析处理比如在HTTP请求中配置成“/”和通配符“/*” 的URL以及正常的URL请求完成这个解析处理过程以后 这个 SimpleUrlHandlerMapping 注册过程的完成,很大一部分需要它的基类来配合,这个基类就是 AbstractUrlHandlerMapping。在 AbstractUrlHandlerMapping 的处理过程中,如果使用 Bean 的名称作为映射,那么直接从容器中获取这个 HTTP映射 对应的 Bean然后还要对不同的 URL配置 进行解析处理,比如在 HTTP请求 中配置成 “/” 和 通配符“/*” 的 URL以及正常的 URL请求完成这个解析处理过程以后会把 URL 和 handler 作为键值对放到一个 handlerMap 中去。
把URL和handler作为键值对放到一个handlerMap中去。
```java ```java
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered { public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
/** /**
* 为给定的URL路径注册指定的handler处理程序 * 为给定的 URL路径 注册指定的 handler处理程序
*/ */
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException { protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null"); Assert.notNull(urlPaths, "URL path array must not be null");
@ -550,14 +549,14 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
} }
/** /**
* 为给定的URL路径注册指定的handler处理程序 * 为给定的 URL路径 注册指定的 handler处理程序
*/ */
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null"); Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler; Object resolvedHandler = handler;
// 如果使用bean名称进行映射就直接从IoC容器中获取该bean名称对应的handler // 如果使用 bean名称 进行映射,就直接从 IoC容器 中获取该 bean名称 对应的 handler
if (!this.lazyInitHandlers && handler instanceof String) { if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler; String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) { if (getApplicationContext().isSingleton(handlerName)) {
@ -574,21 +573,21 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
} }
} }
else { else {
// 处理URL是"/"的映射,把这个"/"映射的controller设置到rootHandler中 // 处理 URL "/" 的映射,把这个 "/" 映射的 controller 设置到 rootHandler
if (urlPath.equals("/")) { if (urlPath.equals("/")) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info("Root mapping to " + getHandlerDescription(handler)); logger.info("Root mapping to " + getHandlerDescription(handler));
} }
setRootHandler(resolvedHandler); setRootHandler(resolvedHandler);
} }
// 处理URL是"/"的映射,把这个"/"映射的controller设置到defaultHandler中 // 处理 URL "/" 的映射,把这个 "/" 映射的 controller 设置到 defaultHandler
else if (urlPath.equals("/*")) { else if (urlPath.equals("/*")) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info("Default mapping to " + getHandlerDescription(handler)); logger.info("Default mapping to " + getHandlerDescription(handler));
} }
setDefaultHandler(resolvedHandler); setDefaultHandler(resolvedHandler);
} }
// 处理正常的URL映射此handlerMap的key和value分别代表URL和映射的Controller // 处理正常的 URL映射 handlerMap key value 分别代表 URL 映射的Controller
else { else {
this.handlerMap.put(urlPath, resolvedHandler); this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
@ -599,7 +598,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
} }
/** /**
* 为此handler映射设置根handler即要为根路径"/"注册的handler * 为此 handler映射 设置 根handler即要为根路径"/")注册的 handler
* <p>Default is {@code null}, indicating no root handler. * <p>Default is {@code null}, indicating no root handler.
*/ */
public void setRootHandler(Object rootHandler) { public void setRootHandler(Object rootHandler) {
@ -611,7 +610,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
} }
/** /**
* 设置此handler映射的默认handler。如果未找到特定映射则将返回此handler * 设置 此handler映射 的默认 handler。如果未找到特定映射则将返回 此handler
*/ */
public void setDefaultHandler(Object defaultHandler) { public void setDefaultHandler(Object defaultHandler) {
this.defaultHandler = defaultHandler; this.defaultHandler = defaultHandler;
@ -622,50 +621,50 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
} }
} }
``` ```
这里的handlerMap是一个HashMap其中保存了URL请求和Controller的映射关系这个handlerMap是在AbstractUrlHandlerMapping中定义的 Map<String, object> handlerMap = new LinkedHashMap<String, object>() 这个配置好URL请求和handler映射数据的handlerMap为Spring MVC响应HTTP请求准备好了基本的映射数据根据这个handlerMap以及设置于其中的映射数据可以方便地由 这里的 handlerMap 是一个 HashMap其中保存了 “URL请求” --> “Controller对象” 的映射关系,这个 handlerMap 是在 AbstractUrlHandlerMapping 中定义的( Map<String, object> handlerMap = new LinkedHashMap<String, object>() ),这个配置好 URL请求 和 handler映射数据 的 handlerMap为 SpringMVC 响应 HTTP请求 准备好了基本的映射数据,根据这个 handlerMap 以及设置于其中的映射数据,可以方便地由 URL请求 得到它所对应的 handler。有了这些准备工作SpringMVC 就可以等待 HTTP请求 的到来了。
URL请求得到它所对应的handler。有了这些准备工作Spring MVC就可以等待HTTP请求的到来了。
### 4.2 使用HandlerMapping完成请求的映射处理 ### 4.2 使用HandlerMapping完成请求的映射处理
继续通过SimpleUrlHandlerMapping的实现来分析HandlerMapping的接口方法getHandler()该方法会根据初始化时得到的映射关系来生成DispatcherServlet需要的HandlerExecutionChain也就是说这个getHandler()方法是实际使用HandlerMapping完成请求的映射处理的地方。在前面的HandlerExecutionChain的执行过程中首先在AbstractHandlerMapping中启动getHandler的调用。 继续通过 SimpleUrlHandlerMapping的实现 来分析 HandlerMapping 接口方法getHandler(),该方法会根据初始化时得到的映射关系来生成 DispatcherServlet 需要的 HandlerExecutionChain也就是说这个 getHandler()方法 是实际使用 HandlerMapping 完成请求的映射处理的地方。在前面的 HandlerExecutionChain 的执行过程中,首先在 AbstractHandlerMapping 中启动 getHandler() 的调用。
```java ```java
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered { public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
/** /**
* 查找给定请求的handler如果找不到特定的handler则返回到defaultHandler * 查找给定请求的 handler如果找不到特定的 handler则返回到 defaultHandler
*/ */
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 模板方法模式 // 这里用到了模板方法模式getHandler() 是一个模板方法定义了流程getHandlerInternal() 则是
// 一个抽象方法,交由子类实现
Object handler = getHandlerInternal(request); Object handler = getHandlerInternal(request);
// 如果找不到特定的handler则取defaultHandler // 如果找不到特定的 handler则取 defaultHandler
if (handler == null) { if (handler == null) {
handler = getDefaultHandler(); handler = getDefaultHandler();
} }
// defaultHandler也没有则返回null // defaultHandler 也没有则返回 null
if (handler == null) { if (handler == null) {
return null; return null;
} }
// 如果该handler是String类型的说明它是一个beanname // 如果该 handler 是 String类型的说明它是一个 beanName
// 根据该beanname从IoC容器中获取真正的handler对象 // 根据该 beanName 从 IoC容器 中获取真正的 handler对象
if (handler instanceof String) { if (handler instanceof String) {
String handlerName = (String) handler; String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName); handler = getApplicationContext().getBean(handlerName);
} }
// 这里把handler添加到到HandlerExecutionChain中 // 这里把 handler 添加到到 HandlerExecutionChain
return getHandlerExecutionChain(handler, request); return getHandlerExecutionChain(handler, request);
} }
} }
``` ```
取得handler的具体过程在getHandlerInternal()方法中实现这个方法接受HTTP请求作为参数它的实现在AbstractHandlerMapping的子类AbstractUrlHandlerMapping中这个实现过程包括从HTTP请求中得到URL并根据URL到urlMapping中获得handler。 取得 handler 的具体过程在 getHandlerInternal()方法 中实现,这个方法接受 HTTP请求 作为参数,它的实现在 AbstractHandlerMapping 的子类 AbstractUrlHandlerMapping 中,这个实现过程包括从 HTTP请求 中得到 URL并根据 URL urlMapping 中获得 handler。
```java ```java
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
/** /**
* 查找给定请求的URL路径 对应的handler * 查找 给定请求的URL 对应的 handler
*/ */
@Override @Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception { protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// 从request中获取请求的URL路径 // 从 request 中获取请求的 URL路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 将得到的URL路径与handler进行匹配得到对应的handler如果没有对应的handler // 将得到的 URL路径 handler 进行匹配,得到对应的 handler如果没有对应的 handler
// 则返回null这样默认的handler会被使用 // 则返回 null这样 默认的handler 会被使用
Object handler = lookupHandler(lookupPath, request); Object handler = lookupHandler(lookupPath, request);
if (handler == null) { if (handler == null) {
// We need to care for the default handler directly, since we need to // We need to care for the default handler directly, since we need to
@ -674,7 +673,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
if ("/".equals(lookupPath)) { if ("/".equals(lookupPath)) {
rawHandler = getRootHandler(); rawHandler = getRootHandler();
} }
// 使用默认的handler // 使用 默认的handler
if (rawHandler == null) { if (rawHandler == null) {
rawHandler = getDefaultHandler(); rawHandler = getDefaultHandler();
} }
@ -698,7 +697,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
} }
/** /**
* 查找给定URL路径的handler实例 * 查找给定 URL路径 handler实例
*/ */
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 直接匹配 // 直接匹配
@ -758,28 +757,28 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
} }
} }
``` ```
经过这一系列对HTTP请求进行解析和匹配handler的过程得到了与请求对应的handler处理器。在返回的handler中已经完成了在HandlerExecutionChain中进行封装的工作为handler对HTTP请求的响应做好了准备。 经过这一系列对 HTTP请求 进行解析和匹配 handler 的过程,得到了与请求对应的 handler处理器。在返回的 handler 中,已经完成了在 HandlerExecutionChain 中进行封装的工作,为 handler HTTP请求 的响应做好了准备。
### 4.3 DispatcherServlet对HTTP请求的分发处理 ### 4.3 DispatcherServlet对HTTP请求的分发处理
DispatcherServlet是Spring MVC框架中非常重要的一个类不但建立了自己持有的IoC容器还肩负着请求分发处理的重任对HTTP请求的处理是在doService()方法中完成的。DispatcherServlet是HttpServlet的子类 与其他HttpServlet一样可以通过doService()来响应HTTP的请求。然而依照Spring MVC的使用业务逻辑的调用入口是在handler的handler()方法中实现的这是连接Spring MVC和应用业务逻辑实现的地方。 DispatcherServlet SpringMVC框架 中非常重要的一个类,不但建立了自己持有的 IoC容器还肩负着请求分发处理的重任 HTTP请求 的处理是在 doService()方法 中完成的。DispatcherServlet HttpServlet 的子类 ,与其他 HttpServlet 一样,可以通过 doService() 来响应 HTTP的请求。然而依照 SpringMVC 的使用,业务逻辑的调用入口是在 handler handler()方法 中实现的,这是连接 SpringMVC 和应用业务逻辑实现的地方。
```java ```java
public class DispatcherServlet extends FrameworkServlet { public class DispatcherServlet extends FrameworkServlet {
/** 此servlet使用的HandlerMappings列表 */ /** 此 DispatcherServlet 使用的 HandlerMapping对象列表 */
private List<HandlerMapping> handlerMappings; private List<HandlerMapping> handlerMappings;
/** 此servlet使用的HandlerAdapter列表 */ /** 此 DispatcherServlet 使用的 HandlerAdapter对象列表 */
private List<HandlerAdapter> handlerAdapters; private List<HandlerAdapter> handlerAdapters;
/** /**
* 公开DispatcherServlet特定的请求属性并将其委托给doDispatch()方法进行实际的分发 * 公开 DispatcherServlet 特定的请求属性,并将其委托给 doDispatch()方法 进行实际的分发
*/ */
@Override @Override
protected void doService(HttpServletRequest request, HttpServletResponse response) protected void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception { throws Exception {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
// 得到请求的URI // 得到 请求的URI
String requestUri = urlPathHelper.getRequestUri(request); String requestUri = urlPathHelper.getRequestUri(request);
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()
? " resumed" : ""; ? " resumed" : "";
@ -836,8 +835,8 @@ public class DispatcherServlet extends FrameworkServlet {
/** /**
* 中央控制器,控制请求的转发 * 中央控制器,控制请求的转发
* 对请求的处理实际上是由doDispatch()来完成的它是DispatcherServlet完成HTTP请求分发处理的主要方法, * 对请求的处理实际上是由 doDispatch() 来完成的,它是 DispatcherServlet 完成 HTTP请求 分发处理的主要方法,
* 包括准备ModelAndView调用getHandler()方法来响应HTTP请求然后通过执行Handler的处理来获取请求的 * 包括准备 ModelAndView调用 getHandler()方法 来响应 HTTP请求然后通过执行 Handler的处理 来获取请求的
* 处理结果,最后把结果返回出去 * 处理结果,最后把结果返回出去
*/ */
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
@ -849,7 +848,7 @@ public class DispatcherServlet extends FrameworkServlet {
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try { try {
// 为视图准备好一个ModelAndView这个ModelAndView持有handler处理请求的结果 // 为视图准备好一个 ModelAndView这个 ModelAndView 持有 handler处理请求的结果
ModelAndView mv = null; ModelAndView mv = null;
Exception dispatchException = null; Exception dispatchException = null;
@ -858,20 +857,20 @@ public class DispatcherServlet extends FrameworkServlet {
processedRequest = checkMultipart(request); processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request; multipartRequestParsed = processedRequest != request;
// 2.取得处理当前请求的controller这里也称为hanlder处理器这里并不是 // 2.取得处理当前请求的 Controller对象这里也称为 hanlder处理器这里并不是
// 直接返回controller而是返回的HandlerExecutionChain请求处理器链对象 // 直接返回 controller对象,而是返回的 HandlerExecutionChain请求处理器链对象
// 该对象封装了handler和interceptors // 该对象封装了 handler interceptors
mappedHandler = getHandler(processedRequest, false); mappedHandler = getHandler(processedRequest, false);
// 如果handler为空,则返回404 // 如果 handler 为空,则返回 404
if (mappedHandler == null || mappedHandler.getHandler() == null) { if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response); noHandlerFound(processedRequest, response);
return; return;
} }
// 3. 获取处理request的处理器适配器handler adapter // 3. 获取处理 request 的处理器适配器 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 获取请求方式GET, POST, PUT // 获取 请求方式GET, POST, PUT
String method = request.getMethod(); String method = request.getMethod();
boolean isGet = "GET".equals(method); boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) { if (isGet || "HEAD".equals(method)) {
@ -939,7 +938,7 @@ public class DispatcherServlet extends FrameworkServlet {
} }
/** /**
* 返回此请求的HandlerExecutionChain按顺序尝试所有的HandlerMapping * 返回此请求的 HandlerExecutionChain按顺序尝试所有的 HandlerMapping
*/ */
@Deprecated @Deprecated
protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache)
@ -948,17 +947,17 @@ public class DispatcherServlet extends FrameworkServlet {
} }
/** /**
* 返回此请求的HandlerExecutionChain * 返回此请求的 HandlerExecutionChain
*/ */
protected HandlerExecutionChain getHandler(HttpServletRequest request) protected HandlerExecutionChain getHandler(HttpServletRequest request)
throws Exception { throws Exception {
// 遍历 此servlet使用的HandlerMapping列表 // 遍历 此servlet 使用的 HandlerMapping列表
for (HandlerMapping hm : this.handlerMappings) { for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Testing handler map [" + hm logger.trace("Testing handler map [" + hm
+ "] in DispatcherServlet with name '" + getServletName() + "'"); + "] in DispatcherServlet with name '" + getServletName() + "'");
} }
// 查找给定请求的handler // 查找给定请求的 handler
HandlerExecutionChain handler = hm.getHandler(request); HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) { if (handler != null) {
return handler; return handler;
@ -968,10 +967,10 @@ public class DispatcherServlet extends FrameworkServlet {
} }
/** /**
* 返回此处理程序对象handler的HandlerAdapter * 返回 此处理程序对象handler HandlerAdapter
*/ */
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
// 对所有持有的HandlerAdapter进行匹配 // 对所有持有的 HandlerAdapter 进行匹配
for (HandlerAdapter ha : this.handlerAdapters) { for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]"); logger.trace("Testing handler adapter [" + ha + "]");
@ -985,11 +984,11 @@ public class DispatcherServlet extends FrameworkServlet {
} }
} }
``` ```
通过判断可以知道这个handler是不是Controller接口的实现比如可以通过具体HandlerAdapter的实现来了解这个适配过程。以SimpleControllerHandlerAdapter的实现为例来了解这个判断是怎样起作用的。 通过判断,可以知道这个 handler 是不是 Controller接口 的实现,比如可以通过具体 HandlerAdapter 的实现来了解这个适配过程。以 SimpleControllerHandlerAdapter的实现 为例来了解这个判断是怎样起作用的。
```java ```java
public class SimpleControllerHandlerAdapter implements HandlerAdapter { public class SimpleControllerHandlerAdapter implements HandlerAdapter {
// 判断要执行的handler是不是Controller类型的 // 判断要执行的 handler 是不是 Controller类型的
public boolean supports(Object handler) { public boolean supports(Object handler) {
return (handler instanceof Controller); return (handler instanceof Controller);
} }
@ -1009,8 +1008,4 @@ public class SimpleControllerHandlerAdapter implements HandlerAdapter {
} }
``` ```
经过上面一系列的处理得到了handler对象接着就可以开始调用handler对象中的HTTP响应动作了。在handler中封装了应用业务逻辑由这些逻辑对HTTP请求进行相应的处理生成需要的数据并把这些数据封装到ModelAndView对象中去这个ModelAndView的数据封装是Spring MVC框架的要求。对handler来说 这些都是通过调用handler()方法中的handleRequest()方法来触发完成的。在得到ModelAndView对象以后这个ModelAndView对象会被交给MVC模式中的视图类由视图类对ModelAndView对象中的数据进行呈现。 经过上面一系列的处理,得到了 handler对象接着就可以开始调用 handler对象 中的 HTTP响应动作了。在 handler 中封装了应用业务逻辑,由这些逻辑对 HTTP请求 进行相应的处理,生成需要的数据,并把这些数据封装到 ModelAndView对象 中去,这个 ModelAndView 的数据封装是 SpringMVC框架 的要求。对 handler 来说, 这些都是通过调用 handler()方法 中的 handleRequest()方法 来触发完成的。在得到 ModelAndView对象 以后,这个 ModelAndView对象 会被交给 MVC模式 中的视图类,由视图类对 ModelAndView对象 中的数据进行呈现。

Loading…
Cancel
Save