下面类图将主要的类及方法抽离出来,以便查看方便,根据类的结构来说明整个请求是如何工作的
主要使用到的技术有Spring的IOC容器和Servlet。
假如我们要实现一个请求home.htm然后返回home.jsp视图资源则
当home.htm请求到达时,我们需要DispatcherServlet来处理该请求,所以首先配置该Servlet
第一步需要在web.xml中配置DispatcherServlet,使该servlet来接收请求并做进一步处理。
dispatch
org.springframework.web.servlet.DispatcherServlet
1
dispatch
*.htm
从类图中很容易看出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文件夹下,
配置文件还是按照命名规范做吧(可以修改为其他名字)
dispatch
org.springframework.web.servlet.DispatcherServlet
namespace
classes/dispatch-servlet
1
当请求到达后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);
}
下面是请求转发的部分
/**
* Render the internal resource given the specified model.
* This includes setting the model as request attributes.
*/
@Override
protected void renderMergedOutputModel(
Map 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 model, HttpServletRequest request) throws Exception {
for (Map.Entry 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
}
}
Hello!This is Training!你好,这里是训练营!
spring将使用实现了Controller接口的HomeController的handleRequest方法来返回映射的视图资源。
在得到MoldelAndView对象后,需要根据这个MoldelAndView对象得到View name然后来解析得到View对象
/**
* Resolve the given view name into a View object (to be rendered).
* 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 null
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 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
.jsp
这些配置完成后,spring就会根据请求地址以及配置信息,找到视图资源并做请求转发操作
总结:整个流程分析下来,其实主要就是做两个操作,
首先请求信息到达DispatchServlet,Servlet中根据请求信息与配置文件找到映射的视图资源
然后使用RequestDispatch请求转发到该视图资源。
另外,可以分成多个bean配置文件,在web.xml中配置载入
org.springframework.web.context.ContextLoaderListener
contextConfigLocation
/WEB-INF/dispatch-data.xml,/WEB-INF/dispatch-service.xml
也有可能是匹配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";