Servlet详解(与SpringMVC的联系)

1,817 阅读5分钟

Servlet的发展过程:

最早的服务器处理程序是CGI,其存在着每次需要对请求进行处理时新建一个CGI对象,所以在速度和内存方面都不是很让人满意;随着JAVA语言的兴起,其跨平台的特性,使得Servlet逐渐流行起来,并且Servlet是可复用的,所以Servlet逐渐取代CGI。

Servlet是如何处理d对应的HTTP请求的呢?

  1. 客户端发起一个HTTP请求到服务器端;
  2. HTTP服务器收到HTTP请求,然后将请求转给Web容器进行处理;
  3. Web容器对HTTP请求内容进行解析,并将信息存储到HttpServletRequest中,并生成HttpResponse(用于存储要返回给客户端的信息),HttpSession(用于管理会话)等对象;
  4. Web容器根据请求的URL决定要转发给哪一个具体的Servlet程序进行处理;
  5. 先由HttpServlet的service方法分析HTTP请求是Get,Post,Put,Delete等方法,然后决定调用选择的Servlet对应于每种请求对象的处理方法;
  6. Servlet处理完成后,将要返回的信息存储到HttpServletResponse对象种并返回给Web容器进行解析转换为HTTP响应并传回客户端。

Web容器是如何找到对应于具体URL的Servlet处理程序的呢?

那首先看Servlet的创建过程:

  1. 在Servlet3.0及以后开始支持通过@WebServelt标注给Web容器提供此Servlet对应的信息,如urlPatterns,name(没有定义时,默认值是Servlet的类名), loadOnStartup(用于表示该Servlet是预加载的,而不是等到需要用到再加载)等;使用web.xml配置同理;
  2. 用户编写的Servlet继承自HttpServlet,然后覆盖HttpServlet的doGet(),doPost()等该Servlet要覆盖的方法完成创建;
  3. 然后Web容器便可以读取这些注解提供的信息用于找到对应于具体URL的Servlet并进行加载用于处理。

如何通过HttpServlet的service方法进行HTTP方法的筛选呢?

用户编写的Servlet继承自HttpServlet,继承图如下:

HttpServlet的内部字段如下:

  1. 首先final字符串常量定义了8种HTTP方法以及是否被修改的标识;
  2. 然后提供了对8种HTTP方法对应处理程序的默认实现,这些如果要用到会被覆盖;
  3. 所以着重看service方法,可以看到其输入参数为ServletRequest, ServletResponse,而没有Http的标识,这是因为在一开始设计Servlet时其面向的不止是处理HTTP请求,所以请求/相应对象的基本行为规范是在ServletRequest(包是javax.servlet),与HTTP相关的行为则由其子接口HttpServletRequest(包是javax.servlet,httpd)定义,所以在HttpServlet中要对该对象进行类型的强转;
  4. 在service方法中通过调用HttpServletRequest.getMethod()方法用于得到HTTP方法,并通过if语句进行相应处理方法的跳转。

如何通过HttpServletResponse对象将处理完的信息传回客户端呢?

通过向Response对象中写入HTML文档信息进行信息传输。

Servlet是可复用的:

了解完Web容器对HTTP消息的处理流程后,之前提到Servlet是可复用的,那是否意味着是线程共享的,那是否会产生线程不安全呢?

理清这个问题我们首先要看Web容器时如何加载Servlet的:

  1. 首先要知道的一点是Web容器是一段Java程序,运行在JVM之上,是JVM的一个进程,可以想想是不是通过localhost:8080端口访问的Tomcat容器呢?
  2. 然后与传统的Java容器不同的是,Web容器不仅持有对象,还负责对象的生命周期与相关服务的连接。
  3. Web容器会为每一个请求加载对应的Servlet实例(如果还没有加载过),并分配一个线程,所以可能存在多个线程共享一个Servlet实例的问题,可以用ThreadLocal解决,或者将对URL请求的处理与对数据业务逻辑的处理分隔开,即使用MVC框架,然后在数据业务逻辑中使用锁等机制保证数据的安全性。

使用Servlet实现MVC框架:

  1. 首先实现Model(即一个普通的POJO),然后提供get,set机制;
  2. Controller:一个Servlet
@WebServlet("/HelloController")
public class HelloController extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private HelloModel model = new HelloModel();
     
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		Cookie[] cookies = request.getCookies();
		if(cookies != null) {
			for(Cookie cookie : cookies) {
				String cookieName = cookie.getName();
				String cookieValue = cookie.getValue();
				request.setAttribute("name", cookieName);
				request.setAttribute("value", cookieValue);
			}
		}
		else {
			Cookie cookie = new Cookie("user", "cjc");
			cookie.setMaxAge(7 * 24 * 60 *60);
			response.addCookie(cookie);
		}
		String name = request.getParameter("user");
		String message = model.doHello(name);
		request.setAttribute("message", message);

		request.getRequestDispatcher("HelloServlet").forward(request, response);
	}
}

与SpringMVC一致,Controller处理完后需要返回model给View进行渲染,此处使用的是将model数据放入HttpServletRequest对象的属性中,然后通过RequestDispatcher对象将该请求(forward)转给HelloServlet进行处理,HelloServlet即是我们的View; 3. View:一个Servlet,负责完善HttpServletResponse对象并返回给客户端;

@WebServlet("/HelloServlet")
public class HelloServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		String user = request.getParameter("user");
		String message = (String) request.getAttribute("message");
		String cookieName = (String)request.getAttribute("name");
		String cookieValue = (String)request.getAttribute("value");
		
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();
		out.println("<html>");
		out.println("<head>");
		out.println("<title>Hello Servlet</title>");
		out.println("</head>");
		out.println("<body>");
		out.println("<h1> Hello!" + user + "!</h1>");
		out.println(message);
		out.println("<h1> cookieName" + cookieName + cookieValue + "!</h1>");
		out.println("</body>");
		out.println("</html>");
		
		out.close();
	}

}

至此,我们通过Servlet实现了一个MVC框架,将处理URL的逻辑,业务逻辑和页面渲染逻辑分离开来,原本这些是通过一个Servlet完成的,这样可以使系统解耦合,架构更加清晰,更容易维护。