Servlet是一套Web应用的开发规范,我们按照这套规范编码就可以实现一个Web应用,使其在Web容器中运行。
我们最开始学习J2EE时,学习和创建的就是Servlet的实现类,后来学习了MVC框架以后,尤其是SpringMVC,就很少直接创建Servlet的实现类了。虽然SpringMVC简化和隐藏了Servlet,但是我们也要了解Servlet的运行原理,这样对了解SpringMVC的原理也很有帮助
一.继承图:
首先看一下Servlet的类结构
public interface Servlet {
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
public String getServletInfo();
public void destroy();
}
Servlet是个接口,定义了init、service、destroy等方法。其中init方法在容器启动时(或Servlet第一次调用时)会被调用,进行Servlet的初始化工作;service方法会在容器接收到请求后处理请求时调用。
下面看一下Servlet接口的各个实现类,从而分析Servlet的初始化原理和Spring的DispatchServlet是如何作为请求分发器的。
二.Servlet的初始化过程—init(ServletConfig config)方法
GenericServlet:
/**重写init(ServletConfig)方法,将参数config保存在对象中,将init()方法交给实现类重写,这样既可以让本类的init(ServletConfig)得到执行,也可以让实现类的init()得到执行。这种方式在Servlet的实现类中经常用到:模板方法**/ public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); } public void init() throws ServletException { }
重写了init方法,保存了配置参数的对象,将具体的init操作交给实现类
- HttpServlet: 没有重写init相关的方法,定义了一些Servlet中的常量(POST、GET等)和常用方法(doPost、doGet等),供实现类使用和重写
HttpServletBean:
@Override public final void init() throws ServletException { //注意看log的内容,是我们在启动tomcat时控制台经常看到的输出内容 if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } 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) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } /*很重要的方法,在本类中是空方法。HttpServletBean的init()方法做了基本的参数处理后,将其他的 init操作交给了实现类,实现类不重写init()方法,而是重写initServletBean(),这样它们都会得到执行。*/ initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
FrameworkServlet:
//注意看log的内容,是我们在启动tomcat时控制台经常看到的输出内容 @Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { //初始化应用上下文 this.webApplicationContext = initWebApplicationContext(); //交给实现类继续做初始化工作,目前未见相关实现类重写了该方法 initFrameworkServlet(); }。。。 if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +elapsedTime + " ms"); } } /** 初始化应用上下文,包括子容器上下文和父容器上下文,onRefresh(ApplicationContext)方法交给实现类 处理,DispatchServlet会在此方法中对mvc-servlet.xml文件进行读取和参数配置初始化等工作 **/ protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { if (cwac.getParent() == null) { //设置子容器的父容器 cwac.setParent(rootContext); } //初始化应用上下文对象 configureAndRefreshWebApplicationContext(cwac); } } } if (!this.refreshEventReceived) { //实现类进行操作 onRefresh(wac); } 。。。。。 return wac; } protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { 。。。。。。。//wac的初始化工作 //很重要的一步 wac.refresh(); } //AbstractApplicationContext的方法 @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { prepareRefresh(); ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); prepareBeanFactory(beanFactory); try { 。。。。。//BeanFactory和MessageSource初始化 onRefresh(); 。。。。。//注册监听器 } } }
DispatchServlet:
//调用DispatchServlet的onRefresh(ApplicationContext)方法,读取mvc-servlet.xml文件,并初始化 //HandlerMapping、HandlerAdapters等属性 @Override protected void onRefresh(ApplicationContext context) { initStrategies(context); } protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
三.Servlet的调用过程—service(ServletRequest req, ServletResponse res)方法
- GenericServlet:没有重新service(ServletRequest,ServletResponse)方法
HttpServlet:
//重写Servlet的service(ServletRequest,ServletResponse)方法,主要做了请求参数类型判断和参数转换 @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; if (!(req instanceof HttpServletRequest && res instanceof HttpServletResponse)) { throw new ServletException("non-HTTP request or response"); } request = (HttpServletRequest) req; response = (HttpServletResponse) res; service(request, response); } //将请求分为POST、GET、DELETE等几种常见方法,然后分别进行处理。以前写Servlet时都是继承该类,然后 //重写doGet、doPost等方法,对请求进行处理。SpringMVC的处理方式于此不同。 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { 。。。 doGet(req, resp); 。。。 } } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } 。。。 }
- HttpServletBean:未重写service或doXxx方法
FrameworkServlet:
/*重写service方法,增加了PATCH方法的处理,其他方法如GET会调用父类的FrameworkServlet重写了 doXxx方法,实际上会调用本类的doXxx方法,也就是调用SpringMVC的统一请求处理方法---p rocessRequest(HttpServletRequest, HttpServletResponse)*/ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (HttpMethod.PATCH == httpMethod || httpMethod == null) { processRequest(request, response); } else { super.service(request, response); } } @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 。。。 try { //具体的doService实现交给了子类 doService(request, response); } 。。 publishRequestHandledEvent(request, response, startTime, failureCause); 。。。 }
DispatchServlet:
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 。。。//设置相关属性 try {//该方法是SpringMVC处理请求的关键,也是DispatchServlet分发请求的关键:根据 //pathHandlerMapping查询对应的HandlerExecutionChain,调用HandlerAdapter的handle方法进行处理, //获取ModelAndView对象,最后处理结果。 doDispatch(request, response); } 。。。//存储相关属性 }