Servlet

1. 什么是Servlet?

  • Servlet就是运行在服务端的一个java程序,通常运行在Tomcat容器中,每当一个HTTP请发发送到服务器时,Tomcat负责创建对应的Servlet对象,并由这个对象来处理当前的HTTP请求;

2. 实现Servlet接口处理HTTP请求

  1. 创建Servlet实现类ServletImpl类并实现Servlet中的方法,我们发现有5个方法需要实现:

    • init():创建Servlet对象后默认调用的初始化方法;
    • service():处理HTTP请求并返回给浏览器;
    • destroy():当Tomcat关闭或Servlet对象销毁的时候调用此方法;
    • getServletConfig():获取Web.xml中的Servlet配置;
    • getServletInfo():获取Servlet信息;
public class ServletImpl implements Servlet {
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("MyServlet被初始化");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("MyServlet被访问");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("MyServlet被销毁");
    }
}
  1. 在web.xml中配置接口映射,为什么要有这个web.xml文件呢?

    • 当接收到一个HTTP请求的时候,Tomcat会去web.xml文件中去查找(Servlet映射)中的(映射路径)/hello;
    • 当找到匹配的映射路径后,就会查看该路径对应的(映射路径对应的Servlet名称)MyServlet;
    • 然后通过Servlet名称去(Servlet配置)中去找该名称对应的Servlet类编译后的class文件路径com.servlet.ServletImpl;
    • 执行ServletImpl类;
  <servlet>
    <servlet-name>ServletImpl</servlet-name>
    <servlet-class>com.servlet.ServletImpl</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ServletImpl</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
  1. 当浏览器第一次访问/hello接口时调用init和service方法,结果如下:
ServletImpl被初始化
ServletImpl被访问
  1. 当浏览器第二次访问/hello接口时不再调用init方法,只调用service方法,结果如下:
ServletImpl被访问
  1. 当关闭Tomcat时,调用destroy方法,结果如下:
ServletImpl被销毁

3. 继承HttpServlet处理HTTP请求

大多数情况下,我们只需要处理HTTP请求,并不关心Servlet对象的创建和销毁,每创建一个新的Servlet接口都要实现5个方法,这样做非常麻烦。所以,我们只需要继承HttpServlet类来重写对应的方法即可,而且HttpServlet本身也实现了Servlet接口并添加了许多处理HTTP请求的方法,比如处理Get请求、Post请求等等。
实现
继承HttpServlet处理HTTP请求的方法如下:

public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("处理http的get请求");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("处理http的post请求");
    }
}
  <servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>com.servlet.MyServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/hello1</url-pattern>
  </servlet-mapping>

那么在HttpServlet中的service方法都干了什么呢?

    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();

        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) {
                    // 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);
        }
    }

我们可以看出,service方法先获取到request请求中的方法,判断这个方法属于哪种方法,如果是GET方法,则调用我们重写的doGet方法进行处理。

4. Servlet的生命周期

Servlet的生命周期以下3个过程:

  1. 创建Servlet对象:当Http请求第一次访问Tomcat的时候,Tomcat会创建一个对应的Servlet实例,创建这个实例后默认调用init方法,之后的相同的Http请求不会再次创建对象,而是获取已有的对象,所以也不会再次调用init方法,只会调用service方法;
  2. 处理Http请求:当浏览器发送了Http请求后,Servlet会调用service方法进行处理;
  3. 销毁Servlet实例:当Tomcat关闭或者检测到某个Servlet对象长时间没有被访问的时候,Tomcat会自动销毁这个Servlet实例,释放掉占用的资源,并调用destroy方法。

5. ServletConfig对象

  • ServletConfig对象是一个Map容器;
  • 我们可以通过ServletConfig对象读取web.xml中配置的初始化参数,比如:
  <servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>com.servlet.MyServlet</servlet-class>
    <init-param>
      <param-name>key1</param-name>
      <param-value>value1</param-value>
    </init-param>
  </servlet>
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("处理http的get请求");
        String value = getServletConfig().getInitParameter("key1");
        System.out.println(value);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("处理http的post请求");
    }
}

访问/hello1接口后,控制台打印出:

处理http的get请求
value1

6. ServletContext对象

  • ServletContext对象是一个Map容器;
  • ServletContext对象只有一个,所有Servlet都共享着这个对象,相当于一个共享变量,所以Servlet之间可以通过ServletContext对象进行通信,示例代码如下:
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("访问MyServlet");
        String value = (String) getServletContext().getAttribute("key3");
        System.out.println(value);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("处理http的post请求");
    }
}

```java
public class MyServlet2 extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("访问MyServlet2");
        getServletContext().setAttribute("key3", "MyServlet2设置的值");
    }
}
访问MyServlet2
访问MyServlet
MyServlet2设置的值
  • ServletConfig对象配置的是单个Servlet的信息,而ServletContext对象配置的是所有Servlet都具备的信息, 即整个web的配置信息,示例代码如下:
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("处理http的get请求");
        String value = getServletContext().getInitParameter("key2");
        System.out.println(value);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("处理http的post请求");
    }
}
  <context-param>
    <param-name>key2</param-name>
    <param-value>我是共享信息</param-value>
  </context-param>
处理http的get请求
我是共享信息
  • 读取资源文件,将资源文件放在WEB-INF同级目录下,并通过一下方法获取:
    在这里插入图片描述
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        InputStream resourceAsStream = getServletContext().getResourceAsStream("test.xlsx");
        System.out.println(resourceAsStream);
    }
}

7. HttpServletRequest对象

  • HttpServletRequest对象封装了HTTP请求的所有信息,包括请求行、请求头、请求数据,我们可以通过该对象的方法获取到HTTP协议中的信息;
  • 常用方法如下:
    获得浏览器基本信息:
    getRequestURL⽅法返回客户端发出请求时的完整URL。
    getRequestURI⽅法返回请求⾏中的资源名部分。
    getQueryString ⽅法返回请求⾏中的参数部分。
    getPathInfo⽅法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路
    径之后和查询参数之前的内容,它以“/”开头。
    getRemoteAddr⽅法返回发出请求的客户机的IP地址
    getRemoteHost⽅法返回发出请求的客户机的完整主机名
    getRemotePort⽅法返回客户机所使⽤的⽹络端⼝号
    getLocalAddr⽅法返回WEB服务器的IP地址。
    getLocalName⽅法返回WEB服务器的主机名
    获得请求头:
    getHeader⽅法
    getHeaders⽅法
    getHeaderNames⽅法
    获得请求参数(客户端提交的数据)
    getParameter⽅法
    getParameterValues(String name)⽅法
    getParameterNames⽅法
    getParameterMap⽅法

8. HttpServletResponse对象

  • HttpServletResponse对象封装了HTTP响应协议的所有内容,包括:响应行、响应头、返回数据;

  • 当返回字符串数据时,指定返回的内容类型和字符类型,再通过Writer对象写入数据:

     response.setContentType("application/json; charset=utf-8");
     response.getWriter().write("中国");
    
  • 当实现文件下载时,做法如下:

public class MyServlet2 extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        InputStream resourceAsStream = getServletContext().getResourceAsStream("test.xlsx");
        response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("下载文件.xlsx", "UTF-8"));

        //将读取的内容发送给浏览器
        int len = 0;
        byte[] bytes = new byte[1024];
        ServletOutputStream outputStream = response.getOutputStream();
        while ((len = resourceAsStream.read(bytes)) > 0) {
            outputStream.write(bytes, 0, len);
        }
        outputStream.close();
        resourceAsStream.close();
    }
}

9. 转发与重定向

转发与重定向的应用场景:

  • 转发:访问Servlet处理业务逻辑,执行request.getRequestDispatcher("/index.html").forward(request, response)跳转到前台index.html文件,并发request对象带过去,在index.html中把数据取出来进行页面渲染,此时浏览器的URL不变;
  • 重定向:提交登录表单,登录验证通过后执行response.sendRedirect("/web应用名/index.html")跳转到应用首页,此时浏览器的URL变了;

转发与重定向的区别:

  • 实际发生的位置不同,地址栏不同

     - 转发是在服务器进行跳转的,浏览器的地址栏是灭有变化的,当访问servlet1的时候,即使跳到了servlet2的页面,浏览器的地址还是servlet1,所以转发只是一次的http请求;
     - 重定向是由浏览器进行跳转的,进行重定向的时候,浏览器的地址栏会发生变化,重定向的本质是:response的状态码为301 + 响应头里增加Location属性,值为要重定向的地址。所以重定向需要浏览器发出两次http请求;
    
  • 用法不同

      - 转发的用法:  request.getRequestDispatcher("/URI").forward(request, response);
      - 重定向的用法:response.sendRedirect("/web应用/URI");
    
  • 能过去往的URL的范围不同:

      - 转发是服务器跳转,所以只能去当前web应用的资源;
      - 重定向是浏览器跳转,可以去往任何资源;
    
  • 传递的数据类型不同:

      - 转发的request对象可以传递任意类型的数据;
      - 重定向只能传递字符串;
    
  • 跳转到时间不同:

      - 转发的跳转时间是执行完这个语句就转发;
      - 重定向需要整个页面执行完才进行跳转;
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值