代码仓库:自学尚硅谷JavaWeb2022版(JavaWeb-atguigu)

Servlet

继承关系

HttpServlet–>GenericServlet–>Servlet

service方法

当请求来到时,service方法会自动响应(实际上是Web容器Tomcat调用),在HttpServlet中会分析请求的方式(get、post、head、delete等),然后再决定调用哪个方法(doGetdoPostdoHeaddoDelete),这些方法默认都是405响应,需要程序员继承HttpServlet并重写这些方法。

服务配置

web.xml中配置,或者使用注解@WebServlet配置。

1
2
3
4
5
6
7
8
9
<!-- 一个servlet对应多个servlet-mapping -->
<servlet>
<servlet-name>Servlet名称</servlet-name>
<servlet-class>Servlet类名</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet名称</servlet-name>
<url-pattern>服务访问路径</url-pattern>
</servlet-mapping>

生命周期

Servlet初始化、提供服务、销毁,对应三个方法initservicedestroy。默认情况下:第一次接收请求时,Servlet会进行实例化(调用构造方法)、初始化(调用init)、再提供服务(调用service);从第二次请求开始,都只提供服务;当Web容器关闭,其中的所有Servlet实例都会被销毁。我们可以通过<load-on-startup>设置各个Servlet的启动顺序,数字越小越早启动。
通过案例可以发现,一个Servlet实例只会被一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.joe.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* @author Joe Zhu
*/
public class LifeCycle extends HttpServlet {
@Override
public void init() {
System.out.println("正在初始化...");
}

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("正在服务...");
}

@Override
public void destroy() {
System.out.println("正在销毁...");
}
}

会话跟踪

Http无状态,即服务器无法判断两次请求是否是同一个客户端发送的,我们需要使用会话跟踪技术来解决无状态的问题。

当第一次发送请求时,服务器给客户端分配一个“Session ID”,接下来的每次请求客服端都主动告知服务器自己的“Session ID”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.joe.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* @author Joe Zhu
*/
public class SessionTracking extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) {
System.out.println(req.getSession().getId());
}
}

第一次访问服务网址localhost:8080/Servlet/SessionTracking,响应头中包含了设置的“Session ID”。

1
2
3
4
5
Connection: keep-alive
Content-Length: 0
Date: Thu, 17 Feb 2022 09:10:09 GMT
Keep-Alive: timeout=20
Set-Cookie: JSESSIONID=E6D29D178C05088E6EA948C7578F8A4C; Path=/Servlet; HttpOnly

接下来再发起请求,请求头中自带“Session ID”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: max-age=0
Connection: keep-alive
Cookie: JSESSIONID=E6D29D178C05088E6EA948C7578F8A4C
Host: localhost:8080
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Microsoft Edge";v="98"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36 Edg/98.0.1108.50

保存作用域

  • page(页面级别)

  • request(一次请求响应范围)

    用户向servlet1发送请求后,又再次向servlet2发送请求,输出null

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.joe.servlet;

    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    /**
    * @author Joe Zhu
    */
    @WebServlet("/servlet1")
    public class Servlet1 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    req.setAttribute("username","joe");
    resp.sendRedirect("servlet2");
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.joe.servlet;

    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    /**
    * @author Joe Zhu
    */
    @WebServlet("/servlet2")
    public class Servlet2 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println(req.getAttribute("username"));
    }
    }

    Servlet1的代码改动如下,进行请求内转发,输出joe

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.joe.servlet;

    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    /**
    * @author Joe Zhu
    */
    @WebServlet("/servlet1")
    public class Servlet1 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    req.setAttribute("username", "joe");
    req.getRequestDispatcher("servlet2").forward(req, resp);
    }
    }
  • session(一次会话范围有效)

    输出joe

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.joe.servlet;

    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    /**
    * @author Joe Zhu
    */
    @WebServlet("/servlet1")
    public class Servlet1 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    req.getSession().setAttribute("username", "joe");
    resp.sendRedirect("servlet2");
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.joe.servlet;

    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    /**
    * @author Joe Zhu
    */
    @WebServlet("/servlet2")
    public class Servlet2 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println(req.getSession().getAttribute("username"));
    }
    }
  • application(一次应用程序范围有效)

过滤器

设置过滤器需要,实现接口javax.servlet.Filter。过滤器用于拦截请求,需要用注解@WebFilter或XML配置;如果采取注解的方式配置,那么过滤器链的拦截顺序是按全类名的字典序排序;如果采取XML方式进行配置,那么按照配置的先后顺序拦截。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.joe.servlet;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
* @author Joe Zhu
*/
@WebFilter("/hello")
public class HelloFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("请求");
// 放行
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("应答");
}
}

监听器

  • ServletContextListener监听ServletContext对象的创建和销毁的过程。
  • HttpSessionListener监听HttpSession对象的创建和销毁的过程。
  • ServletRequestListener监听ServletRequest对象的创建和销毁的过程。
  • ServletContextAttributeListener监听ServletContext中属性的创建、修改和销毁。
  • HttpSessionAttributeListener监听HttpSession中属性的创建、修改和销毁。
  • ServletRequestAttributeListener监听ServletRequest中属性的创建、修改和销毁。
  • HttpSessionBindingListener监听某个对象在Session域中的创建与移除。
  • HttpSessionActivationListener监听某个对象在Session中的序列化与反序列化。

MVC

  • Model(模型)

    • POJO (Plain Ordinary Java Object,简单的Java对象)或 VO(Value Object,值对象)
    • DAO(Data Access Object,数据访问对象)
    • BO(Business Object,业务对象)
  • View(视图)用于做数据展示以及和用户交互。

  • Controller(控制器)接收客服端请求,具体业务功能借助Model组件完成。

书城项目

ER

踩坑记录

遇到的问题 解决方案
Maven已经导入mysql-connector-java-8.0.28.jar,用JDBC连数据库时,Class.forName("com.mysql.cj.jdbc.Driver")报异常。 构建Web项目时,用到的jar包放在 WEB-INF/lib 目录下,这一点在Tomcat的webapps中可以看到。在“项目结构-工件-输出”设置更方便。 其它 ClassNotFind 等异常也类似。
重装了IDEA后打开web项目,web目录没有蓝点,web项目无法正常构建。 在项目结构中选中对应模块,设置web目录。IDEA webapp文件夹没有蓝色小圆点的解决方案_无发可脱-CSDN博客
表单中,用Thymleaf标签th:action无法正常解析。 使用原始的action
maven项目编译后没有xml文件。 在pom.xml中增加配置。Spring编译后没有xml配置文件解决方法_林老头的博客-CSDN博客
UTF-8 序列的字节 3 无效。 解决3 字节的 UTF-8 序列的字节 3 无效_lzupb的博客-CSDN博客
method.getParameters()获取不到实际形参名。 编译时添加-parameters参数。jdk1.8获取目标方法参数的名称_jxz999000的博客-CSDN博客