下面类图将主要的类及方法抽离出来,以便查看方便,根据类的结构来说明整个请求是如何工作的
主要使用到的技术有Spring的IOC容器和Servlet。
假如我们要实现一个请求home.htm然后返回home.jsp视图资源则
当home.htm请求到达时,我们需要DispatcherServlet来处理该请求,所以首先配置该Servlet
第一步需要在web.xml中配置DispatcherServlet,使该servlet来接收请求并做进一步处理。
- <servlet>
- <servlet-name>dispatch</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>dispatch</servlet-name>
- <url-pattern>*.htm</url-pattern>
- </servlet-mapping>
<servlet> <servlet-name>dispatch</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatch</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping>
这个部分很好理解,如果请求以.htm结尾则交给名为dispatch类为DispatcherServlet的Servlet处理。
从类图中很容易看出DispatcherServlet最终继承的是HttpServlet,也就是说它同样满足Servlet的工作原理
Servlet初始化时需要调用init方法,在HttpServletBean中实现,该init方法调用了initServletBean,该方法在FrameworkServlet中实现
initServletBean主要初始化关于配置文件的内容,比如{servlet-name}-servlet.xml
第二步,需要在/WebRoot/WEB-INF下新建名为{servlet-name}-servlet.xml的spring bean配置文件。(该示例中即为dispatch-servlet.xml)
在初始化过程中会去寻找该配置文件,当然我们也可以自己去设置参数来更改配置文件所在路径
比如我们如果在src下新建的该配置文件dispatch-servlet,在编译后会被复制到WEB-INF/classes文件夹下,
配置文件还是按照命名规范做吧(可以修改为其他名字)
- <servlet>
- <servlet-name>dispatch</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>namespace</param-name>
- <param-value>classes/dispatch-servlet</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
<servlet> <servlet-name>dispatch</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>namespace</param-name> <param-value>classes/dispatch-servlet</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
此时的配置就会去寻找/WEB-INF/classes/dispatch-servlet.xml
当请求到达后Servlet将调用service方法进行处理,由于我们是通过输入网址方式的get方法请求,Servlet将调用doGet方法
此处的doGet方法在FrameworkServlet中实现,doGet方法调用processRequest方法,processRequest则调用doService方法处理
而doService在DispatcherServlet中实现,doService再调用了DispatcherServlet的doDispatch方法,
该方法则会根据request找到转发对象,并进行请求转发操作,
下面是获取实际的视图资源部分
- public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- return ((Controller) handler).handleRequest(request, response);
- }
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); }
这里需要我们自己实现Controller接口并实现handleRequest方法,返回对应的ModelAndView对象。
下面是请求转发的部分
- /**
- * Render the internal resource given the specified model.
- * This includes setting the model as request attributes.
- */
- @Override
- protected void renderMergedOutputModel(
- Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
- // Determine which request handle to expose to the RequestDispatcher.
- HttpServletRequest requestToExpose = getRequestToExpose(request);
- ...
- exposeModelAsRequestAttributes(model, requestToExpose);//这个方法看下面源码,request.setAttribute操作
- // Determine the path for the request dispatcher.
- String dispatcherPath = prepareForRendering(requestToExpose, response);
- ...
- // If already included or response already committed, perform include, else forward.
- if (useInclude(requestToExpose, response)) {
- ......
- }
- else {//重点看这部分,在根据请求以及配置文件获取到RequestDispatcher 对象之后,使用该对象做转发处理
- // Note: The forwarded resource is supposed to determine the content type itself.
- exposeForwardRequestAttributes(requestToExpose);
- if (logger.isDebugEnabled()) {
- logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
- }
- rd.forward(requestToExpose, response);
- }
- }
/** * Render the internal resource given the specified model. * This includes setting the model as request attributes. */ @Override protected void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine which request handle to expose to the RequestDispatcher. HttpServletRequest requestToExpose = getRequestToExpose(request); ... exposeModelAsRequestAttributes(model, requestToExpose);//这个方法看下面源码,request.setAttribute操作 // Determine the path for the request dispatcher. String dispatcherPath = prepareForRendering(requestToExpose, response); ... // If already included or response already committed, perform include, else forward. if (useInclude(requestToExpose, response)) { ...... } else {//重点看这部分,在根据请求以及配置文件获取到RequestDispatcher 对象之后,使用该对象做转发处理 // Note: The forwarded resource is supposed to determine the content type itself. exposeForwardRequestAttributes(requestToExpose); if (logger.isDebugEnabled()) { logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); } rd.forward(requestToExpose, response); } }下面是设置model和modelValue
- /**
- * Expose the model objects in the given map as request attributes.
- * Names will be taken from the model Map.
- * This method is suitable for all resources reachable by {@link javax.servlet.RequestDispatcher}.
- * @param model Map of model objects to expose
- * @param request current HTTP request
- */
- protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
- for (Map.Entry<String, Object> entry : model.entrySet()) {
- String modelName = entry.getKey();
- Object modelValue = entry.getValue();
- if (modelValue != null) {
- request.setAttribute(modelName, modelValue);
- if (logger.isDebugEnabled()) {
- logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +
- "] to request in view with name '" + getBeanName() + "'");
- }
- }
- else {
- request.removeAttribute(modelName);
- if (logger.isDebugEnabled()) {
- logger.debug("Removed model object '" + modelName +
- "' from request in view with name '" + getBeanName() + "'");
- }
- }
- }
- }
/** * Expose the model objects in the given map as request attributes. * Names will be taken from the model Map. * This method is suitable for all resources reachable by {@link javax.servlet.RequestDispatcher}. * @param model Map of model objects to expose * @param request current HTTP request */ protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception { for (Map.Entry<String, Object> entry : model.entrySet()) { String modelName = entry.getKey(); Object modelValue = entry.getValue(); if (modelValue != null) { request.setAttribute(modelName, modelValue); if (logger.isDebugEnabled()) { logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() + "] to request in view with name '" + getBeanName() + "'"); } } else { request.removeAttribute(modelName); if (logger.isDebugEnabled()) { logger.debug("Removed model object '" + modelName + "' from request in view with name '" + getBeanName() + "'"); } } } }
第三步,编写实现Controller的类
- public class HomeController implements Controller
- {
- private String greeting;
- public String getGreeting()
- {
- return greeting;
- }
- public void setGreeting(String greeting)
- {
- this.greeting = greeting;
- }
- public ModelAndView handleRequest(HttpServletRequest arg0,
- HttpServletResponse arg1) throws Exception
- {
- System.out.println(arg0.getRequestURI());//请求地址
- return new ModelAndView("home", "message", greeting);
- //返回一个视图资源对象,名为home,model为message的对象(即上面的exposeModelAsRequestAtrributes方法中使用的request.setAttribute
- }
- }
public class HomeController implements Controller { private String greeting; public String getGreeting() { return greeting; } public void setGreeting(String greeting) { this.greeting = greeting; } public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception { System.out.println(arg0.getRequestURI());//请求地址 return new ModelAndView("home", "message", greeting); //返回一个视图资源对象,名为home,model为message的对象(即上面的exposeModelAsRequestAtrributes方法中使用的request.setAttribute } }
第四步,在dispatch-servlet.xml中配置该bean提供给spring web使用。
- <bean name="/home.htm" class="com.iss.spring.web.HomeController">
- <property name="greeting"><value>Hello!This is Training!你好,这里是训练营!</value></property>
- </bean>
<bean name="/home.htm" class="com.iss.spring.web.HomeController"> <property name="greeting"><value>Hello!This is Training!你好,这里是训练营!</value></property> </bean>
这里name将用来匹配请求的资源(默认的使用BeanNameUrlHandlerMapping处理,由bean Name映射 URL),在home.htm请求到达时,
spring将使用实现了Controller接口的HomeController的handleRequest方法来返回映射的视图资源。
在得到MoldelAndView对象后,需要根据这个MoldelAndView对象得到View name然后来解析得到View对象
- /**
- * Resolve the given view name into a View object (to be rendered).
- * <p>The default implementations asks all ViewResolvers of this dispatcher.
- * Can be overridden for custom resolution strategies, potentially based on
- * specific model attributes or request parameters.
- * @param viewName the name of the view to resolve
- * @param model the model to be passed to the view
- * @param locale the current locale
- * @param request current HTTP servlet request
- * @return the View object, or <code>null</code> if none found
- * @throws Exception if the view cannot be resolved
- * (typically in case of problems creating an actual View object)
- * @see ViewResolver#resolveViewName
- */
- protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
- HttpServletRequest request) throws Exception {
- for (ViewResolver viewResolver : this.viewResolvers) {
- View view = viewResolver.resolveViewName(viewName, locale);
- if (view != null) {
- return view;
- }
- }
- return null;
- }
/** * Resolve the given view name into a View object (to be rendered). * <p>The default implementations asks all ViewResolvers of this dispatcher. * Can be overridden for custom resolution strategies, potentially based on * specific model attributes or request parameters. * @param viewName the name of the view to resolve * @param model the model to be passed to the view * @param locale the current locale * @param request current HTTP servlet request * @return the View object, or <code>null</code> if none found * @throws Exception if the view cannot be resolved * (typically in case of problems creating an actual View object) * @see ViewResolver#resolveViewName */ protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } return null; }
此处需要我们配置viewResolver bean给spring使用,指明使用哪个类充当viewResolver并具有什么属性
第五步,配置viewResolver bean
- <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <property name="suffix"><value>.jsp</value></property>
- </bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="suffix"><value>.jsp</value></property> </bean>
中间可以加上prefix或者suffix
这些配置完成后,spring就会根据请求地址以及配置信息,找到视图资源并做请求转发操作
总结:整个流程分析下来,其实主要就是做两个操作,
首先请求信息到达DispatchServlet,Servlet中根据请求信息与配置文件找到映射的视图资源
然后使用RequestDispatch请求转发到该视图资源。
另外,可以分成多个bean配置文件,在web.xml中配置载入
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/dispatch-data.xml,/WEB-INF/dispatch-service.xml</param-value>
- </context-param>
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatch-data.xml,/WEB-INF/dispatch-service.xml</param-value> </context-param>
其中contextConfigLocation这个名字可能是匹配FrameworkServlet的setContextConfigLocation方法
也有可能是匹配ContextLoaderListener继承ContextLoader的CONFIG_LOCATION_PARAM
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
(不确定,不太了解context-param的用法,API上两个类关于这个变量的说明都类似,也分不太清楚,反正可以这么记- -||)
然后配置的viewResolver bean的id为什么要为viewResolver,下面的是DispatcherServlet中一个静态字符串说明了一切
public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
相关推荐
本文将逐步深入Spring MVC的源码,帮助开发者理解其内部工作原理。 首先,我们来了解Spring MVC的基本工作流程。当一个HTTP请求到达服务器时,DispatcherServlet作为Spring MVC的前端控制器,负责接收请求并分发到...
5. **注解驱动编程**:探究@Component、@Service、@Repository和@Controller等注解的解析过程,以及Autowired和Qualifier的工作原理。 6. **事务管理**:研究PlatformTransactionManager接口和相关的事务策略,理解...
5. **研究MVC框架内部**:Spring MVC的DispatcherServlet、Controller、ModelAndView等组件的协同工作,以及视图解析的过程。 6. **理解事务管理**:Spring如何提供透明的事务管理,包括编程式和声明式事务。 7. *...
3. Spring MVC原理:研究`DispatcherServlet`如何分发请求,`HandlerMapping`和`HandlerAdapter`如何找到合适的处理器,以及`ModelAndView`和视图解析器的工作流程。 4. 事件驱动:深入`ApplicationEvent`和`...
这个"springMvc的简单框架"应该包含了一个基础的项目配置,可以让初学者快速上手并了解 Spring MVC 的工作原理。 首先,Spring MVC 的核心组件包括 DispatcherServlet、Controllers、Models、Views 和 ...
通过分析`MyWEB`项目的源代码,开发者可以更好地理解MVC模式的工作原理,学习如何在实际项目中运用这一模式。这不仅有助于提高代码质量,还有利于团队协作和代码复用。 总结来说,MVC模式在Java J2EE开发中扮演着...
3. **DispatcherServlet和MVC**:分析请求如何被Spring MVC处理,以及视图解析的工作原理。 4. **事件机制**:查看ApplicationEvent和ApplicationListener的实现,了解事件在Spring容器内的传播。 5. **Spring ...
Spring 2.5.4版本是该框架的一个重要里程碑,尽管现在已经有了更新的版本,但理解2.5.4的源码对于深入学习Spring的工作原理仍然非常有价值。这个压缩包"spring-framework-2.5.4源码.rar"包含了Spring框架的核心组件...
4. **MVC框架**:Spring MVC的工作流程,DispatcherServlet、Controller、ModelAndView等组件的详细运作。 5. **数据访问**:JdbcTemplate、HibernateTemplate、MyBatis整合等,以及Spring Data JPA、Spring Data ...
在这个源码包中,我们可以深入探究Spring的核心机制,理解它如何实现依赖注入、AOP(面向切面编程)、事务管理等关键功能。 首先,让我们关注依赖注入(Dependency Injection,简称DI)。Spring通过DI帮助我们解耦...
这个压缩包包含了官方发布的原始源码,对于深入理解Spring的工作原理、进行自定义扩展或者排查问题具有重要的价值。下面我们将详细探讨Spring Framework的核心组件、设计理念以及源码学习的重点。 Spring Framework...
源代码的分析可以帮助我们深入了解Spring的工作原理,学习如何设计和实现类似的框架,以及自定义扩展。通过对Spring源码的学习,开发者可以提升自己的编程技巧,理解设计模式的运用,以及如何在实际项目中实现高效率...
《Spring宝典源码3》是一份深入剖析Spring框架核心源码的重要参考资料,旨在帮助开发者更全面、深入地理解Spring的工作原理,提升技术水平。这份资料包含的案例丰富且注释详尽,对于学习和掌握Spring的高级特性和...
Spring MVC提供了一套完整的MVC框架,包括模型-视图-控制器的交互机制,使得构建Web应用变得简单易行。 Spring Data项目旨在简化数据访问,提供了对多种数据存储的支持,包括JPA、MongoDB等。在5.2.25.RELEASE中,...
`spring-framework-master src` 源码包含了Spring框架的核心组件和模块,是深入理解Spring工作原理的重要参考资料。这里我们将详细探讨Spring源码中的关键知识点。 1. **模块结构** Spring框架由多个模块组成,...
Spring MVC是Spring用于构建Web应用的部分,`DispatcherServlet`是其核心,负责请求的分发。`HandlerMapping`和`HandlerAdapter`负责找到合适的处理器并调用。`ModelAndView`用于返回视图和模型数据。`ViewResolver`...
3. **深入Spring MVC**:分析`DispatcherServlet`的请求处理流程,以及`HandlerMapping`和`HandlerAdapter`的角色。 4. **探究数据访问模块**:查看Spring如何与JDBC、ORM框架集成,以及事务管理的实现。 5. **注解...
阅读Spring源码可以帮助我们理解其工作原理,提高开发效率。以下是一些有效的源码阅读方法: 1. 从核心接口和抽象类入手,理解设计模式和架构。 2. 跟踪关键方法的调用链,了解执行流程。 3. 结合单元测试,验证和...
"Spring Source"指的是Spring框架的源代码,提供了深入了解Spring工作原理的机会。对于开发者来说,查看源码能够帮助提升对Spring的理解,优化编码实践,甚至参与开源贡献。 Spring框架的核心组件包括: 1. **IoC...
- 配置SSM框架:配置Spring的bean、Spring MVC的DispatcherServlet、MyBatis的SqlSessionFactory等。 - 编写Mapper接口和XML文件:定义SQL操作,实现数据访问。 - 实现业务逻辑:编写Service层接口和实现类,处理...