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的继承关系

Servlet的常用方法

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

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

总结: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文件

在继承了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的生命周期是由三个方法体现的,称为生命周期方法:

生命周期

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

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

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

HttpServletRequest

获取请求行的方法

获取路径参数

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

获取请求头的方法

获取请求体的方法

HttpServletResponse

设置响应状态码

设置响应头

设置响应体

打印流: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,告诉浏览器,需要重新请求一次

response.sendRedirect();