Servlet

一、  Servlet

1.  Servlet 简介

* 什么是 Servlet?
1. Servlet 的命名可以看出 sun 命名的特点,如 Applet 表示小应用程序;Scriptlet = Script + Applet,表示小脚本程序;同样 Servlet = Service + Applet,表示小服务程序
2. Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层
3. 使用 Servlet,可以收集来自网页表单的用户输入,可以与其它服务器资源(如数据库或基于 Java 的应用程序)进行通信,还可以动态创建网页
4. Servlet 是和平台无关的服务器端组件(java 编写的,跨平台),它运行在 Servlet 容器中

* 什么是 Servlet 容器?
1. Servlet 容器主要是 JavaWeb 应用提供运行时环境,所以也可以称之为 JavaWeb 应用容器,或者 Servlet/JSP 容器
2. Servlet 容器负责 Servlet 和客户的通信以及调用 Servlet 的方法,Servlet 和客户的通信采用“请求/响应”的模式
3. Servlet 容器有哪些:目前最流行的 Servlet 容器软件包括: Tomcat、Jetty、Jboss
4. 下图显示了 Servlet 在 Web 应用程序中的位置

2.  Servlet 生命周期

* Servlet 生命周期可被定义为从创建直到毁灭的整个过程,以下是 Servlet 遵循的过程:
1. Servlet 通过调用 init () 方法进行初始化
2. Servlet 调用 service() 方法来处理客户端的请求
3. Servlet 通过调用 destroy() 方法终止(结束)
4. 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的

* 注意:
1. Servlet 创建于用户第一次调用该 Servlet 的 URL 时,但可以在 web.xml 中配置 <load-on-startup>1</load-on-startup>,来指定 Servlet 在服务器第一次启动时被加载
2. init() 方法只会被调用一次,它在第一次创建 Servlet 时被调用。通常情况下你可以在 init() 方法中初始化数据库连接、打开文件以及其他类似的初始化活动
3. service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doHead、doPut、doDelete 等方法
4. destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用
5. destroy() 方法可以让你的 Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动
6. 在调用 destroy() 方法之后,Servlet 对象被标记为垃圾回收

* 下图显示了一个典型的 Servlet 生命周期方案
1. 第一个到达服务器的 HTTP 请求被委派到 Servlet 容器
2. Servlet 容器在调用 service() 方法之前加载 Servlet
3. 然后 Servlet 容器处理由多个线程产生的多个请求,每个线程执行一个单一的 Servlet 实例的 service() 方法

3.  Servlet 部署

* 1. 默认情况下,Servlet 应用程序位于路径 <Tomcat-installation-directory>/webapps/ROOT 下,且类文件放在 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/classes
* 2. 如果你有一个完全合格的类名称 com.myServlet.Servlet类名,那么这个 Servlet 类必须位于 WEB-INF/classes/com/myServlet/Servlet类名.class
* 3. 把 Servlet类名.class 复制到 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/classes 中,并在位于 <Tomcat-installation-directory>/webapps/ROOT/WEB-INF/ 的 web.xml 文件中创建以下条目:
<servlet>
    <servlet-name>myServlet</servlet-name>
    <servlet-class>com.myServlet.Servlet类名</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>myServlet</servlet-name>
    <url-pattern>/Servlet别名</url-pattern>
  </servlet-mapping>
* 4. 最后,使用 <Tomcat-installation-directory>instartup.bat(在 Windows 上)或 <Tomcat-installation-directory>/bin/startup.sh(在 Linux/Solaris 等上)启动 Tomcat 服务器,最后在浏览器的地址栏中输入 http://localhost:8080/Servlet别名

4.  Servlet 常见状态码

* 如果是用 JavaWeb 创建的项目,则向浏览器发送的请求格式为:http://localhost:8080/项目名/Servlet别名
* 1. 200 OK:请求成功
* 2. 404 Not Found:服务器无法找到所请求的页面,可能原因:
a. 在请求地址中 Servlet 的别名书写错误
b. 虚拟项目名称拼写错误
* 3. 405 Method Not Allowed:请求方式不允许
原因:请求方式和 Servlet 中的方式不匹配所造成
解决:尽量使用 service() 方法进行请求处理,尽量不要在 service() 方法中调用父类 service() 方法( super.service(arg0, arg1);)
* 4. 500 Internal Server Error:内部服务错误,服务器遇到了一个意外的情况
原因a:java.lang.ClassNotFoundException
    解决:在 web.xml 中检验 Servlet 配置 <servlet-class>com.myServlet.Servlet类名</servlet-class> 中的路径是否拼写错误
    原因b:service() 方法体的代码执行错误导致
   解决:根据错误提示对 service() 方法中的代码进行错误更改

5.  HttpServletRequest

* Request 对象学习:
* 1. 作用:Request 对象中的封存了当前请求的所有请求信息
* 2. 使用:
       获取请求头数据
         req.getMethod();// 获取请求方式
         req.getRequestURL();// 获取 URL
         req.getRequestURI();// 获取 URI
         req.getScheme();// 获取协议
获取请求行数据 req.getHeader(String name);// 获取指定的请求行信息
req.getHeaders(String name);// 获取指定请求头的所有信息的枚举集合 req.getHeaderNames();// 返回所有请求行的键名的枚举集合
获取用户数据(重点) req.getParameter(String name);// 获取指定的用户数据 req.getParameterValues(String name);// 返回同键不同值的请求数据(复选框),返回 String 类型的数组 req.getParameterNames();// 返回所有用户请求数据的枚举集合 注意:如果要获取的数据不存在,不会报错,返回 null * 3. Request 对象由 Tomcat 服务器创建,并作为实参传递给处理请求的 Servlet 的 service() 方法
* 4. 解决请求乱码问题:
     a.在 service() 方法中对获取的请求数据进行重新编码,如:
String uname = req.getParameter(“uname”);
          uname = String(uname.getBytes("iso8859-1"), "utf-8");

     b.Get 方式请求步骤(缺一不可):
          1)在 service() 方法中使用:req.setCharacterEncoding("utf-8");
          2)在 Tomcat 安装目录中的 conf 目录下找到 server.xml 文件,进行如下配置:
               <Connector port="8888" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" useBodyEncodingForURI="true"/>

     c.Post 方式请求:
         只需要在 service() 方法中获取请求前使用:req.setCharacterEncoding("utf-8");
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class RequestServlet extends HttpServlet{
      @Override
      protected void service(HttpServletRequest req, HttpServletResponse resp)
                 throws ServletException, IOException{
          //获取请求头数据
               //获取请求方式
               String method = req.getMethod();
               System.out.println(method);
               //获取请求URL
               StringBuffer url = req.getRequestURL();
               System.out.println(url);
               //获取URI
               String uri = req.getRequestURI();
               System.out.println(uri);
               //获取协议
               String h = req.getScheme();
               System.out.println(h);
               
          //获取请求行数据
               //获取指定的请求行信息
               String value = req.getHeader("User-Agent");
               System.out.print(value);
               //获取所有的请求行的键的枚举
               Enumeration e = req.getHeaderNames();
               while(e.hasMoreElements()){
                   String name = (String)e.nextElement();
                   String value02 = req.getHeader(name);
                   System.out.println(name + ":" + value02);
               }
           
          //获取用户信息(重要)
               String name = req.getParameter("uname");
               String password = req.getParameter("pwd");
               System.out.println(name + ":" + password);
               
               //获取同键不同值的请求数据(复选框),返回数组
               String[] favs = req.getParameterValues("fav");
               if(favs!=null){
                   for(String f : favs)
                       System.out.println(f);
               }      
      }
}
request 对象实例

6.  HttpServletResponse

* Response 对象学习:
* 1. 作用:Response 对象是用来响应数据到浏览器
* 2. 使用:
       设置响应头
           resp.setHeader(String name, String Value);// 在响应头中添加或更新信息,同键会覆盖
           resp.addHeader(String name, String Value);// 在响应头中添加信息,不会同键覆盖
       设置响应状态
           resp.sendError(int num, String msg);
       设置响应编码格式
           resp.setContentType("text/html;charset=utf-8");
或者 resp.setHeader("content-type", "text/plain;charset=utf-8"); 设置响应实体 resp.getWriter().write(String str); * 3. service()方法中的一般编写流程:
    1) 设置请求编码格式
       req.setCharacterEncoding("utf-8");
    2) 设置响应编码格式
        resp.setContentType("text/html;charset=utf-8");
    3) 获取请求信息
        uname = req.getParameter("uname");
        pwd = req.getParameter("pwd");
    4) 处理请求信息
        a.创建业务层对象
        b.调用业务层对象的方法
    5) 响应处理结果
        resp.getWriter().write("....");

7.  请求转发和重定向

* 请求转发的学习:
* 1. 使用:
       req.getRequestDispatcher("URL/URI").forward(req, resp);
       如:req.getRequestDispatcher("/index.jsp").forward(req, resp);
* 2. 作用:
实现了多个 Servlet 联动处理请求,这样就能避免代码冗余,让不同 Servlet 的职责更加明确 * 3. 特点: a.一次请求,一个 Request 对象 b.浏览器地址信息不改变 * 4. 请求转发后直接用 return; 结束即可 * 5. 使用 Request 对象实现不同 Servlet 的数据流转: req.setAttribute(String name, Object value); req.getAttribute(String name);
作用:在一次请求中实现不同 Servlet 间的数据共享

* 重定向的学习:
* 1. 使用:
      resp.sendRedirect("URL/URI");
      如:resp.sendRedirect(req.getContextPath() + "/index.jsp");
* 2. 作用:
a.解决了 Servlet 当前无法进行处理请求的问题
b.解决了表单数据重复提交的问题
* 3. 特点:
      a.两次请求,两个 Request 对象
      b.浏览器地址栏信息改变
* 4. 时机:
      a.如果请求中有表单数据,而数据比较重要,不宜重复提交,建议使用重定向
      b.如果请求被 Servlet 接收后无法进行处理,建议使用重定向定位到可以处理的资源

 

8.  Cookie

* Cookie 学习:
* 1. Cookie 本质上是由服务器端生成保存在用户浏览器(客户端)上的小文本文件
* 2. 作用:实现了一个用户发送不同请求时的数据共享
* 3. 工作原理:用户第一次访问服务器时,服务器端会在响应中利用 Set-Cookie header 来创建一个 Cookie,发送给 User-Agent(一般是浏览器),浏览器会将 Cookie 的 key/value 保存到某个目录下的文本文件内,在下一次请求中通过 Cookie header 包含该 Cookie,并且把它返回至服务器,从而完成用户身份等信息的校验
* 4. Cookie 的两种储存方式:
a.临时存储:存储在浏览器的运行内存中,关闭浏览器就失效 b.定时存储:设置了 Cookie 的有效期,存储在了客户端的硬盘中,在有效期内符合路径要求的请求都会附带该 Cookie 里的信息 * 5. 使用: 1)创建 Cookie 对象 Cookie c = new Cookie(String name, String value); 2)设置和获取有效期限 c.setMaxAge(int seconds);// 单位是:秒
c.getMaxAge(); 3)设置和获取有效路径 c.setPath("URL/URI");
c.getPath(); 4)响应 Cookie 信息给客户端 resp.addCookie(c); 5)获取 Cookie 信息 Cookie[] cks = req.getCookies(); if(cks != null){ // 避免出现空指针异常 for(Cookie c : cks){ String name = c.getName(); String value = c.getValue(); } }
* 6. 注意:一个 Cookie 对象存储一条数据;多条数据,可以创建个 Cookie 对象进行存储
/**
 * 模拟三天免登陆
 * 
 * Cookie 信息校验
 *   a.判断请求中是否带 Cookie 信息
 *   b.如果没有则请求转发给登录页面
 *   c.如果有则校验 Cookie 信息是否正确
 *   d.如果校验正确则直接响应主页面给用户
 *   e.如果不正确则请求转发给登录页面
 */
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.pojo.User;
import com.service.LoginService;
import com.service.impl.LoginServiceImpl;

public class CookieServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req,HttpServletResponse resp)
          throws ServletException,IOException{
        //设置请求编码
        req.setCharacterEncoding("utf-8");
        //设置响应编码格式
        resp.setContentType("text/html;charset=utf-8");
        //获取请求信息,获取 Cookie 信息
        Cookie[] cks = req.getCookies();   
        //处理请求信息
            //遍历Coolie信息
            if(cks!=null){//避免出现空指针异常
                System.out.println("have cookies");
                String uid = "";
                  for(Cookie c:cks){
                      if("uid".equals(c.getName())){
                          uid = c.getValue();
                          System.out.println(uid);
                      }
                  }
                  //校验 UID 是否存在
                  if("".equals(uid)){
                        //请求转发
                        req.getRequestDispatcher("/login.jsp").forward(req,resp);
                        return;
                  }else{
                        //校验 UID 用户信息
                        //获取业务层对象
                        LoginService ls = new LoginServiceImpl();
                        User u = ls.checkUidService(uid);
                        if(u!=null){
                              //登录成功,响应主页面
                              resp.sendRedirect("/index.jsp");
                        }else{
                              //请求转发
                              req.getRequestDispatcher("/login.jsp").forward(req,resp);
                              return;
                        }
                  }      
              }else{
                   System.out.println("have no cookies");
                   //请求转发
                   req.getRequestDispatcher("/login.jsp").forward(req,resp);
                   return;
              }
    }
}


import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.pojo.User;
import com.service.LoginService;
import com.service.impl.LoginServiceImpl;

public class LoginServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        // 设置响应编码格式
        resp.setContentType("text/html;charset=utf-8");
        // 获取请求信息
        String uname = req.getParameter("uname");
        // 使用 String 进行重新编码,避免出现乱码
        uname = new String(uname.getBytes("iso8859-1"), "utf-8");
        String pwd = req.getParameter("pwd");
        System.out.println(uname + ":" + pwd);
        // 处理请求信息
           // 创建和获取业务层对象
              LoginService impls = new LoginServiceImpl();
              User u = impls.checkLoginService(uname,pwd);
        
        // 响应处理结果
              if(u!=null){
                  // 创建 Cookie 信息实现三天免登陆,注意:不要直接用 Cookie 存放用户密码
                       Cookie c = new Cookie("uid", u.getUid()+"");
                       // 设置 Cookie 的有效期和有效路径
                       c.setMaxAge(3*24*3600);
                       c.setPath("/login/ck");
                       // 添加 Cookie 信息
                       resp.addCookie(c);
                       resp.getWriter().write("登录成功!");
              }else{
                  // resp.getWriter().write("登录失败!");
                  // 请求转发:
                     req.getRequestDispatcher("/login.jsp").forward(req,resp);
                     return;
              }
    }
}
Cookie 实例

9.  HttpSession

* Session 对象学习:
* 1. 作用:实现了一个用户发送不同请求时的数据共享 
* 2. 工作原理:用户第一次访问服务器时,服务器会创建一个 Session 对象,并将该 Session 对象的 JSESSIONID 使用 Cookie 技术存储到浏览器内存中,保证用户的其他请求都能够获取到同一个 Session 对象,也就实现了用户的不同请求时的数据共享
* 3. 特点:
       a.由服务器创建
       b.存储在服务器端
       c.依赖 Cookie 技术
d.注意:JSESSIONID 存储在了 Cookie 的临时存储空间中,即浏览器运行内存中,浏览器关闭则失效 e.作用域为:一次会话,只要在 JSESSIONID 不失效和 Session 对象不失效的情况下为整个项目内
f.Session 对象的默认有效时间为30分钟,可通过 Tomcat 目录下 web.xml 中的 <session-timeout>30</session-timeout> 来修改默认时间 * 4. 使用: 1)创建/获取 Session 对象 HttpSession session = req.getSession(); a.如果请求中拥有 Session 的标识符(JSESSIONID),则返回其对应的 Session 对象 b.如果请求中没有 Session 的标识符(JSESSIONID),则创建新的 Session 对象,并将其 JSESSIONID 作为 Cookie 数据存储到浏览器内存中 c.如果 Session 对象失效了,也会重新创建一个 Session 对象,并将其 JSESSIONID 存储在浏览器内存中 2)设置和获取 Session 会话的有效时间
session.setMaxInactiveInterval(int seconds);// 单位为:秒
session.getMaxInactiveInterval(); a.注意:在指定的时间内 Session 对象没有被使用则失效,如果使用了则重新计时
b.可通过 Tomcat 目录下 web.xml 中的 <session-timeout>30</session-timeout> 来修改 Session 对象的默认时间 3)强制 Session 会话失效 session.invalidate(); 4)存储和获取 Session 数据 session.setAttribute(String name, Object value); session.getAttribute(String name);// 返回 Object 类型 a.注意:存储的动作和获取的动作可以发生在不同的请求中,但是存储动作一定要先于获取动作
* 5. 使用时机:一般用户在登录 Web 项目时会将个人信息存储到 Session 对象中,供该用户的其他请求使用 * 6. 如何判断 Session 是否失效? a.将用户请求中的 JSESSIONID 和后台获取到的 Session 对象的 JSESSIONID 进行比对 b.如果一致,则 Session 没有失效 c.如果不一致,则 Session 失效了,重定向到登录界面,让用户重新登录

10.  ServletContext

* ServletContext 对象学习:
* 1. ServletContext 是一个全局的储存信息的空间,服务器开始就存在,服务器关闭才释放 * 2. 作用:实现了不同的用户共享数据
* 3. 特点: a.由服务器创建 b.存储在服务器端
c.所有用户共享 d.作用域为:整个项目内 e.生命周期为:服务器启动到服务器关闭
f.使用时机:涉及到不同用户共享数据,而这些数据量不大,同时又不希望写入数据库中,我们就可以考虑使用 ServletContext
g.使用建议:因为 ServletContext 中的数据会长时间存储在服务器中,这样就会占用很多内存,因此在使用 ServletContext 时,建议不要往里面添加过大的数据! * 4. 使用: 1)获取 ServletContext 对象 //第一种方式: ServletContext sc = this.getServletContext(); //第二种方式: ServletContext sc = this.getServletConfig().getServletContext(); //第三种方式: ServletContext sc = req.getSession().getServletContext(); 2)存储和获取 ServletContext 数据 sc.setAttribute(String name, Object value); sc.getAttribute(String name);// 返回类型是 Object a.注意:不同的用户可以使用 ServletContext 对象进行数据的存取,如果获取的数据不存在则返回 null 3)使用 ServletContext 对象获取全局的、整个 Web 应用的初始化参数 sc.getInitParameter(String name); // 根据键名返回 web.xml 中配置的全局初始化参数的 String 类型的值,如果数据不存在返回 null sc.getInitParameterNames();// 返回键名的枚举(Enumeration)
* 5. 在 web.xml 配置全局的初始化参数 <context-param> <param-name>name</param-name> <param-value>value</param-value> </context-param>
a.注意:一组 <context-param> 标签只能存储一组键值对,多组键值对可以配置多个 <context-param> 进行存储
b.作用:将静态数据和代码进行解耦 * 6. 获取 WebContent 目录下的资源的绝对路径 String path = sc.getRealPath("/index.jsp");// 参数为 Web 根目录下的路径 a.注意:ServletContext 只能获取在 Web 根目录下的文件的全路径
* 7. 获取 WebContent 目录下资源的流对象 InputStream is = sc.getResourceAsStream("/index.jsp")// 参数为 Web 根目录下的路径 a.注意:ServletContext 只能获取在 Web 根目录下的资源流对象
b.如果资源放在了 src 目录下,这时就不能用 ServletContext 来获取流对象了,必须使用类加载器获取(类加载器的默认读取路径是 src 根目录):
InputStream is = this.class.getClassLoader().getResourceAsStream("database.properties");
c.如果资源还没有直接在 src 目录下,而是在 src 目录下的某个包下,此时类加载器要加上包的路径
InputStream is = this.class.getClassLoader().getResourceAsStream("com/ncdx/database.properties");

* 8.
由于一个 Web 应用中的所有 Servlet 共享同一个 ServletContext 对象,因此 Servlet 对象之间可以通过 ServletContext 对象来实现通讯,即多个 Servlet 可以通过 ServletContext 对象来实现数据间的共享
* 9. 为了方便大家理解,我们将 ServletContext 和 Cookie、Session 做一个简单对比,如下图:

11.  ServletConfig

* ServletConfig 对象学习:
* 1. 问题:如何获取在 web.xml 中给每个 Servlet 单独配置初始化参数呢?
* 2. 解决:使用 ServletConfig 对象
* 3. 使用:
       1)获取 ServletConfig 对象
ServletConfig sc = this.getServletConfig();
2)获取 web.xml 中的配置的初始化参数
sc.getInitParameter(String name);// 根据键名返回 web.xml 中配置的初始化参数的 String 类型的值,如果数据不存在返回 null
sc.getInitParameterNames();// 返回键名的枚举(Enumeration)
* 4. web.xml 中为某个 Servlet 单独配置的初始化参数,如:
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

 

12.  MVC 架构模式

* 什么是 MVC 架构模式?
* 1. MVC 模式是 Model-View-Controller 的简称,是软件工程中的一种软件架构模式,分为三个基本部分,分别是:模型(Model)、视图(View)和控制器(Controller)
1)Controller:负责转发请求,对请求进行处理 2)View:负责界面显示 3)Model:业务功能编写,例如算法实现、数据库设计以及数据存取操作实现

* 2. MVC 设计理念是实现动作控制、数据处理、结果显示三者分离

* 3. MVC 模式在 Web 开发中有很大的优势,它完美规避了 JSP 与 Servlet 各自的缺点,让 Servlet 只负责业务逻辑部分,而不会生成 HTML 代码;同时 JSP 中也不会充斥着大量的业务代码,这样极大提高了代码的可读性可维护性
* 4. MVC 模式的工作原理如下图所示

1)Web 浏览器发送 HTTP 请求到服务端,然后被 Controller(Servlet) 获取并进行处理(例如参数解析、请求转发)
2)Controller(Servlet) 调用核心业务逻辑——Model 部分,获得结果
3)Controller(Servlet) 将逻辑处理结果交给 View(JSP),动态输出 HTML 内容
4)动态生成的 HTML 内容返回到浏览器中显示

原文地址:https://www.cnblogs.com/IT-LFP/p/11666789.html