做过web开发的小伙伴都应该使用过SpringMVC(或者Struts1/Struts2)等框架来处理前端请求,并响应对应的页面;
也有些特别老的项目,直接使用Servlet来处理web请求。
当然,无论使用哪种技术来处理Web请求,底层都是Servlet
Servlet应用发布在Web容器(如Tomcat)中,Web容器对请求参数进行封装,对请求路径进行解析,然后将请求转发到对应的Servlet中,调用Servlet的doGet()或doPost()方法来进行业务处理,处理完成后,返回对应的页面。整个请求结束。
Servlet与Web容器的关系,请参考:https://www.ibm.com/developerworks/cn/java/j-lo-servlet/
@WebServlet("/HelloForm")
public class HelloForm extends HttpServlet {
private static final long serialVersionUID = 1L;
public HelloForm() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 直接跳转到WEB-INF/res.html页面
request.getRequestDispatcher("/res.html").forward(request, response);
}
// 处理 POST 方法请求的方法
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
res
this is res html
HelloForm
servletdemo.HelloForm
HelloForm
/TomcatTest/HelloForm
笔者tomcat端口号设置为8081,将servletDemo项目加入到Tomcat中,启动Tomcat,访问路径
http://localhost:8081/servletdemo/TomcatTest/HelloForm
说明请求成功。
总结:以上展示了最简单的Servlet使用方式,接收到用户请求后,直接跳转到HTML页面,最关键的代码处理即
request.getRequestDispatcher("/res.html").forward(request, response);
获得RequestDispatcher然后调用其forward()方法即可。
读者要记得这句处理,后面关于SpringMVC源码分析的时候,实际最核心的处理也就是在这里。
springweb
springweb
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
/WEB-INF/springweb-servlet.xml
springweb
/
主要是配置servlet和servlet-mapping,拦截所有的请求(/)统一交由DispatcherServlet来处理,并配置参数contextConfigLocation,指明xml路径
主要是配置视图解析器
package test.SpringMVC;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/mvc")
public class MvcController {
@RequestMapping("/hello")
public String hello(){
return "hello";
}
}
Hello World!
访问 http://localhost:8081/springweb/mvc/hello ,可以看到Hello World!出现,测试成功
总结:可以看到SpringMVC的配置使用与Servlet差不多,只是SpringMVC将所有的请求都交由DispatcherServlet来处理
以下进行源码分析的时候,我们重点分析这个DispatcherServlet类就可以了
先看下DispatcherServlet的类结构分析
可知,DispatcherServlet实现了HttpServlet,那也就有对应的init()、doGet()等方法,init()用来初始化资源,doGet()用来处理请求
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
// 重点在这里,子类负责实现,主要来初始化ApplicationContext及一些Resolve
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
一路跟踪到doDispatch()方法,这里是真正的业务实现方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 1.获取handler(主要包含request相对应的Controller处理类和拦截器列表)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 2.获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 3.根据获取到的HandlerAdapter,调用其handler方法,
// handler方法会先执行拦截器的方法,然后执行Controller的对应方法获取对应的ModelAndView,也就是对应的视图
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
...
// 4.跳转到指定页面(跳转到指定的视图)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
...
}
由以上分析可知,初始化资源的任务放在init()方法中;
处理请求时,主要步骤可分为:
* 获取请求对应的HandlerExecutionChain(主要包含request相对应的Controller处理类和拦截器列表)
* 获取HandlerAdapter
* HandlerAdapter.handler()方法会调用拦截器的方法和Controller的方法处理,最终返回一个ModelAndView(主要就是视图路径名称)
* 跳转到指定视图界面
对框架结构的简单分析之后,会发现与最原始的Servlet的使用差不多,只不过包装的过程更复杂点而已。
下文笔者会对请求过程进行详细分析,请关注 SpringMVC源码解析(下)。
参考:Spring源码深度解析(郝佳)