跳过正文
  1. 文章/
  2. Java/
  3. JavaEE/
  4. JavaWeb/

3、Servlet

·5336 字·11 分钟· loading · loading · ·
Java JavaEE JavaWeb
GradyYoung
作者
GradyYoung
JavaWeb - 点击查看当前系列文章
§ 3、Servlet 「 当前文章 」

Servlet
#

Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容,它是由Sun公司(现在是Oracle公司)开发的,作为Java Servlet API的一部分,包含在Java EE(Enterprise Edition)规范中

狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

servlet-api
#

servlet-api.jar并不包含在我们安装的JDK(Java SE)中,它是Java EE规范的一部分,通常作为Web容器(如Tomcat、Jetty、WebLogic等)的一部分提供。

<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>

如果您想要使用servlet-api.jar来编写servlet应用程序,您可以通过以下途径获取:

  1. 下载并安装一个Java Web容器,如Apache Tomcat。然后在Tomcat安装目录下的lib文件夹中找到servlet-api.jar

  2. 下载Java EE,其中包括Servlet API的实现,并在安装目录下的lib文件夹中找到servlet-api.jar(Oracle已经不提供Java EE的下载了)

servlet-api.jar只是在编译的时候需要,运行的时候使用Web容器内置的即可,所以不必打包进项目依赖,以免发生冲突,使用maven可以声明scopeprovided

注意:由于Java EE被Orcale捐献给Apache,并且在2021年更名为Jakarta EE,所以从 Jakarta EE 9 开始对应用的Servlet类名是:jakarta.servlet.Servlet,而之前是javax.servlet.Servlet,所以如果之前的项目还在使用javax.servlet.Servlet,那么你的项目无法直接部署到Tomcat10+版本上。你只能部署到Tomcat9-版本上。在Tomcat9以及Tomcat9之前的版本中还是能够识别javax.servlet这个包。

JavaWeb三大组件
#

Servlet程序、Filter过滤器、Listener监听器

Servlet的继承关系
#

  • javax.servlet.Servlet(接口):是所有Servlet类的父接口,它定义了Servlet必须实现的方法,包括init()service()destroy()
  • javax.servlet.ServletConfig(接口):代表着Servlet的配置信息,例如初始化参数、Servlet名称等。在Servlet的生命周期中,容器会调用其init()方法,并传递一个ServletConfig对象作为参数。
    • javax.servlet.GenericServlet(抽象类):实现了Servlet、ServletConfig接口。它提供了一些通用的方法,例如getServletConfig()getInitParameter()等。通过继承GenericServlet可以简化自定义Servlet的编程,因为只需要实现其中的service()方法即可。
      • javax.servlet.http.HttpServlet(类):是GenericServlet的子类,并实现了HttpServletRequest和HttpServletResponse接口。它针对HTTP协议提供了更加方便的处理方式,例如doGet()doPost()等方法。通过继承HttpServlet,开发人员可以方便地处理HTTP请求和响应

Servlet的常用方法
#

  • void init(ServletConfig config):由Servlet容器调用,用于初始化Servlet对象
  • void service(ServletRequest req,ServletResponse res):由Servlet容器调用,用于处理客户端请求并响应
  • void destory():由Servlet容器调用,释放Servlet对象所使用的资源
  • ServletConfig getServletConfig():返回ServletConfig对象
  • String getServletInfo():返回关于Servlet的信息,比如作者、版本、版权

service()、doGet()、doPost()的区别
#

service()doGet()doPost()均是Java Servlet中的方法,但是它们在处理请求时有所不同。

  • service()方法是Servlet执行请求的主要方法,当客户端发出请求时,Servlet容器将调用service()方法来处理请求并生成响应。这个方法根据具体的HTTP请求类型(GET、POST、PUT、DELETE等)来决定调用哪个子方法进行处理。

  • doGet()方法处理HTTP GET请求,通常用于获取信息或数据,并且参数会显示在URL中,它不会修改服务器上的任何数据。

  • doPost()方法处理HTTP POST请求,通常用于向服务器提交数据,并且请求参数在请求体内。它可以对服务器上的数据进行更改。

总结:service()方法是Servlet处理请求的主要方法,而doGet()doPost()方法是service()方法的具体实现,用于处理GET和POST请求。同时出现的情况下,service()优先。

使用步骤
#

1、继承HttpServlet
#

三种方式
#

1、定义一个类,去实现javax.servlet.Servlet接口

2、定义一个类,去继承javax.servlet.GenericServlet抽象类

3、定义一个类,去继承javax.servlet.http.HttpServlet抽象类(推荐

//注意:如果一个Servlet类,三个方法一个都没有重写,请求时会报405
class MyServlet extends HttpServlet {
    doGet() {}
    doPost() {}
    service() {}
}

2、配置Servlet程序访问地址
#

在web.xml文件中
#

<servlet>
    <servlet-name>名称1</servlet-name>
    <servlet-class>Servlet全类名(包+类)</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>名称1</servlet-name>
    <url-pattern>/虚拟路径</url-pattern>
</servlet-mapping>

使用注解配置,不需要写xml文件
#

  • @WebServlet:属于类级别的注解,标注在继承了 HttpServlet 的类之上。常用的写法是将 Servlet 的相对请求路径(即 value)直接写在注解内
  • 属性
    • name-String:指定Servlet的名称,如果没有指定,默认是Servlet类的全类名
    • value-String[]:完全等价于urlPatterns属性,二者不能同时指定
    • urlPatterns-String[]:指定一组Servlet的匹配路径
    • loadOnStartup-int:指定Servlet的加载顺序
    • initParams-WebInitParam:指定Servlet的启动参数
    • asyncSupported-boolean:Servlet是否支持异步操作模式
    • description-String:Servlet的描述
    • displayName-String:Servlet的显示名

在继承了Servlet类上方,添加注解,用于配置此Servlet的虚拟地址,三种方法

@WebServlet(value="/servletDemo1")
public MyServle extends HttpServlet{}
@WebServlet(value={"/servletDemo1","/servletDemo2"})
public MyServle extends HttpServlet{}
@WebServlet({"/servletDemo1","/servletDemo2"})
public MyServle extends HttpServlet{}
@WebServlet(urlPatterns="/servletDemo1")
public MyServle extends HttpServlet{}

3、访问
#

http://localhost:8080/项目虚拟目录/servlet虚拟路径

域对象
#

在JavaWeb中,Servlet中三大域对象分别是request,session,ServletContext,其只要是用来存放共享数据的。

之所以他们是域对象,原因是他们都内置了Map集合,都有setAttributegetAttributeremoveAttribute方法用于操作值。

只要是域对象,都有这3个方法,可以实现数据共享,都是以key-value方式存放数据,key必须是String类型,value是Object类型

作用域
#

名称 对象类型 作用域 说明
application ServletContext 在整个应用程序中有效 通过request.getServletContext()方法获取,可以在整个应用范围内共享数据
session HttpSession 在当前会话中有效 通过request.getSession()获取,会话代表同一浏览器向服务器的多次请求和响应
request HttpServletRequest 在当前请求中有效 在一次请求的范围内,可以共享资源
page PageContext JSP的请求到响应中有效 作用范围是当前用户请求的JSP页面渲染时,一旦渲染结束响应即失效

Servlet生命周期
#

生命周期方法
#

servlet的生命周期是由三个方法体现的,称为生命周期方法:

  • init():初始化方法,只有在首次访问时调用
  • service():执行方法,每次访问,都会调用
  • destroy():销毁方法,只有Servlet销毁时调用(WEB工程停止时)

生命周期
#

1、当我们第一次访问servlet的时候,会创建servlet对象(调用构造器),调用servlet的init(),然后调用service()

2、当我们再一次访问servlet的时候,就不会调用init(),只会调用service()

3、当我们正常关闭服务器的时候,会调用servlet的destroy()

HttpServletRequest
#

获取请求行的方法
#

  • String getMethod():获取请求方式
  • String getContextPath():获取项目的虚拟目录
  • String getServletPath():获取servlet的虚拟路径
  • String getQueryString():获取get请求的请求参数
  • String getRequestURI():获取请求的URI(资源路径)
  • StringBuffer getRequestURL():获取请求的URL(绝对路径)
  • String getProtocol():获取协议/版本号
  • String getRemoteAddr():获取客户端的IP地址

获取路径参数
#

path?k1=v1&k2=v2获取这种形式的参数

  • String getParameter(String name):根据请求参数的名称获取值
  • Enumeration<String> getParameterNames():获取所有的请求参数的名称
  • String[] getParameterValues(String name):根据请求参数的名称获取值(多个)
  • Map<String,String[]> getParameterMap():将所有请求参数的名称和值都封装到了map对象

获取请求头的方法
#

  • String getHeader(String name):根据请求头的名称获取请求头的值
  • Enumeration<String> getHeaderNames():获取所有的请求头的名称
  • Enumeration<String> getHeaders(String name):根据请求头的名称获取多个请求头的值
  • int getIntHeader(String name):根据请求头的名称获取请求头的值(请求头值为int类型的时候)

获取请求体的方法
#

  • BufferedReader getReader():获取Reader
  • ServletInputStream getInputStream():获取InputStream

HttpServletResponse
#

设置响应状态码
#

  • void setStatus(int sc)

设置响应头
#

  • void setHeader(String name, String value)

设置响应体
#

打印流:PrintWriter getWriter()

字节流:ServletOutputStream getOutputStream()

解决中文乱码
#

//将tomcat写出的编码由ISO-8859-1变成UTF-8
response.setCharacterEncoding("utf-8"); 
//上述方法,只会将服务器写出的编码为utf-8,所以,需要服务器告诉浏览器使用utf-8来打开

//推荐!!!
response.setHeader("Content-Type", "text/html;charset=utf-8");
//或
response.setContentType("text/html;charset=utf-8");

响应Html文件
#

可以通过Response实例的getOutPutStream()向浏览器进行响应,例如响应html文件

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //获取请求体中的参数,和数据库进比较
    String name = req.getParameter("name");
    String psw = req.getParameter("psw");
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    UsersDao usersDao = sqlSession.getMapper(UsersDao.class);
    Users byNameAndPsw = usersDao.findByNameAndPsw(name, psw);
    //获取当前项目路径
    String contextPath = req.getServletContext().getRealPath("/");
    //登录成功、失败不同的html页面
    String path = null;
    if (byNameAndPsw == null){
        path = "fail.html";
    }else {
        path = "success.html";
    }
    //获得对应页面的输入流
    InputStream inputStream = new FileInputStream(contextPath  + path);
    //获得输出流
    ServletOutputStream outputStream = resp.getOutputStream();
    //将输入流的内容,写出到输出流
    byte[] bytes = new byte[1024];
    int len = 0;
    while ( (len = inputStream.read(bytes)) >0 ){
        outputStream.write(bytes,0,len);
    }
    //关闭资源
    outputStream.flush();
    outputStream.close();
    inputStream.close();
}

同一Servlet处理不同的功能
#

1、通过注解或xml文件设置该url的多个URL,例如

@WebServlet(value={"goodsType/add","goodsType/change"})

2、通过request对象的getRequestURI()方法,获取该请求的URI(如shop/goodsType/add),然后通过字符串的分割,获取具体的请求

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //解析请求的方法
    String uri = req.getRequestURI();
    String resUri = uri.substring(uri.lastIndexOf("/") + 1);
    //根据请求类型,使用反射获取并执行对应的方法
    Method method = null;
    try {
        method = this.getClass().getMethod(resUri, HttpServletRequest.class, HttpServletResponse.class);
        method.setAccessible(true);
        method.invoke(this,req,resp);
    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
        e.printStackTrace();
    }
}
public void add(HttpServletRequest req, HttpServletResponse resp){
    
}
public void change(HttpServletRequest req, HttpServletResponse resp){
    
}

转发和重定向
#

无论转发还是重定向,始终都是请求一次,响应一次

转发
#

调用服务端的资源(静态、动态)

request.getRequestDispatcher("/path").forward(request,response);
  • 属于一次请求

  • 响应头没有Location

  • 可以在request范围内共享数据

  • 只可以使用本应用的资源

  • 地址栏不会发生变化,就是请求时的地址

重定向
#

重新定位一下,通过响应头中的Location,告诉浏览器,需要重新请求一次

response.sendRedirect();
  • 两次请求

  • 第一次的请求中,响应头有Location

  • 无法在request范围内共享数据,可以通过session或者application(ServletContext)达到数据的共享

  • 可以调用应用之外的资源

  • 地址栏会发生变化,就是最后请求地址

JavaWeb - 点击查看当前系列文章
§ 3、Servlet 「 当前文章 」