Java深入 - servlet和Spring的DispatcherServlet详解

Java深入 专栏收录该内容
24 篇文章 79 订阅

Servlet是什么

1. Servlet是服务器端运行的一个程序,是一个被编译好的Java类。它不是框架等。

2. Web容器的启动,需要依赖Servlet。当web服务器开始执行时,servlet类就被初始化

3. 当用户通过浏览器访问的时候,会输入URI,这个时候,web服务器就通过Servlet来分发请求执行不同的内容。


一般我们会使用Tomcat来运行Java的web项目。通常我们可以在web项目的目录中可以看到WEB-INF这样的文件夹。这个文件夹是受保护的,外部无法通过url来访问的。文件夹里面包含了web.xml以及classlibs目录。我们要将web项目运行起来,就得在web.xml中定义一个Servlet,因为定义了Servlet,web容器才能知道需要如何去分发请求进行业务处理的。

Servlet是有一个Jar包,里面包含了一些Servlet的相关设计和细节。


一个使用Spring的Servlet的web.xml配置例子:

	<servlet>
		<servlet-name>apiServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-common.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>apiServlet</servlet-name>
		<url-pattern>/api/*</url-pattern>
	</servlet-mapping>



HttpServlet

HttpServlet是一个抽象类,具体的实现自己需要的Servlet需要通过继承该类来实现 。

HttpServlet中主要的几个方法:

方法用途
doGet         处理HTTP GET请求
doPost处理HTTP POST请求
doPut处理HTTP PUT请求
doDelete处理HTTP DELETE请求

Servlet最终调用的是service方法,这个方法中会根据request的Method来判断具体是执行doGet还是doPost

可以看下HttpServlet类:

package javax.servlet.http;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.Locale;
import java.util.ResourceBundle;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;


public abstract class HttpServlet extends GenericServlet
    implements java.io.Serializable
{
    private static final String METHOD_DELETE = "DELETE";
    private static final String METHOD_HEAD = "HEAD";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String METHOD_POST = "POST";
    private static final String METHOD_PUT = "PUT";
    private static final String METHOD_TRACE = "TRACE";

    private static final String HEADER_IFMODSINCE = "If-Modified-Since";
    private static final String HEADER_LASTMOD = "Last-Modified";
    
    private static final String LSTRING_FILE =
	"javax.servlet.http.LocalStrings";
    private static ResourceBundle lStrings =
	ResourceBundle.getBundle(LSTRING_FILE);
   

    public HttpServlet() { }
    
    //处理HTTP的GET请求,你需要实现一个Servlet,然后实现该方法
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException
    {
	String protocol = req.getProtocol();
	String msg = lStrings.getString("http.method_get_not_supported");
	if (protocol.endsWith("1.1")) {
	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
	} else {
	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
	}
    }

    protected long getLastModified(HttpServletRequest req) {
	return -1;
    }


    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException
    {
	NoBodyResponse response = new NoBodyResponse(resp);
	
	doGet(req, response);
	response.setContentLength();
    }
    
    //实现HTTP POST请求
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException
    {
	String protocol = req.getProtocol();
	String msg = lStrings.getString("http.method_post_not_supported");
	if (protocol.endsWith("1.1")) {
	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
	} else {
	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
	}
    }


    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException
    {
	String protocol = req.getProtocol();
	String msg = lStrings.getString("http.method_put_not_supported");
	if (protocol.endsWith("1.1")) {
	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
	} else {
	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
	}
    }


    protected void doDelete(HttpServletRequest req,
			    HttpServletResponse resp)
	throws ServletException, IOException
    {
	String protocol = req.getProtocol();
	String msg = lStrings.getString("http.method_delete_not_supported");
	if (protocol.endsWith("1.1")) {
	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
	} else {
	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
	}
    }
    

    private Method[] getAllDeclaredMethods(Class c) {

        if (c.equals(javax.servlet.http.HttpServlet.class)) {
            return null;
        }

        Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
        Method[] thisMethods = c.getDeclaredMethods();
	
        if ((parentMethods != null) && (parentMethods.length > 0)) {
            Method[] allMethods =
                new Method[parentMethods.length + thisMethods.length];
	    System.arraycopy(parentMethods, 0, allMethods, 0,
                             parentMethods.length);
	    System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
                             thisMethods.length);

	    thisMethods = allMethods;
	}

	return thisMethods;
    }
         
    protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException
    {
	Method[] methods = getAllDeclaredMethods(this.getClass());
	
	boolean ALLOW_GET = false;
	boolean ALLOW_HEAD = false;
	boolean ALLOW_POST = false;
	boolean ALLOW_PUT = false;
	boolean ALLOW_DELETE = false;
	boolean ALLOW_TRACE = true;
	boolean ALLOW_OPTIONS = true;
	
	for (int i=0; i<methods.length; i++) {
	    Method m = methods[i];
	    
	    if (m.getName().equals("doGet")) {
		ALLOW_GET = true;
		ALLOW_HEAD = true;
	    }
	    if (m.getName().equals("doPost")) 
		ALLOW_POST = true;
	    if (m.getName().equals("doPut"))
		ALLOW_PUT = true;
	    if (m.getName().equals("doDelete"))
		ALLOW_DELETE = true;
	    
	}
	
	String allow = null;
	if (ALLOW_GET)
	    if (allow==null) allow=METHOD_GET;
	if (ALLOW_HEAD)
	    if (allow==null) allow=METHOD_HEAD;
	    else allow += ", " + METHOD_HEAD;
	if (ALLOW_POST)
	    if (allow==null) allow=METHOD_POST;
	    else allow += ", " + METHOD_POST;
	if (ALLOW_PUT)
	    if (allow==null) allow=METHOD_PUT;
	    else allow += ", " + METHOD_PUT;
	if (ALLOW_DELETE)
	    if (allow==null) allow=METHOD_DELETE;
	    else allow += ", " + METHOD_DELETE;
	if (ALLOW_TRACE)
	    if (allow==null) allow=METHOD_TRACE;
	    else allow += ", " + METHOD_TRACE;
	if (ALLOW_OPTIONS)
	    if (allow==null) allow=METHOD_OPTIONS;
	    else allow += ", " + METHOD_OPTIONS;
	
	resp.setHeader("Allow", allow);
    }
    
   
    protected void doTrace(HttpServletRequest req, HttpServletResponse resp) 
	throws ServletException, IOException
    {
	
	int responseLength;
	
	String CRLF = "\r\n";
	String responseString = "TRACE "+ req.getRequestURI()+
	    " " + req.getProtocol();
	
	Enumeration reqHeaderEnum = req.getHeaderNames();
	
	while( reqHeaderEnum.hasMoreElements() ) {
	    String headerName = (String)reqHeaderEnum.nextElement();
	    responseString += CRLF + headerName + ": " +
		req.getHeader(headerName); 
	}
	
	responseString += CRLF;
	
	responseLength = responseString.length();
	
	resp.setContentType("message/http");
	resp.setContentLength(responseLength);
	ServletOutputStream out = resp.getOutputStream();
	out.print(responseString);	
	out.close();
	return;
    }		

    //最终所有的具体实现都是需要实现Service方法.容器最终调用的就是该方法
    protected void service(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException
    {
	String method = req.getMethod();
        //这边通过HTTP的方法来判断,具体的HTTP METHOD,然后决定需要执行的方法名称。   
	if (method.equals(METHOD_GET)) {
	    long lastModified = getLastModified(req);
	    if (lastModified == -1) {
		// servlet doesn't support if-modified-since, no reason
		// to go through further expensive logic
		doGet(req, resp);
	    } else {
		long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
		if (ifModifiedSince < (lastModified / 1000 * 1000)) {
		    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
		    maybeSetLastModified(resp, lastModified);
		    doGet(req, resp);
		} else {
		    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
		}
	    }

	} else if (method.equals(METHOD_HEAD)) {
	    long lastModified = getLastModified(req);
	    maybeSetLastModified(resp, lastModified);
	    doHead(req, resp);
        
	} else if (method.equals(METHOD_POST)) {
	    doPost(req, resp);
	    
	} else if (method.equals(METHOD_PUT)) {
	    doPut(req, resp);	
	    
	} else if (method.equals(METHOD_DELETE)) {
	    doDelete(req, resp);
	    
	} else if (method.equals(METHOD_OPTIONS)) {
	    doOptions(req,resp);
	    
	} else if (method.equals(METHOD_TRACE)) {
	    doTrace(req,resp);
	    
	} else {
	    //
	    // Note that this means NO servlet supports whatever
	    // method was requested, anywhere on this server.
	    //

	    String errMsg = lStrings.getString("http.method_not_implemented");
	    Object[] errArgs = new Object[1];
	    errArgs[0] = method;
	    errMsg = MessageFormat.format(errMsg, errArgs);
	    
	    resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
	}
    }

    private void maybeSetLastModified(HttpServletResponse resp,
				      long lastModified) {
	if (resp.containsHeader(HEADER_LASTMOD))
	    return;
	if (lastModified >= 0)
	    resp.setDateHeader(HEADER_LASTMOD, lastModified);
    }
   
    public void service(ServletRequest req, ServletResponse res)
	throws ServletException, IOException
    {
	HttpServletRequest	request;
	HttpServletResponse	response;
	
	try {
	    request = (HttpServletRequest) req;
	    response = (HttpServletResponse) res;
	} catch (ClassCastException e) {
	    throw new ServletException("non-HTTP request or response");
	}
	service(request, response);
    }
}



HttpServlet最上层是继承Servlet这个接口类。Servlet这个接口类非常简单,只定义了5个方法。

可以看下下面的Servlet接口:

package javax.servlet;

import java.io.IOException;




public interface Servlet {

    //容器初始化
    public void init(ServletConfig config) throws ServletException;
    
    
    //获取配置信息
    public ServletConfig getServletConfig();
    
    //具体的service 请求分发
    public void service(ServletRequest req, ServletResponse res)
	throws ServletException, IOException;
	
	
   //获取servlet信息
    public String getServletInfo();
    
    //容器销毁的时候
    public void destroy();
}


Spring的DispatcherServlet

Spring主要通过DispatcherServlet实现了Servlet。DispatcherServlet最上层也是继承的是HttpServlet这个类。

我们主要看下DispatcherServlet两个比较重要的方法:

@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String requestUri = urlPathHelper.getRequestUri(request);
			logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
					" request for [" + requestUri + "]");
		}

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			logger.debug("Taking snapshot of request attributes before include");
			attributesSnapshot = new HashMap<String, Object>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
		if (inputFlashMap != null) {
			request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
		}
		request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
		request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

		try {
			doDispatch(request, response); //这边最终也是调用了doDispatch方法,该方法主要用来处理SPring框架的具体业务分发逻辑。
		}
		finally {
			// Restore the original attribute snapshot, in case of an include.
			if (attributesSnapshot != null) {
				restoreAttributesAfterInclude(request, attributesSnapshot);
			}
		}
	}
        
        //Spring框架最终的分发都是通过该方法的
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		int interceptorIndex = -1;

		try {
			ModelAndView mv;
			boolean errorView = false;

			try {
				processedRequest = checkMultipart(request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				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()) {
						String requestUri = urlPathHelper.getRequestUri(request);
						logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				// 这里是处理前置拦截器
				HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
				if (interceptors != null) {
					for (int i = 0; i < interceptors.length; i++) {
						HandlerInterceptor interceptor = interceptors[i];
						if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
							triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
							return;
						}
						interceptorIndex = i;
					}
				}

				//处理最终的Action逻辑
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				// Do we need view name translation?
				if (mv != null && !mv.hasView()) {
					mv.setViewName(getDefaultViewName(request));
				}

                                //处理后置拦截器
				if (interceptors != null) {
					for (int i = interceptors.length - 1; i >= 0; i--) {
						HandlerInterceptor interceptor = interceptors[i];
						interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
					}
				}
			}
			catch (ModelAndViewDefiningException ex) {
				logger.debug("ModelAndViewDefiningException encountered", ex);
				mv = ex.getModelAndView();
			}
			catch (Exception ex) {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(processedRequest, response, handler, ex);
				errorView = (mv != null);
			}

			// Did the handler return a view to render?
			if (mv != null && !mv.wasCleared()) {
				render(mv, processedRequest, response);
				if (errorView) {
					WebUtils.clearErrorRequestAttributes(request);
				}
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
							"': assuming HandlerAdapter completed request handling");
				}
			}

			// Trigger after-completion for successful outcome.
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
		}

		catch (Exception ex) {
			// Trigger after-completion for thrown exception.
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
			throw ex;
		}
		catch (Error err) {
			ServletException ex = new NestedServletException("Handler processing failed", err);
			// Trigger after-completion for thrown exception.
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
			throw ex;
		}

		finally {
			// Clean up any resources used by a multipart request.
			if (processedRequest != request) {
				cleanupMultipart(processedRequest);
			}
		}
	}



  • 13
    点赞
  • 2
    评论
  • 11
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值