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

- 客户端发起一个HTTP请求到服务器端;
- HTTP服务器收到HTTP请求,然后将请求转给Web容器进行处理;
- Web容器对HTTP请求内容进行解析,并将信息存储到HttpServletRequest中,并生成HttpResponse(用于存储要返回给客户端的信息),HttpSession(用于管理会话)等对象;
- Web容器根据请求的URL决定要转发给哪一个具体的Servlet程序进行处理;
- 先由HttpServlet的service方法分析HTTP请求是Get,Post,Put,Delete等方法,然后决定调用选择的Servlet对应于每种请求对象的处理方法;
- Servlet处理完成后,将要返回的信息存储到HttpServletResponse对象种并返回给Web容器进行解析转换为HTTP响应并传回客户端。
Web容器是如何找到对应于具体URL的Servlet处理程序的呢?
那首先看Servlet的创建过程:
- 在Servlet3.0及以后开始支持通过@WebServelt标注给Web容器提供此Servlet对应的信息,如urlPatterns,name(没有定义时,默认值是Servlet的类名), loadOnStartup(用于表示该Servlet是预加载的,而不是等到需要用到再加载)等;使用web.xml配置同理;

- 用户编写的Servlet继承自HttpServlet,然后覆盖HttpServlet的doGet(),doPost()等该Servlet要覆盖的方法完成创建;
- 然后Web容器便可以读取这些注解提供的信息用于找到对应于具体URL的Servlet并进行加载用于处理。
如何通过HttpServlet的service方法进行HTTP方法的筛选呢?
用户编写的Servlet继承自HttpServlet,继承图如下:


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

Servlet是可复用的:
了解完Web容器对HTTP消息的处理流程后,之前提到Servlet是可复用的,那是否意味着是线程共享的,那是否会产生线程不安全呢?
理清这个问题我们首先要看Web容器时如何加载Servlet的:
- 首先要知道的一点是Web容器是一段Java程序,运行在JVM之上,是JVM的一个进程,可以想想是不是通过localhost:8080端口访问的Tomcat容器呢?
- 然后与传统的Java容器不同的是,Web容器不仅持有对象,还负责对象的生命周期与相关服务的连接。
- Web容器会为每一个请求加载对应的Servlet实例(如果还没有加载过),并分配一个线程,所以可能存在多个线程共享一个Servlet实例的问题,可以用ThreadLocal解决,或者将对URL请求的处理与对数据业务逻辑的处理分隔开,即使用MVC框架,然后在数据业务逻辑中使用锁等机制保证数据的安全性。
使用Servlet实现MVC框架:
- 首先实现Model(即一个普通的POJO),然后提供get,set机制;
- 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完成的,这样可以使系统解耦合,架构更加清晰,更容易维护。