[Java学习]8、Servlet类

发布于 2022-05-11  34 次阅读


Servlet的原理

Servlet调用过程

Servlet是由Web服务器调用,web服务器在收到浏览器请求之后,会:

客户端输入url访问服务器
服务器解析输入的url解析到访问servle的资源路径
服务器根据解析到的资源路径,在web.xml文件里面找到对应的< url-pattenr >,如果有,同时找到servlet名称
根据servlet名称找到对应的servlet类
服务器自动创建servlet 类的对象,调用其中的方法

Servlet的生命周期

  • 1、Servlet的生命周期由Servlet容器(如:Tomcat)控制

    容器如何处理请求:

  • 2、生命周期的各个阶段

初始化和销毁在一个生命周期中只能发生一次。服务可以多次调用。

实例代码:

package com.mytext.servlet;

import jakarta.servlet.*;
import java.io.IOException;

public class Servletlife implements Servlet{
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("初始化init");
    }
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("服务service");
    }
    @Override
    public String getServletInfo() {
        return null;
    }
    @Override
    public void destroy() {
        System.out.println("停止");
    }
}

当我们访问网页时,服务器端会给我们回显:

ServletConfig接口

在运行 Servlet 程序时,可能需要一些辅助信息,例如,文件使用的编码、使用 Servlet 程序的共享信息等,这些信息可以在 web.xml 文件中使用一个或多个 <init-param> 元素进行配置。当 Tomcat 初始化一个 Servlet 时,会将该 Servlet 的配置信息封装到 ServletConfig 对象中,此时可以通过调用 init(ServletConfig config)方法将 ServletConfig 对象传递给 Servlet。
ServletConfig 接口中定义了一系列获取配置信息的方法,如表 1 所示

下面以 getInitParameter() 方法为例,分步骤讲解该方法的使用。

  • 1)创建Servlet

    在 com.mytext.servlet 包中创建一个名称为 servletconfig 的 Servlet 类,并在类中编写用于读取 web.xml 文件中参数信息的代码,如下所示。

package com.mytext.servlet;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;

public class servletconfig extends HttpServlet {
    protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        ServletConfig config = this.getServletConfig();
        String whoami = ((ServletConfig) config).getInitParameter("name");
        out.println("I'm " + whoami);
    }
    protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
  • 2)配置参数信息
    在web.xml中加入:
   <servlet>
    <servlet-name>servletconfig</servlet-name>
    <servlet-class>com.mytext.servlet.servletconfig</servlet-class>
    <init-param>
      <param-name>name</param-name>
      <param-value>yourfather</param-value>
    </init-param>
  </servlet>
  <!--Servlet的请求路径-->
  <servlet-mapping>
    <servlet-name>servletconfig</servlet-name>
    <url-pattern>/servletconfig</url-pattern>
  </servlet-mapping>
  • 3)运行项目并查看结果
    访问http://localhost:8080/hello_war_exploded/servletconfig

可以看出,web.xml 文件中为 servletconfig 配置的编码信息被读取了出来。由此可见,通过 ServletConfig 对象可以获得 web.xml 文件中的参数信息。

ServletContext接口

当 Tomcat 启动时,Tomcat 会为每个 Web 应用创建一个唯一的 ServletContext 对象代表当前的 Web 应用,该对象封装了当前 Web 应用的所有信息。可以利用该对象获取 Web 应用程序的初始化信息、读取资源文件等。下面对 ServletContext 接口的不同作用分别进行讲解。

1. 获取 Web 应用程序的初始化参数

在 web.xml 文件中,不仅可以配置 Servlet 的映射信息,还可以配置整个 Web 应用的初始化信息。Web 应用初始化参数的配置方式具体如下所示:

    <context-param>
        <param-name>namecnm</param-name>
        <param-value>cnm</param-value>
    </context-param>
    <context-param>
        <param-name>way</param-name>
        <param-value>23333</param-value>
    </context-param>
package com.mytext.servlet;


import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;


import java.io.IOException;


public class servletcontext extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
        ServletContext context= (ServletContext) this.getServletContext();
        String name=(String) context.getInitParameter("namecnm");
        res.setContentType("text/html");
        res.setCharacterEncoding("utf-8");
        res.getWriter().print("name:"+name);
        res.getWriter().print("<br>");


    }
    protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

2、共享数据

这里的域指的是存取数据的操作范围,整个 web 工程。
域对象
存数据:setAttribute()
取数据:getAttribute()
删除数据:removeAttribute()

我在这个Servlet中保存的数据,可以在另外一个servlet中拿到;

setservlet.java

package com.mytext.servlet;


import jakarta.servlet.Servlet;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class setservlet extends HttpServlet {
    protected void doGet(HttpServletRequest req , HttpServletResponse res) throws IOException
    {
        ServletContext context=this.getServletContext();
        String username= "landasika";
        context.setAttribute("user",username);//将user保存在context中,他的值是username


    }
}


package com.mytext.servlet;

import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;


public class getservlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context=this.getServletContext();
        String name=(String) context.getAttribute("user");
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().println("father name:" + name);
    }
}


 <servlet>
    <servlet-name>getservlet</servlet-name>
    <servlet-class>com.mytext.servlet.getservlet</servlet-class>

  </servlet>
  <!--Servlet的请求路径-->
  <servlet-mapping>
    <servlet-name>getservlet</servlet-name>
    <url-pattern>/getservlet</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>setservlet</servlet-name>
    <servlet-class>com.mytext.servlet.setservlet</servlet-class>

  </servlet>
  <!--Servlet的请求路径-->
  <servlet-mapping>
    <servlet-name>setservlet</servlet-name>
    <url-pattern>/setservlet</url-pattern>
  </servlet-mapping>

测试访问结果;

3、请求转发

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext context = this.getServletContext();
    System.out.println("进入了ServletDemo04");
    //RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); //转发的请求路径
    //requestDispatcher.forward(req,resp); //调用forward实现请求转发;
    context.getRequestDispatcher("/gp").forward(req,resp);
}

4、读取资源文件

Properties

  • 在java目录下新建properties
  • 在resources目录下新建properties

发现:都被打包到了同一个路径下:classes,我们俗称这个路径为classpath:

思路:需要一个文件流;

username=root12312
password=zxczxczxc
public class ServletDemo05 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/kuang/servlet/aa.properties");

        Properties prop = new Properties();
        prop.load(is);
        String user = prop.getProperty("username");
        String pwd = prop.getProperty("password");

        resp.getWriter().print(user+":"+pwd);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

访问测试即可ok;

HttpServletRequest类

转自:https://blog.csdn.net/chenxingxingxing/article/details/123121800


1. HttpServletRequest类

每次只要有请求进入Tomcat服务器,Tomcat服务器会把请求过来的HTTP协议信息解析好封装到Request对象中,然后传递到service(doGet或doPost)方法中供我们使用。然后可以通过HttpServletRequest对象获取所有的请求信息。

servlet代码:


public class HttpServletRequest01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.getRequestURI():获取请求的工程路径
        System.out.println("1.请求资源路径:"+req.getRequestURI());
        //2.getRequestURL():获取请求的绝对路径
        System.out.println("2.请求绝对路径:"+req.getRequestURL());
        //3.getRemoteHost():获取客户端ip
        System.out.println("3.客户端ip:"+req.getRemoteHost());
        //4.getMethod():获取请求的方式(get/post)
        System.out.println("4.请求方式:"+req.getMethod());

        //5.获取请求参数
        String name = req.getParameter("name");
        String sex = req.getParameter("sex");
        String country = req.getParameter("country");
        String []hobby = req.getParameterValues("hobby");

        System.out.println("姓名:"+name);
        System.out.println("性别:"+sex);
        System.out.println("国籍:"+country);
        System.out.println("兴趣爱好:"+ Arrays.asList(hobby));
    }}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="http://localhost:8088/servlet01_war/HttpServletRequest01" method="get">
        姓名:<input type="text" name="name"><br/>
        性别:<input type="radio" name="sex" value="boy">男<input type="radio" name="sex" value="girl">女<br/>
        国籍:<select name="country">
                <option name="country">--请选择国籍--</option>
                <option name="country" selected="selected">中国</option>
                <option name="country">美国</option>
                <option name="country">意大利</option>
                <option name="country">俄罗斯</option>
            </select><br/>
        兴趣爱好:<input type="checkbox" name="hobby" value="java">java
        <input type="checkbox" name="hobby" value="c++">c++
        <input type="checkbox" name="hobby" value="python">python
        <input type="checkbox" name="hobby" value="js">javascript<br/>
        <input type="submit" value="提交">
    </form>
</body>
</html>

2.HttpServletRequest类常用方法

  • (1)getRequestURI():获取请求的工程路径,可以获取你部署的该工程的路径。

这就是我的部署的web工程路径,用getRequestURI()就可以得到了

  • (2)getRequestURL():获取请求的绝对路径

  • (3)getRemoteHost():获取客户端ip

如果客户端用localhost访问,得到的是127.0.0.1

如果用同一局域网不同ip访问,就能得到客户端真实ip

  • (4)getMethod():获取请求的方式(get/post)

请求有get和post,但大多数是get请求

3.HttpServletRequest获取请求参数的方法

网页数据的对应请求参数

得到的对应提交信息

getParameter()是获取单个参数信息,如以上的姓名,性别国籍
而getParameterValues()获取多个参数信息,如以上的兴趣爱好为多个

HttpServletResponse类

1.HttpServletResponse类的简介

HttpServletResponse类是实现响应的一个类,他将将要响应的数据封装到response对象里面,通过特定的方法响应。响应数据的响应行,响应头,响应体分别由setStatus()、setHeader ()、getWriter() 和getOutputStream()几个方法实现。下面着重介绍 getWriter()(响应字符数据) 和getOutputStream()(响应字节数据)方法。前面我们说到Servlet请求的转发,他是在服务器里面跳转资源的方式。这里介绍的重定向也有类似的功能,但是实现的原理有所不同。

2.响应字符数据

通过response对象,将你想要响应到客户端的字符型数据发送到浏览器里面。只用两步就可以实现:

  • 1、先通过response对象获取字符输出流:PrintWriter writer = resp.getWriter();
  • 2、将你想要写的数据通过write()方法响应到客户端:writer.write()。public class HttpServletResponse001 extends HttpServlet {
  @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter writer = resp.getWriter();
        writer.write("hello servlet!!!");
    }}
![9b11449cdd539dc59759c2a5324c7716.png](en-resource://database/3781:0)

需要注意的几点

  • 1.response不仅可以返回纯文本,还可以HTML的各个标签。在返回标签时要注意先把响应响应头的ContentType属性改成text/html格式,不然浏览器会默认把相应的内容当成纯文本解析。public class HttpServletResponse001 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        PrintWriter writer = resp.getWriter();
        writer.write("hello servlet!!!");
        writer.write("<br/>name:<input type = 'text'/>");
    }}
![a8cec17584d19afc29023a71452c73da.png](en-resource://database/3783:0)
  • 2.如果响应的是中文的内容,需要把响应头字体类型改成UTF-8。因为服务器默认的字体类型是ISO-8859-1,如果不修改会导致浏览器解析乱码。
 public class HttpServletResponse001 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//        resp.setContentType("text/html");
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter writer = resp.getWriter();
        writer.write("我的servlet程序!!!");
        writer.write("<br/>姓名:<input type = 'text'/>");
    }}
  • 3.修改响应头等信息需要在通过response对象获取字符输出流就完成,在后面才修改没有效果。
    public class HttpServletResponse001 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//        resp.setContentType("text/html");
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter writer = resp.getWriter();//        resp.setContentType("text/html;charset=utf-8");
        writer.write("aaaaa这是在获取输入流对象之前就修改响应头的。");
        writer.write("<br/>姓名:<input type = 'text'/>");
    }}
  • 4.响应结束后,相应的字符流不需要关闭。他不需要向像Java基础里面字节输入流那种最后需要手动关闭流节省资源。因为在响应结束后,response对象自动销毁,服务器会对其关闭。

3.响应字节数据

通过response对象,将你想要响应到客户端的字符型数据发送到浏览器里面。大致需要3步完成:

1、读取文件(图片,音频,视频等二进制输入输出数据)
2、获取字节输出流
3、通过write()方法响应给客户端

public class HttpServletResponse002 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.读物文件
        FileInputStream inputStream = new FileInputStream("C:\\Users\\cx\\Desktop\\1.png");
        //2.获取字节输出流
        ServletOutputStream outputStream = resp.getOutputStream();
        //3.打印文件
        byte []b = new byte[2048];
        int len = 0;
        while ((len = inputStream.read(b))!=0){
            outputStream.write(b,0,len);
        }
        inputStream.close();
    }}
![7cb0affa8dd3c9975174ca1f86728b52.png](en-resource://database/3789:0)

4.重定向

1.重定向与请求的转发的区别重定向特点;
(1)浏览器地址栏会发生改变,会变成servlet程序1中指定的地址
(2)是两次请求
(3)多个servlet程序不共享request对象内的数据
(4)不可以访问WEB-INF下的资源
(5)可以访问工程以外的其他连接(例如百度、谷歌等网页资源)请求转发的特点(参考我另一篇博客)

(1)浏览器地址栏没有发生变化。
(2)多个servlet程序是同一个请求
(3)多个servlet程序共享一个request对象
(4)可以转发到WEB-INF目录下2.重定向与请求转发的图示区别

2.求情的转发:

重定向:

3.重定向程序的实现
第一种:
@WebServlet("/httpServletResponse003")public class HttpServletResponse003 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.write("servlet001!!!");

    //设置响应状态码(302)
    resp.setStatus(302);
    //设置响应头,告诉新地址在哪
    resp.setHeader(&quot;location&quot;,&quot;http://localhost:8888/servlet_war_exploded/httpServletResponse004&quot;);
}}

第二种:@WebServlet("/httpServletResponse005")public class HttpServletResponse005 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("http://localhost:8888/servlet_war_exploded/httpServletResponse004");
}}
7

两种方法只要访问了第一个servlet程序再回车,浏览器会自动跳到localhost:8888/servlet_war_exploded/httpServletResponse004而且浏览器地址栏会发生变化。与请求不同,虽然是两个页面,但是是同一个地址(即浏览器地址栏不会发生变化),这与上述所说的第一个特点相对应。

Mapping问题

  1. 一个Servlet可以指定一个映射路径
        <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
    

2. 一个Servlet可以指定多个映射路径

```xml
       <servlet-mapping>
           <servlet-name>hello</servlet-name>
           <url-pattern>/hello</url-pattern>
       </servlet-mapping>
       <servlet-mapping>
           <servlet-name>hello</servlet-name>
           <url-pattern>/hello2</url-pattern>
       </servlet-mapping>
       <servlet-mapping>
           <servlet-name>hello</servlet-name>
           <url-pattern>/hello3</url-pattern>
       </servlet-mapping>
       <servlet-mapping>
           <servlet-name>hello</servlet-name>
           <url-pattern>/hello4</url-pattern>
       </servlet-mapping>
       <servlet-mapping>
           <servlet-name>hello</servlet-name>
           <url-pattern>/hello5</url-pattern>
       </servlet-mapping>
   
  1. 一个Servlet可以指定通用映射路径
        <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>/hello/*</url-pattern>
        </servlet-mapping>
    

4. 默认请求路径

   ```xml
       <!--默认请求路径-->
       <servlet-mapping>
           <servlet-name>hello</servlet-name>
           <url-pattern>/*</url-pattern>
       </servlet-mapping>
  1. 指定一些后缀或者前缀等等….
    <!--可以自定义后缀实现请求映射
        注意点,*前面不能加项目映射的路径
        hello/sajdlkajda.qinjiang
        -->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>*.qinjiang</url-pattern>
    </servlet-mapping>
    

6. 优先级问题
   指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求;

   ```xml
   <!--404-->
   <servlet>
       <servlet-name>error</servlet-name>
       <servlet-class>com.kuang.servlet.ErrorServlet</servlet-class>
   </servlet>
   <servlet-mapping>
       <servlet-name>error</servlet-name>
       <url-pattern>/*</url-pattern>
   </servlet-mapping>
   

“缘分让我们相遇乱世以外,命运却让我们危难中相爱”