系统架构

一、定义与核心差异

  1. C/S架构(Client/Server,客户端-服务器架构)
    • 核心逻辑:客户端需安装专用软件(如桌面应用),负责业务逻辑处理;服务器端负责数据存储和核心计算。
    • 典型场景:早期QQ、大型游戏客户端、企业内部ER
    • P系统。
  2. B/S架构(Browser/Server,浏览器-服务器架构)
    • 核心逻辑:用户通过浏览器访问(如网页应用),客户端仅负责展示,业务逻辑完全由服务器端处理。
    • 典型场景:Gmail、淘宝网、SaaS服务(如钉钉)。

二、优缺点对比

维度 C/S架构 B/S架构
部署与维护 需安装客户端,跨平台性差;升级需逐台更新客户端。 无需安装,跨平台性强;仅需更新服务器端代码。
性能与响应 客户端分担计算任务,响应快,适合高频交互(如实时游戏、金融交易)。 依赖网络传输,服务器压力大;浏览器渲染能力有限,复杂操作体验稍逊。
安全性 客户端封闭性高,数据加密链路可控,安全性较强。 开放性强,易受跨站脚本(XSS)、SQL注入攻击,需额外安全防护。
开发成本 需开发多平台客户端(Windows/macOS/移动端),适配成本高。 基于HTML/CSS/JS标准化开发,适配成本低。

三、适用场景选择

  1. 优先选择C/S架构
    • 高性能需求:如视频剪辑软件(Adobe Premiere)、3D建模工具(AutoCAD)。
    • 离线场景:工业控制系统、医疗设备终端(需本地化数据处理)。
    • 高安全性场景:银行柜员系统、政府机密数据处理。
  2. 优先选择B/S架构
    • 跨平台访问:企业OA系统、在线协作工具(如飞书文档)。
    • 快速迭代需求:电商促销页面、轻量级信息查询系统(如12306余票查询)。
    • 移动端优先:PWA(渐进式网页应用)替代部分原生APP场景(如Twitter Lite)

B/S架构的系统通信原理

  • WEB系统的访问过程
    • 第一步:打开浏览器
    • 第二步:找到地址栏
    • 第三步:输入一个合法的网址
    • 第四步:回车
    • 第五步:在浏览器上会展示响应的结果。
  • 关于域名:
  • IP地址
    • 计算机在网络当中的一个身份证号。在同一个网络当中,IP地址是唯一的。
    • A计算机要想和B计算机通信,首先你需要知道B计算机的IP地址,有了IP地址才能建立连接。
  • 端口号
    • 一个端口代表一个软件(一个端口代表一个应用,一个端口仅代表一个服务)。
    • 一个计算机当中有很多软件,每一个软件启动之后都有一个端口号。
    • 在同一个计算机上,端口号具有唯一性。
  • WEB系统通信步骤:
    • 第一步:用户输入网址(URL)
    • 第二步:域名解析器进行域名解析:http://110.242.68.3:80/index.html
    • 第三步:浏览器软件在网络中搜索110.242.68.3这一台主机,直到找到这台主机。
    • 第四步:定位110.242.68.3这台主机上的服务器软件,因为是80端口,可以很轻松的定位到80端口对应的服务器软件。
    • 第五步:80端口对应的服务器软件得知浏览器想要的资源名是:index.html
    • 第六步:服务器软件找到index.html文件,并且将index.html文件中的内容直接输出响应到浏览器上。
    • 第七步:浏览器接收到来自服务器的代码(HTML CSS JS)
    • 第八步:浏览器渲染,执行HTML CSS JS代码,展示效果。
  • URL
  • 请求、响应
    • 请求和响应实际上说的是数据的流向不同。
    • 从Browser端发送数据到Server端,我们称为请求。英语单词:request
    • 从Server端向浏览器Browser端发送数据,我们称为响应。英语单词:response
    • B --> S (请求request)
    • S --> B (响应response)

WEB服务器软件

  • WEB服务器软件都有哪些呢?

    • Tomcat(WEB服务器)
    • jetty(WEB服务器)
    • JBOSS(应用服务器)
    • WebLogic(应用服务器)
    • WebSphere(应用服务器)
  • 应用服务器和WEB服务器的关系?

    • 应用服务器实现了JavaEE的所有规范。
    • WEB服务器只实现了JavaEE中的Servlet + JSP两个核心的规范。
    • 应用服务器是包含WEB服务器的。
  • Tomcat下载

  • Tomcat服务器要想运行,需要先有JRE,所以要先安装JDK,配置java运行环境。

    • JAVA_HOME=C:\Program Files\Java\jdk-17.0.1
    • PATH=%JAVA_HOME%\bin
    • 目前JAVA_HOME没有配置,思考一个问题,这样行不行呢?目前只运行java程序是没问题的。真的没问题吗?
  • Tomcat服务器的安装:

    • 绿色版本的安装很简单,直接zip包解压即可。解压就是安装。
    • 启动Tomcat
      • bin目录下有一个文件:startup.bat,通过它可以启动Tomcat服务器。
        • xxx.bat文件是个什么文件?bat文件是windows操作系统专用的,bat文件是批处理文件,这种文件中可以编写大量的windows的dos命令,然后执行bat文件就相当于批量的执行dos命令。
        • startup.sh,这个文件在windows当中无法执行,在Linux环境当中可以使用。在Linux环境下能够执行的是shell命令,大量的shell命令编写在shell文件当中,然后执行这个shell文件可以批量的执行shell命令。
        • tomcat服务器提供了bat和sh文件。
        • 分析startup.bat文件得出,执行这个命令,实际上最后是执行:catalina.bat文件。
        • catalina.bat文件中有这样一行配置:MAINCLASS=org.apache.catalina.startup.Bootstrap (这个类就是main方法所在的类。)
        • tomcat服务器就是Java语言写的,既然是java语言写的,那么启动Tomcat服务器就是执行main方法。
      • 打开dos命令窗口,在dos命令窗口中输入startup.bat来启动tomcat服务器。
      • 启动Tomcat服务器只配置path对应的bin目录是不行的。有两个环境变量需要配置:
        • JAVA_HOME=JDK的根
        • CATALINA_HOME=Tomcat服务器的根
  • 关于Tomcat服务器的目录

    • bin : 这个目录是Tomcat服务器的命令文件存放的目录,比如:启动Tomcat,关闭Tomcat等。
    • conf: 这个目录是Tomcat服务器的配置文件存放目录。(server.xml文件中可以配置端口号,默认Tomcat端口是8080)
    • lib :这个目录是Tomcat服务器的核心程序目录,因为Tomcat服务器是Java语言编写的,这里的jar包里面都是class文件。
    • logs: Tomcat服务器的日志目录,Tomcat服务器启动等信息都会在这个目录下生成日志文件。
    • temp:Tomcat服务器的临时目录。存储临时文件。
    • webapps:这个目录当中就是用来存放大量的webapp(web application:web应用)
    • work:这个目录是用来存放JSP文件翻译之后的java文件以及编译之后的class文件。
  • 配置Tomcat服务器需要哪些环境变量?

    • JAVA_HOME=JDK的根
    • CATALINA_HOME=Tomcat服务器的根
    • PATH=%JAVA_HOME%\bin;%CATALINA_HOME%\bin
  • 启动Tomcat: startup

  • 关闭Tomcat: shutdown.bat

  • 怎么测试Tomcat服务器有没有启动成功呢?

    • 打开浏览器,在浏览器的地址栏上输入URL即可:
      • ip地址:端口号
      • 本机的IP地址是:127.0.0.1,或者是localhost

JavaEE的核心规范

(现更名为Jakarta EE)

一、Web层规范

  • Servlet

    • 用于处理HTTP请求和生成动态Web内容,是JavaEE Web应用的底层核心
  • JSP(JavaServer Pages)

    • 动态网页技术标准,通过HTML嵌入Java代码简化页面开发
  • JSF(JavaServer Faces)

    • 提供组件化Web界面开发框架,支持可重用UI组件
  • WebSocket

    • 实现浏览器与服务器间的全双工实时通信

二、业务层规范

  • EJB(Enterprise JavaBeans)

    • 分布式组件模型,支持事务管理、安全控制和企业级分布式计算
  • 衍生规范:JPA(Java Persistence API)从EJB 3.0分离,专注对象关系映射

    • CDI(Contexts and Dependency Injection)
    • 上下文依赖注入框架,实现组件解耦
  • JTA(Java Transaction API)

    • 跨数据库和消息队列的分布式事务管理

三、企业信息层规范

  • JDBC(Java Database Connectivity)

    • 数据库访问标准API,支持多种关系型数据库
  • JMS(Java Message Service)

    • 异步消息通信标准,支持消息队列和发布订阅模式
  • JCA(Java Connector Architecture)

    • 企业系统集成框架,连接ERP、CRM等遗留系统

四、通用平台规范

  • JNDI(Java Naming and Directory Interface)

    • 统一资源定位服务,通过名称查找数据源、消息服务等资源
  • Bean Validation

    • 数据校验框架,支持注解式字段验证
  • JavaMail

    • 邮件服务API,支持SMTP、POP3等协议

Servlet

什么是Servlet

1、Servlet 是 JavaEE 规范之一。规范就是接口

2、Servlet 就 JavaWeb 三大组件之一。三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器。

3、Servlet 是运行在服务器上的一个 java 小程序,它可以接收客户端发送过来的请求,并响应数据给客户端。

Servlet对象的生命周期

  1. 执行 Servlet 构造器方法

  2. 执行 init 初始化方法

    第一、二步,是在第一次访问,的时候创建 Servlet 程序会调用。

  3. 执行 service 方法

    第三步,每次访问都会调用。

  4. 执行 destroy 销毁方法

    第四步,在 web 工程停止的时候调用

Servlet对象的创建,对象方法的调用,对象最终的销毁,Javaweb程序员是无权干预的。Servlet对象的生命周期是由Tomcat服务器(WEB Server)全权负责的。

我们自己new的Servlet对象是不受WEB容器管理的。

  • WEB容器创建的Servlet对象,这些Servlet对象都会被放到一个集合当中(HashMap),只有放到这个HashMap集合中的Servlet才能够被WEB容器管理,自己new的Servlet对象不会被WEB容器管理。(自己new的Servlet对象不在容器当中)

  • web容器底层应该有一个HashMap这样的集合,在这个集合当中存储了Servlet对象和请求路径之间的关系

image-20250617220509930

  • 怎么让服务器启动的时候创建Servlet对象呢?

    • 在servlet标签中添加子标签,在该子标签中填写整数,越小的整数优先级越高。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <servlet>
    <servlet-name>aservlet</servlet-name>
    <servlet-class>com.bjpowernode.javaweb.servlet.AServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>aservlet</servlet-name>
    <url-pattern>/a</url-pattern>
    </servlet-mapping>
  • Servlet对象生命周期

    • 默认情况下服务器启动的时候AServlet对象并没有被实例化

    • 用户发送第一次请求的时候,控制台输出了以下内容:

      AServlet无参数构造方法执行了
      AServlet's init method execute!
      AServlet's service method execute!
      
    • 根据以上输出内容得出结论:

      • 用户在发送第一次请求的时候Servlet对象被实例化(AServlet的构造方法被执行了。并且执行的是无参数构造方法。)
      • AServlet对象被创建出来之后,Tomcat服务器马上调用了AServlet对象的init方法。
      • 用户发送第一次请求的时候,init方法执行之后,Tomcat服务器马上调用AServlet对象的service方法。
    • 用户继续发送第二次请求,控制台输出了以下内容:

      AServlet's service method execute!    
      
    • 根据以上输出结果得知,用户在发送第二次,或者第三次,或者第四次请求的时候,Servlet对象并没有新建,还是使用之前创建好的Servlet对象,直接调用该Servlet对象的service方法,这说明:

      • 第一:Servlet对象是单例的(单实例的。但是要注意:Servlet对象是单实例的,但是Servlet类并不符合单例模式。我们称之为假单例。之所以单例是因为Servlet对象的创建我们javaweb程序员管不着,这个对象的创建只能是Tomcat来说了算,Tomcat只创建了一个,所以导致了单例,但是属于假单例。)
      • 第二:无参数构造方法、init方法只在第一次用户发送请求的时候执行。也就是说无参数构造方法只执行一次。init方法也只被Tomcat服务器调用一次。
      • 第三:只要用户发送一次请求:service方法必然会被Tomcat服务器调用一次。发送100次请求,service方法会被调用100次。
    • 关闭服务器的时候,控制台输出了以下内容:

      AServlet's destroy method execute!    
      
    • 通过以上输出内容,可以得出以下结论:

      • Servlet的destroy方法只被Tomcat服务器调用一次。
      • destroy方法是在什么时候被调用的?
        • 在服务器关闭的时候。
        • 因为服务器关闭的时候要销毁AServlet对象的内存。
        • 服务器在销毁AServlet对象内存之前,Tomcat服务器会自动调用AServlet对象的destroy方法。
    • destroy方法调用的时候,AServlet对象还在,没有被销毁。destroy方法执行结束之后,AServlet对象的内存才会被Tomcat释放。

    • 当我们Servlet类中编写一个有参数的构造方法,如果没有手动编写无参数构造方法会出现什么问题?

      • 报错了:500错误。
      • 注意:500是一个HTTP协议的错误状态码。
      • 500一般情况下是因为服务器端的Java程序出现了异常。(服务器端的错误都是500错误:服务器内部错误。)
      • 如果没有无参数的构造方法,会导致出现500错误,无法实例化Servlet对象。
      • 所以,一定要注意:在Servlet开发当中,不建议程序员来定义构造方法,因为定义不当,一不小心就会导致无法实例化Servlet对象。
    • 思考:Servlet的无参数构造方法是在对象第一次创建的时候执行,并且只执行一次。init方法也是在对象第一次创建的时候执行,并且只执行一次。那么这个无参数构造方法可以代替掉init方法吗?

      • 不能。Servlet规范中有要求,作为javaweb程序员,编写Servlet类的时候,不建议手动编写构造方法,因为编写构造方法,很容易让无参数构造方法消失,这个操作可能会导致Servlet对象无法实例化。所以init方法是有存在的必要的。

GenericServlet

  • 我们编写一个Servlet类直接实现Servlet接口有什么缺点?

    • 我们只需要service方法,其他方法大部分情况下是不需要使用的。
  • 适配器设计模式Adapter

  • 编写一个GenericServlet类,这个类是一个抽象类,其中有一个抽象方法service。

    • GenericServlet实现Servlet接口。
    • GenericServlet是一个适配器。
    • 以后编写的所有Servlet类继承GenericServlet,重写service方法即可。
  • 思考:GenericServlet类是否需要改造一下?怎么改造?更利于子类程序的编写?

    • 思考第一个问题:我提供了一个GenericServlet之后,init方法还会执行吗?

      • 还会执行。会执行GenericServlet类中的init方法。
    • 思考第二个问题:init方法是谁调用的?

      • Tomcat服务器调用的。
    • 思考第三个问题:init方法中的ServletConfig对象是谁创建的?是谁传过来的?

      • 都是Tomcat服务器。
      • Tomcat服务器先创建了ServletConfig对象,然后调用init方法,将ServletConfig对象传给了init方法。
    • 思考一下Tomcat服务器伪代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class Tomcat {
    public static void main(String[] args){
    // .....
    // Tomcat服务器伪代码
    // 创建LoginServlet对象(通过反射机制,调用无参数构造方法来实例化LoginServlet对象)
    Class clazz = Class.forName("com.bjpowernode.javaweb.servlet.LoginServlet");
    Object obj = clazz.newInstance();

    // 向下转型
    Servlet servlet = (Servlet)obj;

    // 创建ServletConfig对象
    // Tomcat服务器负责将ServletConfig对象实例化出来。
    // 多态(Tomcat服务器完全实现了Servlet规范)
    ServletConfig servletConfig = new org.apache.catalina.core.StandardWrapperFacade();

    // 调用Servlet的init方法
    servlet.init(servletConfig);

    // 调用Servlet的service方法
    // ....

    }
    }

ServletConfig

ServletConfig 类从类名上来看,就知道是 Servlet 程序的配置信息类。

Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建,我们负责使用。

Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象

ServletConfig对象中封装了标签中的配置信息。(web.xml文件中servlet的配置信息)

ServletConfig接口常用方法

1
2
3
4
5
6
7
8
9
10
public interface ServletConfig {

public String getServletName(); // 获取Servlet的name

public ServletContext getServletContext(); // 获取ServletContext对象

public String getInitParameter(String name); // 通过初始化参数的name获取value

public Enumeration<String> getInitParameterNames(); // 获取所有的初始化参数的name
}

以上方法在Servlet类当中,都可以使用this去调用。因为GenericServlet实现了ServletConfig接口。

ServletContext

  1. ServletContext 是一个接口,它被称为Servlet上下文对象

  2. 一个 web 工程,只有一个 ServletContext 对象实例

  3. ServletContext 对象是一个域对象。

  4. ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁

  5. 一个ServletContext对象通常对应的是一个web.xml文件

  6. 只要在同一个webapp当中,所有的Servlet对象都是共享同一个ServletContext对象的

Tomcat服务器对ServletContext接口进行了实现。ServletContext对象的创建也是Tomcat服务器来完成的。启动webapp的时候创建的。

ServletContext接口中有哪些常用的方法?

1
2
public String getInitParameter(String name); // 通过初始化参数的name获取value
public Enumeration<String> getInitParameterNames(); // 获取所有的初始化参数的name
1
2
3
4
5
6
7
8
9
10
11
<!--以上两个方法是ServletContext对象的方法,方法获取以下的配置信息-->
<context-param>
<param-name>pageSize</param-name>
<param-value>10</param-value>
</context-param>
<context-param>
<param-name>startIndex</param-name>
<param-value>0</param-value>
</context-param>
<!--注意:以上的配置信息属于应用级的配置信息,一般一个项目中共享的配置信息会放到以上的标签当中。-->
<!--如果你的配置信息只是想给某一个servlet作为参考,那么你配置到servlet标签当中即可,使用ServletConfig对象来获取。-->
1
2
// 获取应用的根路径
public String getContextPath();
1
2
// 获取文件的绝对路径(真实路径)
public String getRealPath(String path);
1
2
3
4
5
6
7
8
9
10
// 通过ServletContext对象也是可以记录日志的
public void log(String message);
public void log(String message, Throwable t);
// 这些日志信息记录到哪里了?
// localhost.2021-11-05.log

// Tomcat服务器的logs目录下都有哪些日志文件?
//catalina.2021-11-05.log 服务器端的java程序运行的控制台信息。
//localhost.2021-11-05.log ServletContext对象的log方法记录的日志信息存储到这个文件中。
//localhost_access_log.2021-11-05.txt 访问日志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ServletContext对象还有另一个名字:应用域(后面还有其他域,例如:请求域、会话域)

// 如果所有的用户共享一份数据,并且这个数据很少的被修改,并且这个数据量很少,可以将这些数据放到ServletContext这个应用域中

// 为什么是所有用户共享的数据? 不是共享的没有意义。因为ServletContext这个对象只有一个。只有共享的数据放进去才有意义。

// 为什么数据量要小? 因为数据量比较大的话,太占用堆内存,并且这个对象的生命周期比较长,服务器关闭的时候,这个对象才会被销毁。大数据量会影响服务器的性能。占用内存较小的数据量可以考虑放进去。

// 为什么这些共享数据很少的修改,或者说几乎不修改?
// 所有用户共享的数据,如果涉及到修改操作,必然会存在线程并发所带来的安全问题。所以放在ServletContext对象中的数据一般都是只读的。

// 数据量小、所有用户共享、又不修改,这样的数据放到ServletContext这个应用域当中,会大大提升效率。因为应用域相当于一个缓存,放到缓存中的数据,下次在用的时候,不需要从数据库中再次获取,大大提升执行效率。

// 存(向ServletContext应用域中存数据)
public void setAttribute(String name, Object value);
// 取(从ServletContext应用域中取数据)
public Object getAttribute(String name);
// 删(删除ServletContext应用域中的数据)
public void removeAttribute(String name);
  • 注意:以后我们编写Servlet类的时候,实际上是不会去直接继承GenericServlet类的,因为我们是B/S结构的系统,这种系统是基于HTTP超文本传输协议的,在Servlet规范当中,提供了一个类叫做HttpServlet,它是专门为HTTP协议准备的一个Servlet类。我们编写的Servlet类要继承HttpServlet。使用HttpServlet处理HTTP协议更便捷。但是你需要知道它的继承结构:

    1
    2
    3
    jakarta.servlet.Servlet(接口)
    jakarta.servlet.GenericServlet implements Servlet(抽象类)
    jakarta.servlet.http.HttpServlet extends GenericServlet(抽象类)

HTTP协议

什么是协议?

协议实际上是某些人,或者某些组织提前制定好的一套规范,大家都按照这个规范来,这样可以做到沟通无障碍。协议就是一套规范,就是一套标准。由其他人或其他组织来负责制定的。

HTTP协议?

  • HTTP协议:是W3C制定的一种超文本传输协议。(通信协议:发送消息的模板提前被制定好。)

  • 所谓 HTTP 协议,就是指,客户端和服务器之间通信时,发送的数据,需要遵守的规则,叫 HTTP 协议。HTTP 协议中的数据又叫报文。

  • W3C:

    • 万维网联盟组织
    • 负责制定标准的:HTTP HTML4.0 HTML5 XML DOM等规范都是W3C制定的。
    • 万维网之父:蒂姆·伯纳斯·李
  • 什么是超文本?

    • 超文本说的就是:不是普通文本,比如流媒体:声音、视频、图片等。
    • HTTP协议支持:不但可以传送普通字符串,同样支持传递声音、视频、图片等流媒体信息。
  • 这种协议游走在B和S之间。B向S发数据要遵循HTTP协议。S向B发数据同样需要遵循HTTP协议。这样B和S才能解耦合。

  • HTTP协议包括:

    请求协议、响应协议

  • HTTP请求协议的具体报文:GET请求

image-20250618192229130

  • HTTP请求协议的具体报文:POST请求

image-20250618192244991

  • HTTP响应协议的具体报文:

  • 怎么向服务器发送GET请求,怎么向服务器发送POST请求?

    • 到目前为止,只有一种情况可以发送POST请求:使用form表单,并且form标签中的method属性值为:method=“post”。
    • 其他所有情况一律都是get请求:
      • 在浏览器地址栏上直接输入URL,敲回车,属于get请求。
      • 在浏览器上直接点击超链接,属于get请求。
      • 使用form表单提交数据时,form标签中没有写method属性,默认就是get
      • 或者使用form的时候,form标签中method属性值为:method=“get”
  • GET请求和POST请求有什么区别?

    • get请求发送数据的时候,数据会挂在URI的后面,并且在URI后面添加一个“?”,"?"后面是数据。这样会导致发送的数据回显在浏览器的地址栏上。(get请求在“请求行”上发送数据)
    • post请求发送数据的时候,在请求体当中发送。不会回显到浏览器的地址栏上。也就是说post发送的数据,在浏览器地址栏上看不到。(post在“请求体”当中发送数据)
    • get请求只能发送普通的字符串。并且发送的字符串长度有限制,不同的浏览器限制不同。这个没有明确的规范。
    • get请求无法发送大数据量。
    • post请求可以发送任何类型的数据,包括普通字符串,流媒体等信息:视频、声音、图片。
    • post请求可以发送大数据量,理论上没有长度限制。
    • get请求在W3C中是这样说的:get请求比较适合从服务器端获取数据。
    • post请求在W3C中是这样说的:post请求比较适合向服务器端传送数据。
    • get请求是安全的。get请求是绝对安全的。为什么?因为get请求只是为了从服务器上获取数据。不会对服务器造成威胁。
    • post请求是危险的。为什么?因为post请求是向服务器提交数据,如果这些数据通过后门的方式进入到服务器当中,服务器是很危险的。另外post是为了提交数据,所以一般情况下拦截请求的时候,大部分会选择拦截(监听)post请求。
    • get请求支持缓存。
    • post请求不支持缓存。(POST是用来修改服务器端的资源的。)
      • post请求之后,服务器“响应的结果”不会被浏览器缓存起来。因为这个缓存没有意义。
  • GET请求和POST请求如何选择,什么时候使用GET请求,什么时候使用POST请求?

    • 怎么选择GET请求和POST请求呢?衡量标准是什么呢?你这个请求是想获取服务器端的数据,还是想向服务器发送数据。如果你是想从服务器上获取资源,建议使用GET请求,如果你这个请求是为了向服务器提交数据,建议使用POST请求。
    • 大部分的form表单提交,都是post方式,因为form表单中要填写大量的数据,这些数据是收集用户的信息,一般是需要传给服务器,服务器将这些数据保存/修改等。
    • 如果表单中有敏感信息,还是建议适用post请求,因为get请求会回显敏感信息到浏览器地址栏上。(例如:密码信息)
    • 做文件上传,一定是post请求。要传的数据不是普通文本。
    • 其他情况都可以使用get请求。
  • 不管你是get请求还是post请求,发送的请求数据格式是完全相同的,只不过位置不同,格式都是统一的:

    • name=value&name=value&name=value&name=value
    • name是什么?
      • 以form表单为例:form表单中input标签的name。
    • value是什么?
      • 以form表单为例:form表单中input标签的value。

HttpServlet源码分析

  • HttpServlet类是专门为HTTP协议准备的。比GenericServlet更加适合HTTP协议下的开发。
  • HttpServlet在哪个包下?
    • jakarta.servlet.http.HttpServlet
  • 到目前为止我们接触了servlet规范中哪些接口?
    • jakarta.servlet.Servlet 核心接口(接口)
    • jakarta.servlet.ServletConfig Servlet配置信息接口(接口)
    • jakarta.servlet.ServletContext Servlet上下文接口(接口)
    • jakarta.servlet.ServletRequest Servlet请求接口(接口)
    • jakarta.servlet.ServletResponse Servlet响应接口(接口)
    • jakarta.servlet.ServletException Servlet异常(类)
    • jakarta.servlet.GenericServlet 标准通用的Servlet类(抽象类)
  • http包下都有哪些类和接口呢?jakarta.servlet.http.*;
    • jakarta.servlet.http.HttpServlet (HTTP协议专用的Servlet类,抽象类)
    • jakarta.servlet.http.HttpServletRequest (HTTP协议专用的请求对象)
    • jakarta.servlet.http.HttpServletResponse (HTTP协议专用的响应对象)
  • HttpServletRequest对象中封装了什么信息?
    • HttpServletRequest,简称request对象。
    • HttpServletRequest中封装了请求协议的全部内容。
    • Tomcat服务器(WEB服务器)将“请求协议”中的数据全部解析出来,然后将这些数据全部封装到request对象当中了。
    • 也就是说,我们只要面向HttpServletRequest,就可以获取请求协议中的数据。
  • HttpServletResponse对象是专门用来响应HTTP协议到浏览器的。
  • 回忆Servlet生命周期?
    • 用户第一次请求
      • Tomcat服务器通过反射机制,调用无参数构造方法。创建Servlet对象。(web.xml文件中配置的Servlet类对应的对象。)
      • Tomcat服务器调用Servlet对象的init方法完成初始化。
      • Tomcat服务器调用Servlet对象的service方法处理请求。
    • 用户第二次请求
      • Tomcat服务器调用Servlet对象的service方法处理请求。
      • Tomcat服务器调用Servlet对象的service方法处理请求。
    • 服务器关闭
      • Tomcat服务器调用Servlet对象的destroy方法,做销毁之前的准备工作。
      • Tomcat服务器销毁Servlet对象。
  • HttpServlet源码分析:
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
public class HelloServlet extends HttpServlet {
// 用户第一次请求,创建HelloServlet对象的时候,会执行这个无参数构造方法。
public HelloServlet() {
}

//override 重写 doGet方法
//override 重写 doPost方法
}

public abstract class GenericServlet implements Servlet, ServletConfig,
java.io.Serializable {

// 用户第一次请求的时候,HelloServlet对象第一次被创建之后,这个init方法会执行。
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
// 用户第一次请求的时候,带有参数的init(ServletConfig config)执行之后,会执行这个没有参数的init()
public void init() throws ServletException {
// NOOP by default
}
}

// HttpServlet模板类。
public abstract class HttpServlet extends GenericServlet {
// 用户发送第一次请求的时候这个service会执行
// 用户发送第N次请求的时候,这个service方法还是会执行。
// 用户只要发送一次请求,这个service方法就会执行一次。
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {

HttpServletRequest request;
HttpServletResponse response;

try {
// 将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(lStrings.getString("http.non_http"));
}
// 调用重载的service方法。
service(request, response);
}

// 这个service方法的两个参数都是带有Http的。
// 这个service是一个模板方法。
// 在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 获取请求方式
// 这个请求方式最终可能是:""
// 注意:request.getMethod()方法获取的是请求方式,可能是七种之一:
// GET POST PUT DELETE HEAD OPTIONS TRACE
String method = req.getMethod();

// 如果请求方式是GET请求,则执行doGet方法。
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;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// 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)) {
// 如果请求方式是POST请求,则执行doPost方法。
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);
}
}


protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
// 报405错误
String msg = lStrings.getString("http.method_get_not_supported");
sendMethodNotAllowed(req, resp, msg);
}

protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 报405错误
String msg = lStrings.getString("http.method_post_not_supported");
sendMethodNotAllowed(req, resp, msg);
}

}

/*
通过以上源代码分析:
假设前端发送的请求是get请求,后端程序员重写的方法是doPost
假设前端发送的请求是post请求,后端程序员重写的方法是doGet
会发生什么呢?
发生405这样的一个错误。
405表示前端的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。

通过以上源代码可以知道:只要HttpServlet类中的doGet方法或doPost方法执行了,必然405.

怎么避免405的错误呢?
后端重写了doGet方法,前端一定要发get请求。
后端重写了doPost方法,前端一定要发post请求。
这样可以避免405错误。

这种前端到底需要发什么样的请求,其实应该后端说了算。后端让发什么方式,前端就得发什么方式。

有的人,你会看到为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写。
这样,确实可以避免405的发生,但是不建议,405错误还是有用的。该报错的时候就应该让他报错。
如果你要是同时重写了doGet和doPost,那还不如你直接重写service方法好了。这样代码还能
少写一点。
*/


  • 我们编写的HelloServlet直接继承HttpServlet,直接重写HttpServlet类中的service()方法行吗?

    • 可以,只不过你享受不到405错误。享受不到HTTP协议专属的东西。
  • 到今天我们终于得到了最终的一个Servlet类的开发步骤:

    • 第一步:编写一个Servlet类,直接继承HttpServlet
    • 第二步:重写doGet方法或者重写doPost方法,到底重写谁,javaweb程序员说了算。
    • 第三步:将Servlet类配置到web.xml文件当中。
    • 第四步:准备前端的页面(form表单),form表单中指定请求路径即可。

HttpServletRequest接口详解

  • HttpServletRequest是一个接口,全限定名称:jakarta.servlet.http.HttpServletRequest

  • HttpServletRequest接口是Servlet规范中的一员。

  • HttpServletRequest接口的父接口:ServletRequest

    1
    public interface HttpServletRequest extends ServletRequest {}
  • HttpServletRequest接口的实现类谁写的? HttpServletRequest对象是谁给创建的?

    • org.apache.catalina.connector.RequestFacade 实现了 HttpServletRequest接口

      1
      public class RequestFacade implements HttpServletRequest {}
    • 测试结果说明:Tomcat服务器(WEB服务器、WEB容器)实现了HttpServletRequest接口,Tomcat服务器实现了Servlet规范。而对于我们javaweb程序员来说,实际上不需要关心这个,我们只需要面向接口编程即可。我们关心的是HttpServletRequest接口中有哪些方法,这些方法可以完成什么功能!!!!

    • HttpServletRequest对象是Tomcat服务器负责创建的。这个对象中封装了HTTP的请求协议。

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

    • javaweb程序员面向HttpServletRequest接口编程,调用方法就可以获取到请求的信息了。

    • request对象和response对象,一个是请求对象,一个是响应对象。这两个对象只在当前请求中有效。

  • HttpServletRequest接口中常用的方法

    1
    2
    3
    4
    5
    Map<String,String[]> getParameterMap() 这个是获取Map
    Enumeration<String> getParameterNames() 这个是获取Map集合中所有的key
    String[] getParameterValues(String name) 根据key获取Map集合的value
    String getParameter(String name) 获取value这个一维数组当中的第一个元素。这个方法最常用。
    // 以上的4个方法,和获取用户提交的数据有关系。

    注意:前端表单提交数据的时候,假设提交了120这样的“数字”,其实是以字符串"120"的方式提交的,所以服务器端获取到的一定是一个字符串的"120",而不是一个数字。(前端永远提交的是字符串,后端获取的也永远是字符串。)

    • request对象实际上又称为“请求域”对象。

      • 应用域对象是什么?

        • ServletContext (Servlet上下文对象。)

        • 什么情况下会考虑向ServletContext这个应用域当中绑定数据呢?

          • 第一:所有用户共享的数据。
          • 第二:这个共享的数据量很小。
          • 第三:这个共享的数据很少的修改操作。
          • 在以上三个条件都满足的情况下,使用这个应用域对象,可以大大提高我们程序执行效率。
          • 实际上向应用域当中绑定数据,就相当于把数据放到了缓存(Cache)当中,然后用户访问的时候直接从缓存中取,减少IO的操作,大大提升系统的性能,所以缓存技术是提高系统性能的重要手段。
        • ServletContext当中有三个操作域的方法:

          1
          2
          3
          void setAttribute(String name, Object obj); // 向域当中绑定数据。
          Object getAttribute(String name); // 从域当中根据name获取数据。
          void removeAttribute(String name); // 将域当中绑定的数据移除
      • “请求域”对象

        • “请求域”对象要比“应用域”对象范围小很多。生命周期短很多。请求域只在一次请求内有效。

        • 一个请求对象request对应一个请求域对象。一次请求结束之后,这个请求域就销毁了。

        • 请求域对象也有这三个方法:

          1
          2
          3
          void setAttribute(String name, Object obj); // 向域当中绑定数据。
          Object getAttribute(String name); // 从域当中根据name获取数据。
          void removeAttribute(String name); // 将域当中绑定的数据移除
        • 请求域和应用域的选用原则?

          • 尽量使用小的域对象,因为小的域对象占用的资源较少。

        转发(一次请求)

        请求转发是指,服务器收到请求后,从一次资源跳转到另一个资源。

        1
        2
        3
        4
        5
        6
        7
        8
        // 第一步:获取请求转发器对象
        RequestDispatcher dispatcher = request.getRequestDispatcher("/b");
        // 第二步:调用转发器的forward方法完成跳转/转发
        dispatcher.forward(request,response);

        // 第一步和第二步代码可以联合在一起。
        request.getRequestDispatcher("/b").forward(request,response);

      • 两个Servlet怎么共享数据?

        • 将数据放到ServletContext应用域当中,当然是可以的,但是应用域范围太大,占用资源太多。不建议使用。
        • 可以将数据放到request域当中,然后AServlet转发到BServlet,保证AServlet和BServlet在同一次请求当中,这样就可以做到两个Servlet,或者多个Servlet共享同一份数据。
      • 转发的下一个资源必须是一个Servlet吗?

        • 不一定,只要是Tomcat服务器当中的合法资源,都是可以转发的。例如:html…
        • 注意:转发的时候,路径的写法要注意,转发的路径以“/”开始,不加项目名。
      • 关于request对象中两个非常容易混淆的方法:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        // uri?username=zhangsan&userpwd=123&sex=1
        String username = request.getParameter("username");

        // 之前一定是执行过:request.setAttribute("name", new Object())
        Object obj = request.getAttribute("name");

        // 以上两个方法的区别是什么?
        // 第一个方法:获取的是用户在浏览器上提交的数据。
        // 第二个方法:获取的是请求域当中绑定的数据。
      • HttpServletRequest接口的其他常用方法:

        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
        28
        29
        30
        31
        32
        33
        34
        35
        36
        // 获取客户端的IP地址
        String remoteAddr = request.getRemoteAddr();

        // get请求在请求行上提交数据。
        // post请求在请求体中提交数据。
        // 设置请求体的字符集。(显然这个方法是处理POST请求的乱码问题。这种方式并不能解决get请求的乱码问题。)
        // Tomcat10之后,request请求体当中的字符集默认就是UTF-8,不需要设置字符集,不会出现乱码问题。
        // Tomcat9前(包括9在内),如果前端请求体提交的是中文,后端获取之后出现乱码,怎么解决这个乱码?执行以下代码。
        request.setCharacterEncoding("UTF-8");

        // 在Tomcat9之前(包括9),响应中文也是有乱码的,怎么解决这个响应的乱码?
        response.setContentType("text/html;charset=UTF-8");
        // 在Tomcat10之后,包括10在内,响应中文的时候就不在出现乱码问题了。以上代码就不需要设置UTF-8了。

        // 注意一个细节
        // 在Tomcat10包括10在内之后的版本,中文将不再出现乱码。

        // get请求乱码问题怎么解决?
        // get请求发送的时候,数据是在请求行上提交的,不是在请求体当中提交的。
        // get请求乱码怎么解决
        // 方案:修改CATALINA_HOME/conf/server.xml配置文件
        <Connector URIEncoding="UTF-8" />
        // 注意:从Tomcat8之后,URIEncoding的默认值就是UTF-8,所以GET请求也没有乱码问题了。

        // 获取应用的根路径
        String contextPath = request.getContextPath();

        // 获取请求方式
        String method = request.getMethod();

        // 获取请求的URI
        String uri = request.getRequestURI(); // /aaa/testRequest

        // 获取servlet path
        String servletPath = request.getServletPath(); // /testRequest

HttpServletResponse接口详解

HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传递给 Servlet 程序去使用。HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置

字节流 getOutputStream(); 常用于下载(传递二进制数据)

字符流 getWriter(); 常用于回传字符串(常用)

两个流同时只能使用一个。使用了字节流,就不能再使用字符流,反之亦然,否则就会报错。

转发和重定向

  • 在一个web应用中通过两种方式,可以完成资源的跳转:

    • 第一种方式:转发
    • 第二种方式:重定向
  • 转发和重定向有什么区别?

    • 代码上有什么区别?

      • 转发

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        // 获取请求转发器对象
        RequestDispatcher dispatcher = request.getRequestDispatcher("/dept/list");
        // 调用请求转发器对象的forward方法完成转发
        dispatcher.forward(request, response);

        // 合并一行代码
        request.getRequestDispatcher("/dept/list").forward(request, response);
        // 转发的时候是一次请求,不管你转发了多少次。都是一次请求。
        // AServlet转发到BServlet,再转发到CServlet,再转发到DServlet,不管转发了多少次,都在同一个request当中。
        // 这是因为调用forward方法的时候,会将当前的request和response对象传递给下一个Servlet。
      • 重定向

        1
        2
        3
        4
        5
        // 注意:路径上要加一个项目名。为什么?
        // 浏览器发送请求,请求路径上是需要添加项目名的。
        // 以下这一行代码会将请求路径“/oa/dept/list”发送给浏览器
        // 浏览器会自发的向服务器发送一次全新的请求:/oa/dept/list
        response.sendRedirect("/oa/dept/list");
    • 形式上有什么区别?

    • 转发和重定向的本质区别?

      • 转发:是由WEB服务器来控制的。A资源跳转到B资源,这个跳转动作是Tomcat服务器内部完成的。
      • 重定向:是浏览器完成的。具体跳转到哪个资源,是浏览器说了算。
  • 转发和重定向应该如何选择?什么时候使用转发,什么时候使用重定向?

    • 如果在上一个Servlet当中向request域当中绑定了数据,希望从下一个Servlet当中把request域里面的数据取出来,使用转发机制。
    • 剩下所有的请求均使用重定向。(重定向使用较多。)
  • 跳转的下一个资源有没有要求呢?必须是一个Servlet吗?

    • 不一定,跳转的资源只要是服务器内部合法的资源即可。包括:Servlet、JSP、HTML…
  • 转发会存在浏览器的刷新问题。

Web应用开发

对于一个动态的web应用来说,一个请求和响应的过程有多少个角色参与,角色和角色之间有多少个协议

image-20250618192347534

  • 有哪些角色(在整个BS结构的系统当中,有哪些人参与进去了)

    • 浏览器软件的开发团队(谷歌浏览器、火狐浏览器、IE浏览器…)
    • WEB Server的开发团队(Tomcat、Jetty、WebLogic、JBOSS、WebSphere…)
    • DB Server的开发团队(Oracle、MySQL…)
    • webapp的开发团队(JavaWEB程序员开发)
  • 角色和角色之间需要遵守哪些规范,哪些协议

    • webapp的开发团队 和 WEB Server的开发团队 之间有一套规范: JavaEE规范之一Servlet规范。
      • 实现WEB Server 和 webapp解耦合。
    • Browser 和 WebServer之间有一套传输协议:HTTP协议。(超文本传输协议。)
    • webapp开发团队 和 DB Server的开发团队之间有一套规范:JDBC规范。
  • Servlet规范是一个什么规范?

    • 遵循Servlet规范的webapp,这个webapp就可以放在不同的WEB服务器中运行。
    • Servlet规范包括什么呢?
      • 规范了哪些接口
      • 规范了哪些类
      • 规范了一个web应用中应该有哪些配置文件
      • 规范了一个web应用中配置文件的名字
      • 规范了一个web应用中配置文件存放的路径
      • 规范了一个web应用中配置文件的内容
      • 规范了一个合法有效的web应用它的目录结构应该是怎样的。

实现一个最基本的web应用

  • 第一步:找到CATALINA_HOME\webapps目录

    • 因为所有的webapp要放到webapps目录下。(没有为什么,这是Tomcat服务器的要求。如果不放到这里,Tomcat服务器找不到你的应用。)
  • 第二步:在CATALINA_HOME\webapps目录下新建一个子目录,起名:oa

    • 这个目录名oa就是你这个webapp的名字。
  • 第三步:在oa目录下新建资源文件,例如:index.html

    • 编写index.html文件的内容。
  • 第四步:启动Tomcat服务器

  • 第五步:打开浏览器,在浏览器地址栏上输入这样的URL:

  • http://127.0.0.1:8080/oa/index.html

  • 思考一个问题:

    • 我们在浏览器上直接输入一个URL,然后回车。这个动作和超链接一样,我们完全可以使用超链接。

      1
      2
      3
      4
      5
      6
      <!--注意以下的路径,以/开始,带项目名,是一个绝对路径。不需要添加:http://127.0.0.1:8080-->
      <a href="/oa/login.html">user login2</a>

      <!--多个层级也没有关系,正常访问即可。-->
      <!--注意:我们目前前端上的路径都以“/”开始的,都是加项目名的。-->
      <a href="/oa/test/debug/d.html">d page</a>
  • http://127.0.0.1:8080/oa/userList.html

    • 访问这个地址,可以展示一个用户列表页面。但是这个用户列表页面是写死在HTML文件当中的。这种资源我们称为静态资源。怎么能变成动态资源。显然需要连接数据库。
    • 连接数据库需要JDBC程序,也就是说需要编写Java程序连接数据库,数据库中有多少条记录,页面上就显示多少条记录,这种技术被称为动态网页技术。(动态网页技术并不是说页面中有flash动画。动态网页技术是说页面中的数据是动态的,根据数据库中数据的变化而变化。)

开发Servlet的webapp(重点)

  • 开发步骤是怎样的?

    • 第一步:在webapps目录下新建一个目录,起名crm(这个crm就是webapp的名字)

      • 注意:crm就是这个webapp的根
    • 第二步:在webapp的根下新建一个目录:WEB-INF

      • 注意:这个目录的名字是Servlet规范中规定的,必须全部大写,必须一模一样
    • 第三步:在WEB-INF目录下新建一个目录:classes

      • 注意:这个目录的名字必须是全部小写的classes。这也是Servlet规范中规定的。另外这个目录下一定存放的是Java程序编译之后的class文件
    • 第四步:在WEB-INF目录下新建一个目录:lib

      • 注意:这个目录不是必须的。但如果一个webapp需要第三方的jar包的话,这个jar包要放到这个lib目录下,这个目录的名字也不能随意编写,必须是全部小写的lib。例如java语言连接数据库需要数据库的驱动jar包。那么这个jar包就一定要放到lib目录下
    • 第五步:在WEB-INF目录下新建一个文件:web.xml

      • 注意:这个文件是必须的,这个文件名必须叫做web.xml。这个文件必须放在这里。一个合法的webapp,web.xml文件是必须的,这个web.xml文件就是一个配置文件,在这个配置文件中描述了请求路径和Servlet类之间的对照关系

      • 这个文件最好从其他的webapp中拷贝

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <?xml version="1.0" encoding="UTF-8"?>

      <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
      version="5.0"
      metadata-complete="true">


      </web-app>

    • 第六步:编写一个Java程序,必须实现Servlet接口。

      • 这个Servlet接口不在JDK当中。(因为Servlet不是JavaSE了。Servlet属于JavaEE,是另外的一套类库。)
      • Servlet接口(Servlet.class文件)是Oracle提供的
      • Servlet接口是JavaEE的规范中的一员。
      • Tomcat服务器实现了Servlet规范,所以Tomcat服务器也需要使用Servlet接口。Tomcat服务器的CATALINA_HOME\lib目录下有一个servlet-api.jar,解压这个servlet-api.jar之后,你会看到里面有一个Servlet.class文件。
      • 从JakartaEE9开始,Servlet接口的全名变了:jakarta.servlet.Servlet
    • 第七步:编译我们编写的HelloServlet

      • 重点:你怎么能让你的HelloServlet编译通过呢?配置环境变量CLASSPATH

        CLASSPATH=.;C:\dev\apache-tomcat-10.0.12\lib\servlet-api.jar

      • 思考问题:以上配置的CLASSPATH和Tomcat服务器运行有没有关系?

        • 没有任何关系,以上配置这个环境变量只是为了让你的HelloServlet能够正常编译生成class文件。
    • 第八步:将以上编译之后的HelloServlet.class文件拷贝到WEB-INF\classes目录下。

    • 第九步:在web.xml文件中编写配置信息,将“请求路径”和“Servlet类名”关联在一起。

      • 这一步用专业术语描述:在web.xml文件中注册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
      28
      29
      <?xml version="1.0" encoding="UTF-8"?>

      <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
      version="5.0"
      metadata-complete="true">

      <!--servlet描述信息-->
      <!--任何一个servlet都对应一个servlet-mapping -->
      <servlet>
      <servlet-name>fdsafdsagfdsafdsa</servlet-name>
      <!--这个位置必须是带有包名的全限定类名-->
      <servlet-class>com.bjpowernode.servlet.HelloServlet</servlet-class>
      </servlet>

      <!--servlet映射信息-->
      <servlet-mapping>
      <!--这个也是随便的,不过这里写的内容要和上面的一样。-->
      <servlet-name>fdsafdsagfdsafdsa</servlet-name>
      <!--这里需要一个路径-->
      <!--这个路径唯一的要求是必须以 / 开始-->
      <!--当前这个路径可以随便写-->
      <url-pattern>/fdsa/fd/saf/d/sa/fd/sa/fd</url-pattern>
      </servlet-mapping>

      </web-app>

    • 第十步:启动Tomcat服务器

    • 第十一步:打开浏览器,在浏览器地址栏上输入一个url,这个URL必须是:

      • http://127.0.0.1:8080/crm/fdsa/fd/saf/d/sa/fd/sa/fd
      • 浏览器上的请求路径不能随便写,这个请求路径必须和web.xml文件中的url-pattern一致。
      • 注意:浏览器上的请求路径和web.xml文件中的url-pattern的唯一区别就是:浏览器上的请求路径带项目名:/crm
    • 浏览器上编写的路径太复杂,可以使用超链接。(非常重要:html页面只能放到WEB-INF目录外面。

    • 以后不需要我们编写main方法了。tomcat服务器负责调用main方法,Tomcat服务器启动的时候执行的就是main方法。我们javaweb程序员只需要编写Servlet接口的实现类,然后将其注册到web.xml文件中,即可。

    • 总结一下:一个合法的webapp目录结构应该是怎样的?

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      webapproot
      |------WEB-INF
      |------classes(存放字节码)
      |------lib(第三方jar包)
      |------web.xml(注册Servlet)
      |------html
      |------css
      |------javascript
      |------image
      ....
    • 浏览器发送请求,到最终服务器调用Servlet中的方法,是怎样的一个过程?(以下这个过程描述的很粗糙。其中还有很多步骤我省略了。)

      • 用户输入URL,或者直接点击超链接:http://127.0.0.1:8080/crm/fdsa/fd/saf/d/sa/fd/sa/fd
      • 然后Tomcat服务器接收到请求,截取路径:/crm/fdsa/fd/saf/d/sa/fd/sa/fd
      • Tomcat服务器找到crm项目
      • Tomcat服务器在web.xml文件中查找/fdsa/fd/saf/d/sa/fd/sa/fd 对应的Servlet是:com.bjpowernode.servlet.HelloServlet
      • Tomcat服务器通过反射机制,创建com.bjpowernode.servlet.HelloServlet的对象。
      • Tomcat服务器调用com.bjpowernode.servlet.HelloServlet对象的service方法。

解决Tomcat服务器在DOS命令窗口中的乱码问题(控制台乱码)

将CATALINA_HOME/conf/logging.properties文件中的内容修改如下:

java.util.logging.ConsoleHandler.encoding = GBK

向浏览器响应一段HTML代码

1
2
3
4
5
public void service(ServletRequest request, ServletResponse response){
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print("<h1>hello servlet!</h1>");
}

在Servlet中连接数据库

  • Servlet是Java程序,所以在Servlet中完全可以编写JDBC代码连接数据库。
  • 在一个webapp中去连接数据库,需要将驱动jar包放到WEB-INF/lib目录下。(com.mysql.cj.jdbc.Driver)

在集成开发环境当中开发Servlet程序

  • 使用IDEA集成开发工具开发Servlet

    • 第一步:New Project
    • 第二步:新建模块(File --> new --> Module…)
      • 这里新建的是一个普通的JavaSE模块(这里先不要新建Java Enterprise模块)
      • 这个Module自动会被放在javaweb的project下面。
      • 这个Module起名:servlet01
    • 第三步:让Module变成JavaEE的模块。(让Module变成webapp的模块。符合webapp规范。符合Servlet规范的Module)
      • 在Module上点击右键:Add Framework Support…(添加框架支持)
      • 在弹出的窗口中,选择Web Application(选择的是webapp的支持)
      • 选择了这个webapp的支持之后,IDEA会自动给你生成一个符合Servlet规范的webpp目录结构。
      • 重点,需要注意的:在IDEA工具中根据Web Application模板生成的目录中有一个web目录,这个目录就代表webapp的根
    • 第四步(非必须):根据Web Application生成的资源中有index.jsp文件,这里我选择删除这个index.jsp文件。
    • 第五步:编写Servlet(StudentServlet)
      • class StudentServlet implements Servlet
      • 这个时候发现Servlet.class文件没有。怎么办?将CATALINA_HOME/lib/servlet-api.jar和jsp-api.jar添加到classpath当中(这里的classpath说的是IDEA的classpath)
        • File --> Project Structrue --> Modules --> + 加号 --> Add JARS…
      • 实现jakarta.servlet.Servlet接口中的5个方法。
    • 第六步:在Servlet当中的service方法中编写业务代码
    • 第七步:在WEB-INF目录下新建子目录lib,将连接数据库的驱动jar包放到lib目录下。
    • 第八步:在web.xml文件中完成StudentServlet类的注册。(请求路径和Servlet之间对应起来)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">

    <servlet>
    <servlet-name>studentServlet</servlet-name>
    <servlet-class>com.bjpowernode.javaweb.servlet.StudentServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>studentServlet</servlet-name>
    <url-pattern>/servlet/student</url-pattern>
    </servlet-mapping>

    </web-app>
    • 第九步:给一个html页面,在HTML页面中编写一个超链接,用户点击这个超链接,发送请求,Tomcat执行后台的StudentServlet。

      • student.html
      • 这个文件不能放到WEB-INF目录里面,只能放到WEB-INF目录外面。
      • student.html文件的内容
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <title>student page</title>
      </head>
      <body>
      <!--这里的项目名是 /xmm ,无法动态获取,先写死-->
      <a href="/xmm/servlet/student">student list</a>
      </body>
      </html>
    • 第十步:让IDEA工具去关联Tomcat服务器。关联的过程当中将webapp部署到Tomcat服务器当中。

      • IDEA工具右上角,绿色小锤子右边有一个:Add Configuration
      • 左上角加号,点击Tomcat Server --> local
      • 在弹出的界面中设置服务器Server的参数(基本上不用动)
      • 在当前窗口中有一个Deployment(点击这个用来部署webapp),继续点击加号,部署即可。
      • 修改 Application context为:/xmm
    • 第十一步:启动Tomcat服务器

      • 采用debug的模式启动Tomcat服务器。
    • 第十二步:打开浏览器,在浏览器地址栏上输入:http://localhost:8080/xmm/student.html

web站点的欢迎页面

  • 什么是一个web站点的欢迎页面?

    • 对于一个webapp来说,我们是可以设置它的欢迎页面的。
    • 设置了欢迎页面之后,当你访问这个webapp的时候,或者访问这个web站点的时候,没有指定任何“资源路径”,这个时候会默认访问你的欢迎页面。
    • 我们一般的访问方式是:
    • 如果我们访问的方式是:
      • http://localhost:8080/servlet06 如果我们访问的就是这个站点,没有指定具体的资源路径。它默认会访问谁呢?
      • 默认会访问你设置的欢迎页面。
  • 怎么设置欢迎页面呢?

    • 第一步:我在IDEA工具的web目录下新建了一个文件login.html

    • 第二步:在web.xml文件中进行了以下的配置

      注意:设置欢迎页面的时候,这个路径不需要以“/”开始。并且这个路径默认是从webapp的根下开始查找。

      1
      2
      3
      <welcome-file-list>
      <welcome-file>login.html</welcome-file>
      </welcome-file-list>
    • 第三步:启动服务器,浏览器地址栏输入地址

  • 如果在webapp的根下新建一个目录,目录中再给一个文件,那么这个欢迎页该如何设置呢?

    • 在webapp根下新建page1

    • 在page1下新建page2目录

    • 在page2目录下新建page.html页面

    • 在web.xml文件中应该这样配置

      1
      2
      3
      <welcome-file-list>
      <welcome-file>page1/page2/page.html</welcome-file>
      </welcome-file-list>
  • 一个webapp是可以设置多个欢迎页面的

    注意:越靠上的优先级越高。找不到的继续向下找。

    1
    2
    3
    4
    <welcome-file-list>
    <welcome-file>page1/page2/page.html</welcome-file>
    <welcome-file>login.html</welcome-file>
    </welcome-file-list>
  • 你有没有注意一件事:当我的文件名设置为index.html的时候,不需要在web.xml文件中进行配置欢迎页面。这是为什么?

    • 这是因为Tomcat服务器已经提前配置好了。

    • 实际上配置欢迎页面有两个地方可以配置:

      • 一个是在webapp内部的web.xml文件中。(在这个地方配置的属于局部配置)

      • 一个是在CATALINA_HOME/conf/web.xml文件中进行配置。(在这个地方配置的属于全局配置)

        1
        2
        3
        4
        5
        <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
        • Tomcat服务器的全局欢迎页面是:index.html index.htm index.jsp。如果你一个web站点没有设置局部的欢迎页面,Tomcat服务器就会以index.html index.htm index.jsp作为一个web站点的欢迎页面。
      • 注意原则:局部优先原则。(就近原则)

  • 欢迎页可以是一个Servlet吗?

    • 当然可以。

    • 静态资源:index.html welcome.html …

    • 动态资源:Servlet类。

    • 步骤:

      • 第一步:写一个Servlet

        1
        2
        3
        4
        5
        6
        7
        8
        public class WelcomeServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.print("<h1>welcome to bjpowernode!</h1>");
        }
        }
      • 第二步:在web.xml文件中配置servlet

        1
        2
        3
        4
        5
        6
        7
        8
        <servlet>
        <servlet-name>welcomeServlet</servlet-name>
        <servlet-class>com.bjpowernode.javaweb.servlet.WelcomeServlet</servlet-class>
        </servlet>
        <servlet-mapping>
        <servlet-name>welcomeServlet</servlet-name>
        <url-pattern>/fdsa/fds/a/fds/af/ds/af/dsafdsafdsa</url-pattern>
        </servlet-mapping>
      • 第三步:在web.xml文件中配置欢迎页

        1
        2
        3
        <welcome-file-list>
        <welcome-file>fdsa/fds/a/fds/af/ds/af/dsafdsafdsa</welcome-file>
        </welcome-file-list>

关于WEB-INF目录

  • 在WEB-INF目录下新建了一个文件:welcome.html
  • 打开浏览器访问:http://localhost:8080/servlet07/WEB-INF/welcome.html 出现了404错误。
  • 注意:放在WEB-INF目录下的资源是受保护的。在浏览器上不能够通过路径直接访问。所以像HTML、CSS、JS、image等静态资源一定要放到WEB-INF目录之外。

Servlet注解,简化配置

  • Servlet3.0版本之后,推出了各种Servlet基于注解式开发。

    • 开发效率高,不需要编写大量的配置信息。直接在java类上使用注解进行标注。
    • web.xml文件体积变小了。
  • 并不是说注解有了之后,web.xml文件就不需要了:

    • 有一些需要变化的信息,还是要配置到web.xml文件中。一般都是 注解+配置文件 的开发模式。
    • 一些不会经常变化修改的配置建议使用注解。一些可能会被修改的建议写到配置文件中。
  • 我们的第一个注解:

    jakarta.servlet.annotation.WebServlet

    • 在Servlet类上使用:@WebServlet,WebServlet注解中有哪些属性呢?

      • name属性:用来指定Servlet的名字。等同于:
      • urlPatterns属性:用来指定Servlet的映射路径。可以指定多个字符串。
      • loadOnStartUp属性:用来指定在服务器启动阶段是否加载该Servlet。等同于:
      • value属性:当注解的属性名是value的时候,使用注解的时候,value属性名是可以省略的。
      • 注意:不是必须将所有属性都写上,只需要提供需要的。
      • 注意:属性是一个数组,如果数组中只有一个元素,使用该注解的时候,属性值的大括号可以省略。
  • 注解对象的使用格式:

    • @注解名称(属性名=属性值, 属性名=属性值, 属性名=属性值…)

分析使用纯粹Servlet开发web应用的缺陷

  • 在Servlet当中编写HTML/CSS/JavaScript等前端代码。存在什么问题?
    • java程序中编写前端代码,编写难度大。麻烦。
    • java程序中编写前端代码,显然程序的耦合度非常高。
    • java程序中编写前端代码,代码非常不美观。
    • java程序中编写前端代码,维护成本太高。(非常难于维护)
      • 修改小小的一个前端代码,只要有改动,就需要重新编译java代码,生成新的class文件,打一个新的war包,重新发布。
  • 思考一下,如果是你的话,你准备怎么解决这个问题?
    • 思路很重要。使用什么样的思路去做、去解决这个问题
      • 上面的那个Servlet(Java程序)能不能不写了,让机器自动生成。我们程序员只需要写这个Servlet程序中的“前端的那段代码”,然后让机器将我们写的“前端代码”自动翻译生成“Servlet这种java程序”。然后机器再自动将“java”程序编译生成"class"文件。然后再使用JVM调用这个class中的方法。

JSP

  • 我的第一个JSP程序:

    • 在WEB-INF目录之外创建一个index.jsp文件,然后这个文件中没有任何内容。
  • 将上面的项目部署之后,启动服务器,打开浏览器,访问以下地址:

    • http://localhost:8080/jsp/index.jsp 展现在大家面前的是一个空白。
    • 实际上访问以上的这个:index.jsp,底层执行的是:index_jsp.class 这个java程序。
    • 这个index.jsp会被tomcat翻译生成index_jsp.java文件,然后tomcat服务器又会将index_jsp.java编译生成index_jsp.class文件
    • 访问index.jsp,实际上执行的是index_jsp.class中的方法。
  • JSP实际上就是一个Servlet。

    • index.jsp访问的时候,会自动翻译生成index_jsp.java,会自动编译生成index_jsp.class,那么index_jsp 这就是一个类。
    • index_jsp 类继承 HttpJspBase,而HttpJspBase类继承的是HttpServlet。所以index_jsp类就是一个Servlet类。
    • jsp的生命周期和Servlet的生命周期完全相同。完全就是一个东西。没有任何区别。
    • jsp和servlet一样,都是单例的。(假单例。)
  • jsp文件第一次访问的时候是比较慢的,为什么?

    • 第一次比较麻烦:
      • 要把jsp文件翻译生成java源文件
      • java源文件要编译生成class字节码文件
      • 然后通过class去创建servlet对象
      • 然后调用servlet对象的init方法
      • 最后调用servlet对象的service方法。
    • 第二次就比较快了,为什么?
      • 因为第二次直接调用单例servlet对象的service方法即可。
  • JSP是什么?

    • JSP是java程序。(JSP本质还是一个Servlet)
    • JSP是:JavaServer Pages的缩写。(基于Java语言实现的服务器端的页面。)
    • Servlet是JavaEE的13个子规范之一,那么JSP也是JavaEE的13个子规范之一。
    • JSP是一套规范。所有的web容器/web服务器都是遵循这套规范的,都是按照这套规范进行的“翻译”
    • 每一个web容器/web服务器都会内置一个JSP翻译引擎。
  • 对JSP进行错误调试的时候,还是要直接打开JSP文件对应的java文件,检查java代码。

  • JSP既然本质上是一个Servlet,那么JSP和Servlet到底有什么区别呢?

    • 职责不同:
      • Servlet的职责是什么:收集数据。(Servlet的强项是逻辑处理,业务处理,然后链接数据库,获取/收集数据。)
      • JSP的职责是什么:展示数据。(JSP的强项是做数据的展示)
  • JSP的基础语法

    • 在jsp文件中直接编写文字,都会自动被翻译到哪里?

      • 翻译到servlet类的service方法的out.write(“翻译到这里”),直接翻译到双引号里,被java程序当做普通字符串打印输出到浏览器。
      • 在JSP中编写的HTML CSS JS代码,这些代码对于JSP来说只是一个普通的字符串。但是JSP把这个普通的字符串一旦输出到浏览器,浏览器就会对HTML CSS JS进行解释执行。展现一个效果。
    • JSP的page指令(这个指令后面再详细说,这里先解决一下中文乱码问题),解决响应时的中文乱码问题:

      • 通过page指令来设置响应的内容类型,在内容类型的最后面添加:charset=UTF-8
        • <%@page contentType=“text/html;charset=UTF-8”%>,表示响应的内容类型是text/html,采用的字符集UTF-8
        • <%@page import=“java.util.List,java.util.ArrayList”%>
    • 怎么在JSP中编写Java程序:

      • <% java语句; %>
        • 在这个符号当中编写的被视为java程序,被翻译到Servlet类的service方法内部。
        • 这里你要细心点,你要思考,在<% %>这个符号里面写java代码的时候,你要时时刻刻的记住你正在“方法体”当中写代码
        • 在service方法当中编写的代码是有顺序的,方法体当中的代码要遵循自上而下的顺序依次逐行执行。
        • 在同一个JSP当中 <%%> 这个符号可以出现多个。
      • <%! %>
        • 在这个符号当中编写的java程序会自动翻译到service方法之外。
        • 这个语法很少用,为什么?不建议使用,因为在service方法外面写静态变量和实例变量,都会存在线程安全问题,因为JSP就是servlet,servlet是单例的,多线程并发的环境下,这个静态变量和实例变量一旦有修改操作,必然会存在线程安全问题。
      • JSP的输出语句
        • 怎么向浏览器上输出一个java变量。
        • <% String name = “jack”; out.write("name = " + name); %>
        • 注意:以上代码中的out是JSP的九大内置对象之一。可以直接拿来用。当然,必须只能在service方法内部使用。
        • 如果向浏览器上输出的内容中没有“java代码”,例如输出的字符串是一个固定的字符串,可以直接在jsp中编写,不需要写到<%%> 这里。
        • 如果输出的内容中含有“java代码”,这个时候可以使用以下语法格式:
          • <%= %> 注意:在=的后面编写要输出的内容。
          • <%= %> 这个符号会被翻译到哪里?最终翻译成什么?
            • 翻译成了这个java代码: out.print();
            • 翻译到service方法当中了。
          • 什么时候使用<%=%> 输出呢?输出的内容中含有java的变量,输出的内容是一个动态的内容,不是一个死的字符串。如果输出的是一个固定的字符串,直接在JSP文件中编写即可。
  • 在JSP中如何编写JSP的专业注释

    • <%–JSP的专业注释,不会被翻译到java源代码当中。–%>

    • JSP基础语法总结:

      • JSP中直接编写普通字符串
      • 翻译到service方法的out.write(“这里”)
      • <%%>
        • 翻译到service方法体内部,里面是一条一条的java语句。
      • <%! %>
        • 翻译到service方法之外。
      • <%= %>
        • 翻译到service方法体内部,翻译为:out.print();
      • <%@page contentType=“text/html;charset=UTF-8”%>
        • page指令,通过contentType属性用来设置响应的内容类型。
    • 思考一个问题:如果我只用JSP这一个技术,能不能开发web应用?

      • 当然可以使用JSP来完成所有的功能。因为JSP就是Servlet,在JSP的<%%>里面写的代码就是在service方法当中的,所以在<%%>当中完全可以编写JDBC代码,连接数据库,查询数据,也可以在这个方法当中编写业务逻辑代码,处理业务,都是可以的,所以使用单独的JSP开发web应用完全没问题。
    • 虽然JSP一个技术就可以完成web应用,但是不建议,还是建议采用servlet + jsp的方式进行开发。这样都能将各自的优点发挥出来。JSP就是做数据展示。Servlet就是做数据的收集。(JSP中编写的Java代码越少越好。)一定要职责分明。

  • JSP文件的扩展名必须是xxx.jsp吗?

    • jsp文件的扩展名是可以配置的。不是固定的。

      • 在CATALINA_HOME/conf/web.xml,在这个文件当中配置jsp文件的扩展名。
      1
      2
      3
      4
      5
      <servlet-mapping>
      <servlet-name>jsp</servlet-name>
      <url-pattern>*.jsp</url-pattern>
      <url-pattern>*.jspx</url-pattern>
      </servlet-mapping>
    • xxx.jsp文件对于Tomcat来说,只是一个普通的文本文件,web容器会将xxx.jsp文件最终生成java程序,最终调用的是java对象相关的方法,真正执行的时候,和jsp文件就没有关系了。

    • 包名bean是什么意思?

      • 什么是javabean?实际上javabean你可以理解为符合某种规范的java类,比如:
      • 有无参数构造方法
        • 属性私有化
      • 对外提供公开的set和get方法
        • 实现java.io.Serializable接口
        • 重写toString
      • 重写hashCode+equals
      • javabean其实就是java中的实体类。负责数据的封装。
        • 由于javabean符合javabean规范,具有更强的通用性。
  • JSP的指令

    • 指令的作用:指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件。)

    • 指令包括哪些呢?

      • include指令:包含指令,在JSP中完成静态包含,很少用了。(这里不讲)
      • taglib指令:引入标签库的指令。这个到JJSTL标签库的时候再学习。现在先不管。
      • page指令:目前重点学习一个page指令。
    • 指令的使用语法是什么?

      • <%@指令名 属性名=属性值 属性名=属性值 属性名=属性值…%>
    • 关于page指令当中都有哪些常用的属性呢

      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
      28
      <%@page session="true|false" %>
      true表示启用JSP的内置对象session,表示一定启动session对象。没有session对象会创建。
      如果没有设置,默认值就是session="true"
      session="false" 表示不启动内置对象session。当前JSP页面中无法使用内置对象session。

      <%@page contentType="text/json" %>
      contentType属性用来设置响应的内容类型
      但同时也可以设置字符集。
      <%@page contentType="text/json;charset=UTF-8" %>


      <%@page pageEncoding="UTF-8" %>
      pageEncoding="UTF-8" 表示设置响应时采用的字符集。


      <%@page import="java.util.List, java.util.Date, java.util.ArrayList" %>
      <%@page import="java.util.*" %>
      import语句,导包。


      <%@page errorPage="/error.jsp" %>
      当前页面出现异常之后,跳转到error.jsp页面。
      errorPage属性用来指定出错之后的跳转位置。


      <%@page isErrorPage="true" %>
      表示启用JSP九大内置对象之一:exception
      默认值是false。
  • JSP的九大内置对象

    • jakarta.servlet.jsp.PageContext pageContext 页面作用域

    • jakarta.servlet.http.HttpServletRequest request 请求作用域

    • jakarta.servlet.http.HttpSession session 会话作用域

    • jakarta.servlet.ServletContext application 应用作用域

      • pageContext < request < session < application
      • 以上四个作用域都有:setAttribute、getAttribute、removeAttribute方法。
      • 以上作用域的使用原则:尽可能使用小的域。
    • java.lang.Throwable exception

    • jakarta.servlet.ServletConfig config

    • java.lang.Object page (其实是this,当前的servlet对象)

    • jakarta.servlet.jsp.JspWriter out (负责输出)

    • jakarta.servlet.http.HttpServletResponse response (负责响应)

EL表达式

  • EL表达式是干什么用的?

    • Expression Language(表达式语言)
    • EL表达式可以代替JSP中的java代码,让JSP文件中的程序看起来更加整洁,美观。
    • JSP中夹杂着各种java代码,例如<% java代码 %>、<%=%>等,导致JSP文件很混乱,不好看,不好维护。所以才有了后期的EL表达式。
    • EL表达式可以算是JSP语法的一部分。EL表达式归属于JSP。
  • EL表达式出现在JSP中主要是:

    • 从某个作用域中取数据,然后将其转换成字符串,然后将其输出到浏览器。这就是EL表达式的功效。三大功效:
      • 第一功效:从某个域中取数据。
        • 四个域:
          • pageContext
          • request
          • session
          • application
      • 第二功效:将取出的数据转成字符串。
        • 如果是一个java对象,也会自动调用java对象的toString方法将其转换成字符串。
      • 第三功效:将字符串输出到浏览器。
        • 和这个一样:<%= %>,将其输出到浏览器。
  • EL表达式很好用,基本的语法格式:

    • ${表达式}
  • EL表达式的使用:

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    <%
    // 创建User对象
    User user = new User();
    user.setUsername("jackson");
    user.setPassword("1234");
    user.setAge(50);

    // 将User对象存储到某个域当中。一定要存,因为EL表达式只能从某个范围中取数据。
    // 数据是必须存储到四大范围之一的。
    request.setAttribute("userObj", user);
    %>

    <%--使用EL表达式取--%>
    ${这个位置写什么????这里写的一定是存储到域对象当中时的name}
    要这样写:
    ${userObj}
    等同于java代码:<%=request.getAttribute("userObj")%>
    你不要这样写:${"userObj"}

    面试题:
    ${abc} 和 ${"abc"}的区别是什么?
    ${abc}表示从某个域中取出数据,并且被取的这个数据的name是"abc",之前一定有这样的代码: 域.setAttribute("abc", 对象);
    ${"abc"} 表示直接将"abc"当做普通字符串输出到浏览器。不会从某个域中取数据了。

    ${userObj} 底层是怎么做的?从域中取数据,取出user对象,然后调用user对象的toString方法,转换成字符串,输出到浏览器。

    <%--如果想输出对象的属性值,怎么办?--%>
    ${userObj.username} 使用这个语法的前提是:User对象有getUsername()方法。
    ${userObj.password} 使用这个语法的前提是:User对象有getPassword()方法。
    ${userObj.age} 使用这个语法的前提是:User对象有getAge()方法。
    ${userObj.email} 使用这个语法的前提是:User对象有getEmail()方法。
    EL表达式中的. 这个语法,实际上调用了底层的getXxx()方法。
    注意:如果没有对应的get方法,则出现异常。报500错误。

    ${userObj.addr222.zipcode}
    以上EL表达式对应的java代码:
    user.getAddr222().getZipcode()
    • EL表达式优先从小范围中读取数据。

      • pageContext < request < session < application
    • EL表达式中有四个隐含的隐式的范围:

      • pageScope 对应的是 pageContext范围。
      • requestScope 对应的是 request范围。
      • sessionScope 对应的是 session范围。
      • applicationScope 对应的是 application范围。
    • EL表达式对null进行了预处理。如果是null,则向浏览器输出一个空字符串。

    • EL表达式取数据的时候有两种形式:

      • 第一种:. (大部分使用这种方式)
      • 第二种:[ ] (如果存储到域的时候,这个name中含有特殊字符,可以使用 [ ])
        • request.setAttribute(“abc.def”, “zhangsan”);
        • ${requestScope.abc.def} 这样是无法取值的。
        • 应该这样:${requestScope[“abc.def”]}
    • 掌握使用EL表达式,怎么从Map集合中取数据:

      • ${map.key}
    • 掌握使用EL表达式,怎么从数组和List集合中取数据:

      • ${数组[0]}
      • ${数组[1]}
      • ${list[0]}
    • page指令当中,有一个属性,可以忽略EL表达式

      1
      2
      3
      4
      5
      6
      7
      <%@page contentType="text/html;charset=UTF-8" isELIgnored="true" %>
      isELIgnored="true" 表示忽略EL表达式
      isELIgnored="false" 表示不忽略EL表达式。(这是默认值)

      isELIgnored="true" 这个是全局的控制。

      可以使用反斜杠进行局部控制:\${username} 这样也可以忽略EL表达式。
    • 通过EL表达式获取应用的根:

      • ${pageContext.request.contextPath}
    • EL表达式中其他的隐式对象:

      • pageContext
      • param
      • paramValues
      • initParam
    • EL表达式的运算符

      • 算术运算符
        • +、-、*、/、%
      • 关系运算符
        • [ ] == eq != > >= < <=
      • 逻辑运算符
        • [ ] ! && || not and or
      • 条件运算符
        • [ ] ? :
      • 取值运算符
        • [ ]和.
      • empty运算符
        • [ ] empty运算符的结果是boolean类型
        • [ ] ${empty param.username}
        • [ ] ${not empty param.username}
        • [ ] ${!empty param.password}

JSTL标签库

  • 什么是JSTL标签库?

    • Java Standard Tag Lib(Java标准的标签库)
    • JSTL标签库通常结合EL表达式一起使用。目的是让JSP中的java代码消失。
    • 标签是写在JSP当中的,但实际上最终还是要执行对应的java程序。(java程序在jar包当中。)
  • 使用JSTL标签库的步骤:

    • 第一步:引入JSTL标签库对应的jar包。

      • tomcat10之后引入的jar包是:
        • jakarta.servlet.jsp.jstl-2.0.0.jar
        • jakarta.servlet.jsp.jstl-api-2.0.0.jar
      • 在IDEA当中怎么引入?
        • 在WEB-INF下新建lib目录,然后将jar包拷贝到lib当中。然后将其“Add Lib…”
        • 一定是要和mysql的数据库驱动一样,都是放在WEB-INF/lib目录下的。
        • 什么时候需要将jar包放到WEB-INF/lib目录下?如果这个jar是tomcat服务器没有的。
    • 第二步:在JSP中引入要使用标签库。(使用taglib指令引入标签库。)

      • JSTL提供了很多种标签,你要引入哪个标签????重点掌握核心标签库。
      1
      2
      3
      <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
      这个就是核心标签库。
      prefix="这里随便起一个名字就行了,核心标签库,大家默认的叫做c,你随意。"
    • 第三步:在需要使用标签的位置使用即可。表面使用的是标签,底层实际上还是java程序。

  • JSTL标签的原理

    1
    2
    3
    4
    5
    6
    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    以上uri后面的路径实际上指向了一个xxx.tld文件。
    tld文件实际上是一个xml配置文件。
    在tld文件中描述了“标签”和“java类”之间的关系。
    以上核心标签库对应的tld文件是:c.tld文件。它在哪里。
    在jakarta.servlet.jsp.jstl-2.0.0.jar里面META-INF目录下,有一个c.tld文件。
    • 源码解析:配置文件tld解析

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      <tag>
      <description>对该标签的描述</description>
      <name>catch</name> 标签的名字
      <tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class> 标签对应的java类。
      <body-content>JSP</body-content> 标签体当中可以出现的内容,如果是JSP,就表示标签体中可以出现符合JSP所有语法的代码。例如EL表达式。
      <attribute>
      <description>
      对这个属性的描述
      </description>
      <name>var</name> 属性名
      <required>false</required> false表示该属性不是必须的。true表示该属性是必须的。
      <rtexprvalue>false</rtexprvalue> 这个描述说明了该属性是否支持EL表达式。false表示不支持。true表示支持EL表达式。
      </attribute>
      </tag>

      <c:catch var="">
      JSP....
      </c:catch>
    • jstl中的核心标签库core当中有哪些常用的标签呢?

      • c:if

        • <c:if test=“boolean类型,支持EL表达式”></c: if>
      • c:forEach

        • <c:forEach items=“集合,支持EL表达式” var=“集合中的元素” varStatus=“元素状态对象”> ${元素状态对象.count} </c: forEach>
        • <c:forEach var=“i” begin=“1” end=“10” step=“2”> ${i} </c: forEach>
      • c:choose c:when c:otherwise

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        <c:choose>
        <c:when test="${param.age < 18}">
        青少年
        </c:when>
        <c:when test="${param.age < 35}">
        青年
        </c:when>
        <c:when test="${param.age < 55}">
        中年
        </c:when>
        <c:otherwise>
        老年
        </c:otherwise>
        </c:choose>
  • session的实现原理中,每一个session对象都会关联一个sessionid,例如:

    • JSESSIONID=41C481F0224664BDB28E95081D23D5B8
    • 以上的这个键值对数据其实就是cookie对象。
    • 对于session关联的cookie来说,这个cookie是被保存在浏览器的“运行内存”当中。
    • 只要浏览器不关闭,用户再次发送请求的时候,会自动将运行内存中的cookie发送给服务器。
    • 服务器根据41C481F0224664BDB28E95081D23D5B8这个值来找到对应的session对象的。
  • cookie怎么生成?cookie保存在什么地方?cookie有啥用?浏览器什么时候会发送cookie,发送哪些cookie给服务器???????

  • cookie最终是保存在浏览器客户端上的。

    • 可以保存在运行内存中。(浏览器只要关闭cookie就消失了。)
    • 也可以保存在硬盘文件中。(永久保存。)
  • cookie有啥用呢?

    • cookie和session机制其实都是为了保存会话的状态。
    • cookie是将会话的状态保存在浏览器客户端上。(cookie数据存储在浏览器客户端上的。)
    • session是将会话的状态保存在服务器端上。(session对象是存储在服务器上。)
    • 为什么要有cookie和session机制呢?因为HTTP协议是无状态 无连接协议。
  • cookie的经典案例

    • 京东商城,在未登录的情况下,向购物车中放几件商品。然后关闭商城,再次打开浏览器,访问京东商城的时候,购物车中的商品还在,这是怎么做的?我没有登录,为什么购物车中还有商品呢?
      • 将购物车中的商品编号放到cookie当中,cookie保存在硬盘文件当中。这样即使关闭浏览器。硬盘上的cookie还在。下一次再打开京东商城的时候,查看购物车的时候,会自动读取本地硬盘中存储的cookie,拿到商品编号,动态展示购物车中的商品。
        • 京东存储购物车中商品的cookie可能是这样的:productIds=xxxxx,yyyy,zzz,kkkk
        • 注意:cookie如果清除掉,购物车中的商品就消失了。
    • 126邮箱中有一个功能:十天内免登录
      • 这个功能也是需要cookie来实现的。
      • 怎么实现的呢?
        • 用户输入正确的用户名和密码,并且同时选择十天内免登录。登录成功后。浏览器客户端会保存一个cookie,这个cookie中保存了用户名和密码等信息,这个cookie是保存在硬盘文件当中的,十天有效。在十天内用户再次访问126的时候,浏览器自动提交126的关联的cookie给服务器,服务器接收到cookie之后,获取用户名和密码,验证,通过之后,自动登录成功。
        • 怎么让cookie失效?
          • 十天过后自动失效。
          • 或者改密码。
          • 或者在客户端浏览器上清除cookie。
  • HTTP协议中规定:任何一个cookie都是由name和value组成的。name和value都是字符串类型的。

  • 在java的servlet中,对cookie提供了哪些支持呢?

    • 提供了一个Cookie类来专门表示cookie数据。jakarta.servlet.http.Cookie;
    • java程序怎么把cookie数据发送给浏览器呢?response.addCookie(cookie);
  • 在HTTP协议中是这样规定的:当浏览器发送请求的时候,会自动携带该path下的cookie数据给服务器。

  • 关于cookie的有效时间

    • 设置cookie的有效时间
      • cookie.setMaxAge(60 * 60); 设置cookie在一小时之后失效。
    • 没有设置有效时间:默认保存在浏览器的运行内存中,浏览器关闭则cookie消失。
    • 只要设置cookie的有效时间 > 0,到达指定时间后删除
    • 设置cookie的有效时间 = 0 ,表示马上删除 Cookie
    • 设置cookie的有效时间 < 0 ,表示浏览器一关,Cookie 就会被删除(默认值是-1)
  • 关于cookie的path,cookie关联的路径:

Session

什么是会话

  • 用户打开浏览器,进行一系列操作,然后最终将浏览器关闭,这个整个过程叫做:一次会话。会话在服务器端也有一个对应的java对象,这个java对象叫做:session

  • 什么是一次请求:用户在浏览器上点击了一下,然后到页面停下来,可以粗略认为是一次请求

  • 一个会话当中包含多次请求。(一次会话对应N次请求。)

  • 它是用来维护一个客户端和服务器之间关联的一种技术

  • 在java的servlet规范当中,session对应的类名:HttpSession(jarkata.servlet.http.HttpSession)

  • session机制属于B/S结构的一部分。如果使用php语言开发WEB项目,同样也是有session这种机制的。session机制实际上是一个规范。然后不同的语言对这种会话机制都有实现

为什么需要session对象来保存会话状态呢

  • 因为HTTP协议是一种无状态协议
  • 什么是无状态:请求的时候,B和S是连接的,但是请求结束之后,连接就断了。HTTP协议为什么要设计成这样?因为这样的无状态协议,可以降低服务器的压力。请求的瞬间是连接的,请求结束之后,连接断开,这样服务器压力小
  • 只要B和S断开了,那么关闭浏览器这个动作,服务器是不知道的

为什么不使用request对象保存会话状态?为什么不使用ServletContext对象保存会话状态?

  • request.setAttribute()存,request.getAttribute()取,ServletContext也有这个方法。request是请求域。ServletContext是应用域
  • request是一次请求一个对象
  • ServletContext对象是服务器启动的时候创建,服务器关闭的时候销毁,这个ServletContext对象只有一个
  • ServletContext对象的域太大
  • request请求域(HttpServletRequest) < session会话域(HttpSession) < application域(ServletContext)

session对象的实现原理。

  • HttpSession session = request.getSession();

  • 张三访问的时候获取的session对象就是张三的。李四访问的时候获取的session对象就是李四的。

  • session的实现原理:

    • JSESSIONID=xxxxxx 这个是以Cookie的形式保存在浏览器的内存中的。浏览器只要关闭。这个cookie就没有了。
    • session列表是一个Map,map的key是sessionid,map的value是session对象。
    • 用户第一次请求,服务器生成session对象,同时生成id,将id发送给浏览器。
    • 用户第二次请求,自动将浏览器内存中的id发送给服务器,服务器根据id查找session对象。
    • 关闭浏览器,内存消失,cookie消失,sessionid消失,会话等同于结束。
  • Cookie禁用了,session还能找到吗?

  • 销毁session对象:

    session.invalidate();

域对象

  • request(对应的类名:HttpServletRequest)
    • 请求域(请求级别的)
  • session(对应的类名:HttpSession)
    • 会话域(用户级别的)
  • application(对应的类名:ServletContext)
    • 应用域(项目级别的,所有用户共享的。)
  • 这三个域对象的大小关系
    • request < session < application
  • 他们三个域对象都有以下三个公共的方法:
    • setAttribute(向域当中绑定数据)
    • getAttribute(从域当中获取数据)
    • removeAttribute(删除域当中的数据)
  • 使用原则:尽量使用小的域。

image-20250618192425026

Filter过滤器

  • 当前的项目存在缺陷?

    • DeptServlet、EmpServlet、OrderServlet。每一个Servlet都是处理自己相关的业务。在这些Servlet执行之前都是需要判断用户是否登录了。如果用户登录了,可以继续操作,如果没有登录,需要用户登录。这段判断用户是否登录的代码是固定的,并且在每一个Servlet类当中都需要编写,显然代码没有得到重复利用。包括每一个Servlet都要解决中文乱码问题,也有公共的代码。这些代码目前都是重复编写,并没有达到复用。怎么解决这个问题?
      • 可以使用Servlet规范中的Filter过滤器来解决这个问题。
  • Filter是什么,有什么用,执行原理是什么?

    • Filter是过滤器。
    • Filter可以在Servlet这个目标程序执行之前添加代码。也可以在目标Servlet执行之后添加代码。之前之后都可以添加过滤规则。
    • 一般情况下,都是在过滤器当中编写公共代码。
  • 一个过滤器怎么写呢?

    • 第一步:编写一个Java类实现一个接口:jarkata.servlet.Filter。并且实现这个接口当中所有的方法。

      • init方法:在Filter对象第一次被创建之后调用,并且只调用一次。
      • doFilter方法:只要用户发送一次请求,则执行一次。发送N次请求,则执行N次。在这个方法中编写过滤规则。
      • destroy方法:在Filter对象被释放/销毁之前调用,并且只调用一次。
    • 第二步:在web.xml文件中对Filter进行配置。这个配置和Servlet很像。

      1
      2
      3
      4
      5
      6
      7
      8
      <filter>
      <filter-name>filter2</filter-name>
      <filter-class>com.bjpowernode.javaweb.servlet.Filter2</filter-class>
      </filter>
      <filter-mapping>
      <filter-name>filter2</filter-name>
      <url-pattern>*.do</url-pattern>
      </filter-mapping>
      • 或者使用注解:@WebFilter({“*.do”})
  • 注意:

    • Servlet对象默认情况下,在服务器启动的时候是不会新建对象的。
    • Filter对象默认情况下,在服务器启动的时候会新建对象。
    • Servlet是单例的。Filter也是单例的。(单实例。)
  • 目标Servlet是否执行,取决于两个条件:

    • 第一:在过滤器当中是否编写了:chain.doFilter(request, response); 代码。
    • 第二:用户发送的请求路径是否和Servlet的请求路径一致。
  • chain.doFilter(request, response); 这行代码的作用:

    • 执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet。
  • 注意:Filter的优先级,天生的就比Servlet优先级高。

    • /a.do 对应一个Filter,也对应一个Servlet。那么一定是先执行Filter,然后再执行Servlet。
  • 关于Filter的配置路径:

    • /a.do、/b.do、/dept/save。这些配置方式都是精确匹配。
    • /* 匹配所有路径。
    • *.do 后缀匹配。不要以 / 开始
    • /dept/* 前缀匹配。
  • 在web.xml文件中进行配置的时候,Filter的执行顺序是什么?

    • 依靠filter-mapping标签的配置位置,越靠上优先级越高。
  • 过滤器的调用顺序,遵循栈数据结构。

  • 使用@WebFilter的时候,Filter的执行顺序是怎样的呢?

    • 执行顺序是:比较Filter这个类名。
    • 比如:FilterA和FilterB,则先执行FilterA。
    • 比如:Filter1和Filter2,则先执行Filter1.
  • Filter的生命周期?

    • 和Servlet对象生命周期一致。
    • 唯一的区别:Filter默认情况下,在服务器启动阶段就实例化。Servlet不会。
  • Filter过滤器这里有一个设计模式:

    • 责任链设计模式。
    • 过滤器最大的优点:
      • 在程序编译阶段不会确定调用顺序。因为Filter的调用顺序是配置到web.xml文件中的,只要修改web.xml配置文件中filter-mapping的顺序就可以调整Filter的执行顺序。显然Filter的执行顺序是在程序运行阶段动态组合的。那么这种设计模式被称为责任链设计模式。
    • 责任链设计模式最大的核心思想:
      • 在程序运行阶段,动态的组合程序的调用顺序。

Listener监听器

  • 什么是监听器?

    • 监听器是Servlet规范中的一员。就像Filter一样。Filter也是Servlet规范中的一员。
    • 在Servlet中,所有的监听器接口都是以“Listener”结尾。
  • 监听器有什么用?

    • 监听器实际上是Servlet规范留给我们javaweb程序员的特殊时机。
    • 特殊的时刻如果想执行这段代码,你需要想到使用对应的监听器。
  • Servlet规范中提供了哪些监听器?

    • jakarta.servlet包下:
      • ServletContextListener
      • ServletContextAttributeListener
      • ServletRequestListener
      • ServletRequestAttributeListener
    • jakarta.servlet.http包下:
      • HttpSessionListener
      • HttpSessionAttributeListener
        • 该监听器需要使用@WebListener注解进行标注。
        • 该监听器监听的是什么?是session域中数据的变化。只要数据变化,则执行相应的方法。主要监测点在session域对象上。
      • HttpSessionBindingListener
        • 该监听器不需要使用@WebListener进行标注。
        • 假设User类实现了该监听器,那么User对象在被放入session的时候触发bind事件,User对象从session中删除的时候,触发unbind事件。
        • 假设Customer类没有实现该监听器,那么Customer对象放入session或者从session删除的时候,不会触发bind和unbind事件。
      • HttpSessionIdListener
        • session的id发生改变的时候,监听器中的唯一一个方法就会被调用。
      • HttpSessionActivationListener
        • 监听session对象的钝化和活化的。
        • 钝化:session对象从内存存储到硬盘文件。
        • 活化:从硬盘文件把session恢复到内存。
  • 实现一个监听器的步骤:以ServletContextListener为例。

    • 第一步:编写一个类实现ServletContextListener接口。并且实现里面的方法。

      1
      2
      void contextInitialized(ServletContextEvent event)
      void contextDestroyed(ServletContextEvent event)
    • 第二步:在web.xml文件中对ServletContextListener进行配置,如下:

      1
      2
      3
      <listener>
      <listener-class>com.bjpowernode.javaweb.listener.MyServletContextListener</listener-class>
      </listener>
      • 当然,第二步也可以不使用配置文件,也可以用注解,例如:@WebListener
  • 注意:所有监听器中的方法都是不需要javaweb程序员调用的,由服务器来负责调用?什么时候被调用呢?

    • 当某个特殊的事件发生(特殊的事件发生其实就是某个时机到了。)之后,被web服务器自动调用。
  • 思考一个业务场景:

    • 请编写一个功能,记录该网站实时的在线用户的个数。
    • 我们可以通过服务器端有没有分配session对象,因为一个session代表了一个用户。有一个session就代表有一个用户。如果你采用这种逻辑去实现的话,session有多少个,在线用户就有多少个。这种方式的话:HttpSessionListener够用了。session对象只要新建,则count++,然后将count存储到ServletContext域当中,在页面展示在线人数即可。
    • 业务发生改变了,只统计登录的用户的在线数量,这个该怎么办?
      • session.setAttribute(“user”, userObj);
      • 用户登录的标志是什么?session中曾经存储过User类型的对象。那么这个时候可以让User类型的对象实现HttpSessionBindingListener监听器,只要User类型对象存储到session域中,则count++,然后将count++存储到ServletContext对象中。页面展示在线人数即可。
  • 实现oa项目中当前登录在线的人数。

    • 什么代表着用户登录了?
      • session.setAttribute(“user”, userObj); User类型的对象只要往session中存储过,表示有新用户登录。
    • 什么代表着用户退出了?
      • session.removeAttribute(“user”); User类型的对象从session域中移除了。
      • 或者有可能是session销毁了。(session超时)