您的位置:首页 > 教育 > 培训 > SpringMVC重点功能底层源码解析

SpringMVC重点功能底层源码解析

2024/10/6 1:45:32 来源:https://blog.csdn.net/qq_36324341/article/details/141954525  浏览:    关键词:SpringMVC重点功能底层源码解析

课堂疑问1:

当我们使用@RequestParam,并且没有注册StringToUserEditor时,但是User中提供了一个String类型参数的构造方法时:

@RequestMapping(method = RequestMethod.GET, path = "/test")
@ResponseBody
public String test(@RequestParam("name") User user) {return user.getName();
}
public class User {private String name;public User(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

SpringMVC在进行把String转成User对象时,会先判断有没有User类型对应的StringToUserEditor,如果有就会利用它来把String转成User对象,如果没有则会找User类中有没有String类型参数的构造方法,如果有则用该构造方法来构造出User对象。

对应的源码方法为:org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class, org.springframework.core.convert.TypeDescriptor)

课堂疑问2:

如果方法返回的是byte[]:

@RequestMapping(method = RequestMethod.GET, path = "/test")
@ResponseBody
public byte[] test() {byte[] bytes = new byte[1024];return bytes;
}

这种情况会直接使用ByteArrayHttpMessageConverter来处理,会直接把byte[]写入响应中:

public class ByteArrayHttpMessageConverter extends AbstractHttpMessageConverter<byte[]> {/*** Create a new instance of the {@code ByteArrayHttpMessageConverter}.*/public ByteArrayHttpMessageConverter() {super(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL);}@Overridepublic boolean supports(Class<?> clazz) {return byte[].class == clazz;}// ...@Overrideprotected void writeInternal(byte[] bytes, HttpOutputMessage outputMessage) throws IOException {StreamUtils.copy(bytes, outputMessage.getBody());}}

SpringMVC父子容器

我们可以在web.xml文件中这么来定义:

<web-app><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring.xml</param-value></context-param><servlet><servlet-name>app</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>app</servlet-name><url-pattern>/app/*</url-pattern></servlet-mapping></web-app>

在这个web.xml文件中,我们定义了一个listener和servlet。

父容器的创建

ContextLoaderListener的作用是用来创建一个Spring容器,就是我们说的SpringMVC父子容器中的父容器,执行流程为:

  1. Tomcat启动,解析web.xml时
  2. 发现定义了一个ContextLoaderListener,Tomcat就会执行该listener中的contextInitialized()方法,该方法就会去创建要给Spring容器
  3. 从ServletContext中获取contextClass参数值,该参数表示所要创建的Spring容器的类型,可以在web.xml中通过来进行配置
  4. 如果没有配置该参数,那么则会从ContextLoader.properties文件中读取org.springframework.web.context.WebApplicationContext配置项的值,SpringMVC默认提供了一个ContextLoader.properties文件,内容为org.springframework.web.context.support.XmlWebApplicationContext
  5. 所以XmlWebApplicationContext就是要创建的Spring容器类型
  6. 确定好类型后,就用反射调用无参构造方法创建出来一个XmlWebApplicationContext对象
  7. 然后继续从ServletContext中获取contextConfigLocation参数的值,也就是一个spring配置文件的路径
  8. 把spring配置文件路径设置给Spring容器,然后调用refresh(),从而启动Spring容器,从而解析spring配置文件,从而扫描生成Bean对象等
  9. 这样Spring容器就创建出来了
  10. 有了Spring容器后,就会把XmlWebApplicationContext对象作为attribute设置到ServletContext中去,key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
  11. 把Spring容器存到ServletContext中的原因,是为了给Servlet创建出来的子容器来作为父容器的

子容器的创建

Tomcat启动过程中,执行完ContextLoaderListener的contextInitialized()之后,就会创建DispatcherServlet了,web.xml中定义DispatcherServlet时,load-on-startup为1,表示在Tomcat启动过程中要把这个DispatcherServlet创建并初始化出来,而这个过程是比较费时间的,所以要把load-on-startup设置为1,如果不为1,会在servlet接收到请求时才来创建和初始化,这样会导致请求处理比较慢。

  1. Tomcat启动,解析web.xml时
  2. 创建DispatcherServlet对象
  3. 调用DispatcherServlet的init()
  4. 从而调用initServletBean()
  5. 从而调用initWebApplicationContext(),这个方法也会去创建一个Spring容器(就是子容器)
  6. initWebApplicationContext()执行过程中,会先从ServletContext拿出ContextLoaderListener所创建的Spring容器(父容器),记为rootContext
  7. 然后读取contextClass参数值,可以在servlet中的标签来定义想要创建的Spring容器类型,默认为XmlWebApplicationContext
  8. 然后创建一个Spring容器对象,也就是子容器
  9. 将rootContext作为parent设置给子容器(父子关系的绑定)
  10. 然后读取contextConfigLocation参数值,得到所配置的Spring配置文件路径
  11. 然后就是调用Spring容器的refresh()方法
  12. 从而完成了子容器的创建

SpringMVC初始化

子容器创建完后,还会调用一个DispatcherServlet的onRefresh()方法,这个方法会从Spring容器中获取一些特殊类型的Bean对象,并设置给DispatcherServlet对象中对应的属性,比如HandlerMapping、HandlerAdapter。

流程为:

  1. 会先从Spring容器中获取HandlerMapping类型的Bean对象,如果不为空,那么就获取出来的Bean对象赋值给DispatcherServlet的handlerMappings属性
  2. 如果没有获取到,则会从DispatcherServlet.properties文件中读取配置,从而得到SpringMVC默认给我们配置的HandlerMapping

DispatcherServlet.properties文件内容为:

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.org.springframework.web.ser

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com