目录
Servlet
01 系统架构
1.1 系统架构包括什么形式?
1.2 C/S架构
1.3 B/S架构
1.4 C/S和B/S结构的系统如何选择?
1.5 开发一个WEB系统需要哪些技术?
1.6 Java EE是什么?
02 B/S结构的系统通信原理
2.1 WEB系统的访问过程
2.2 域名
2.3 IP地址
2.4 端口号
2.5 WEB系统的通信原理
2.6 URL
2.7 请求、响应
03 WEB服务器软件
3.1 WEB服务器软件都有哪些呢?
3.2 应用服务器和WEB服务器的关系
3.3 Tomcat下载
04 实现一个最基本的web应用
4.1 实现web应用的步骤
4.2 为何不使用超链接访问?
05 动态web应用的一个请求和响应过程中的角色和协议
5.1 在整个BS结构的系统当中,有哪些角色?
5.2 角色和角色之间需要遵守哪些规范,哪些协议?
5.3 Servlet规范
06 模拟Servlet本质
6.1 充当SUN公司的角色,制定Servlet规范
6.2 充当Tomcat服务器的开发者
6.3 充当Webapp的开发者
6.4 通过我们的分析:
6.5 Servlet规范规定了什么?
07 开发带有Servlet的webapp
7.1 开发步骤
08 关于JavaEE的版本
09 解决Tomcat服务器在DOS命令窗口中的乱码问题(控制台乱码)
10 向浏览器响应一段HTML代码
11 在Servlet中连接数据库
12 集成开发环境开发Servlet程序
12.1 集成开发工具
12.2 使用IDEA中开发Servlet
13 Servlet对象的生命周期
13.1 什么是Servlet对象生命周期?
13.2 Servlet对象是由谁来维护的?
13.3 我们自己new的Servlet对象受WEB容器的管理吗?
13.4 Servlet对象生命周期
14 GenericServlet
14.1 编写一个Servlet类直接实现Servlet接口有什么缺点?
14.2 适配器设计模式Adapter
14.3 如何编写GenericServlet抽象类?
14.4 怎么改造GenericServlet类更利于子类程序的编写?
15 ServletConfig
15.1 什么是ServletConfig?
15.2 ServletConfig接口中常用的方法
16 ServletContext
16.1 什么是ServletContext?
16.2 形象描述ServletContext
16.3 ServletContext是一个接口
16.4 ServletContext接口中常用的方法
16.5 ServletContext对象的使用
17 缓存机制
18 HTTP协议
18.1 什么是协议?
18.2 什么是HTTP协议?
18.3 HTTP的请求协议(B --> S)
18.4 HTTP的响应协议(S --> B)
18.5 怎么查看的协议内容?
18.6 怎么向服务器发送GET请求、POST请求?
18.7 GET请求和POST请求有什么区别?
18.8 GET请求和POST请求如何选择?
18.9 get和post请求的格式统一
19 模板方法设计模式
19.1 什么是设计模式?
19.2 有哪些设计模式?
19.3 什么是模板方法设计模式?
20 HttpServlet源码分析
20.1 HttpServlet在哪个包下?
20.2 servlet规范中的接口
20.3 http包下都有哪些类和接口?
20.4 HttpServletRequest对象中封装了什么信息?
20.5 Servlet生命周期
20.6 HttpServlet源码分析
20.7 我们编写的类直接继承HttpServlet,直接重写HttpServlet类中的service()方法行吗?
20.8 Servlet类的开发步骤:
21 web站点的欢迎页面
21.1 什么是一个web站点的欢迎页面?
21.2 怎么设置欢迎页面?
21.3 一个webapp是可以设置多个欢迎页面的
21.4 Tomca默认配置了index.html
21.6 欢迎页可以是一个Servlet吗?
22 WEB-INF目录
23 HttpServletRequest接口详解
23.1 关于HttpServletRequest接口
23.2 HttpServletRequest接口的实现类谁写的? HttpServletRequest对象是谁给创建的?
23.3 HttpServletRequest对象中都有什么信息?都包装了什么信息?
23.4 request和response对象的生命周期
23.5 HttpServletRequest接口中常用的方法
23.6 应用域与请求域
23.7 两个Servlet如何共享数据?
23.8 转发的下一个资源不一定是Servlet
23.9 HttpServletRequest接口的其他常用方法:
24 使用纯Servlet做一个单表的CRUD操作页面
实现步骤:
第一步:准备一张数据库表。(sql脚本)
第二步:准备一套HTML页面(项目原型)
第三步:分析我们这个系统包括哪些功能?
第四步:在IDEA当中搭建开发环境
第五步:查看部门列表
第六步:查看部门详情。
第七步:删除部门
第八步:新增部门
第九步:跳转到修改部门的页面
第十步:修改部门
25 转发和重定向
25.1 转发和重定向的区别
25.2 转发和重定向应该如何选择?
25.3 跳转的下一个资源有没有要求呢?必须是一个Servlet吗?
25.4 转发会存在浏览器的刷新问题。
26 Servlet注解简化配置
26.1 分析oa项目中的web.xml文件
26.2 注解式开发的优点
26.3 web.xml文件何时用?
26.4 如何书写注解?
26.5 注解对象的使用格式
27 使用模板方法设计模式优化oa项目
28 分析使用纯粹Servlet开发web应用的缺陷
28.1 存在什么问题?
28.2 怎么解决这些问题?
29 session会话机制
29.1 关于会话
29.2 为什么需要session对象来保存会话状态呢?
29.3 为什么不使用request对象保存会话状态?为什么不使用ServletContext对象保存会话状态?
29.4 session对象的实现原理
29.5 Cookie禁用
29.6 域对象
29.7 解决oa项目中的登录问题,让登录起作用。
29.8 会话结束
29.9 销毁session对象
30 Cookie
31 乱码问题
31.1 总结解决中文数据库乱码或者页面显示乱码问题
31.2 request和response的乱码解决
31.3 解决doc命令窗口乱码和IDEA工具控制台乱码问题
JSP
01 关于JSP
1.1 第一个JSP程序
1.2 JSP的本质
1.3 jsp文件第一次访问的时候为什么比较慢?
1.4 JSP是什么?
1.6 JSP和Servlet的区别
02 JSP的基础语法
2.1 jsp文件内容都会自动被翻译到哪里?
2.2 JSP的page指令,解决响应时的中文乱码问题:
2.2 怎么在JSP中编写Java程序:
2.3 在JSP中如何编写JSP的专业注释
2.4 JSP基础语法总结:
03 使用Servlet + JSP完成oa项目的改造。
04 当前的oa应用存在的问题:
05 JSP的指令
06 JSP的九大内置对象
EL表达式
01 关于EL表达式
02 EL表达式在JSP中的作用:
03 EL表达式的使用
3.1 EL表达式基本的语法格式及简单使用
3.2 ${abc} 和 ${“abc”}的区别是什么?
3.3 ${userObj} 底层是怎么做的?
3.4 如何输出对象的属性值?
3.5 EL表达式优先从小范围中读取数据。
3.6 EL表达式中有四个隐含的隐式的范围:
3.7 EL表达式取数据的时候有两种形式:
3.8 EL表达式怎么从集合和数组中取数据?
3.9 page指令中忽略EL表达式的属性
3.10 EL表达式中其他的隐式对象:
3.11 EL表达式的运算符
JSTL标签库
01 什么是JSTL标签库?
02 使用JSTL标签库的步骤:
2.1 第一步:引入JSTL标签库对应的jar包。
2.2 第二步:在JSP中引入要使用标签库。(使用taglib指令引入标签库。)
2.3 第三步:在需要使用标签的位置使用即可。
03 JSTL标签的原理
04 jstl中的核心标签库core当中常用的标签
04 Servlet + JSP + EL表达式 + JSTL标签改造OA
Filter过滤器
01 关于Filter
02 过滤器如何编写?
03 如何执行下一个过滤器或者执行Servlet
04 关于Filter的配置路径:
05 在web.xml文件中进行配置的时候,Filter的执行顺序是什么?
06 使用@WebFilter的时候,Filter的执行顺序
07 Filter的生命周期
08 Filter过滤器的设计模式:
Listener监听器
01 什么是监听器?
02 监听器有什么用?
03 Servlet规范中提供了哪些监听器?
04 实现一个监听器的步骤
05 业务场景:记录该网站实时的在线用户的个数
Client / Server(客户端 / 服务器)
C/S架构的软件或者说系统有哪些呢?
QQ(先去腾讯官网下载一个QQ软件,几十MB,然后把这个客户端软件安装上去,然后输入QQ号以及密码,登录之后,就可以和你的朋友聊天了,就可以使用这个软件了。)
C/S架构的特点:需要安装特定的客户端软件。
C/S架构的系统优点和缺点分别是什么?
B/S(Browser / Server,浏览器 / 服务器)
B/S结构的系统是一个特殊的C/S系统
B/S结构的系统优点和缺点是:
优点:
缺点:
C/S和B/S结构并不是哪个好,哪个不好。不同结构的系统在不同的业务场景下有不同的适用场景。
娱乐性软件建议使用?
公司内部使用的一些业务软件建议使用?
开发B/S结构的系统,其实就是开发网站,其实就是开发一个WEB系统。
Java包括三大块:
JavaSE
JavaEE(WEB方向,WEB系统。)
JavaME
JavaEE实际上包括很多种规范,13种规范,其中Servlet就是JavaEE规范之一。学Servlet还是Java语言。
通信步骤:
这些软件都是提前开发好的。
3.3.1 关于Tomcat
3.3.2 配置java运行环境
Tomcat服务器要想运行,需要先有jre,所以要先安装JDK,配置java运行环境。
3.3.3 Tomcat服务器的安装
3.3.4 启动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文件,说明了这个tomcat服务器的通用性。
分析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目录是不行的。有两个环境变量需要配置:
3.3.5 Tomcat服务器的目录
3.3.6 配置Tomcat服务器需要的环境变量
3.3.7 Tomcat命令
启动Tomcat: startup
关闭Tomcat:stop (shutdown.bat文件重命名为stop.bat,为什么?原因是shutdown命令和windows中的关机命令冲突。所以修改一下。)
3.3.8 测试Tomcat服务器是否启动成功
打开浏览器,在浏览器的地址栏上输入URL即可:
第一步:找到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,然后回车。这个动作和超链接一样吗?既然是一样的,我们完全可以使用超链接。
user login2
d page
http://127.0.0.1:8080/oa/userList.html
访问这个地址,可以展示一个用户列表页面。但是这个用户列表页面是写死在HTML文件当中的。这种资源我们称为静态资源。怎么能变成动态资源。显然需要连接数据库。
连接数据库需要JDBC程序,也就是说需要编写Java程序连接数据库,数据库中有多少条记录,页面上就显示多少条记录,这种技术被称为动态网页技术。(动态网页技术并不是说页面中有flash动画。动态网页技术是说页面中的数据是动态的,根据数据库中数据的变化而变化。)
webapp的开发团队 和 WEB Server的开发团队 之间有一套规范: JavaEE规范之一Servlet规范。
Servlet规范的作用是什么?
WEB Server 和 webapp解耦合。
Browser 和 WebServer之间有一套传输协议:HTTP协议。(超文本传输协议。)
webapp开发团队 和 DB Server的开发团队之间有一套规范:JDBC规范。
javax.servlet.Servlet接口
package javax.servlet;
/*
我们现在充当的角色是SUN公司。
SUN公司把Servlet接口/规范制定出来了。
*/
public interface Servlet{
// 一个专门提供服务的方法
void service();
}
package org.apache;
import java.util.Scanner;
import java.util.Properties;
import java.io.FileReader;
import javax.servlet.Servlet;
// 充当Tomcat服务器的开发者
public class Tomcat{
public static void main(String[] args) throws Exception{
System.out.println("Tomcat服务器启动成功,开始接收用户的访问。");
// 简单的使用Scanner来模拟一下用户的请求
// 用户访问服务器是通过浏览器上的“请求路径”
// 也就是说用户请求路径不同,后台执行的Servlet不同。
/*
/userList UserListServlet
/login UserLoginServlet
/bank BankServlet
......
*/
System.out.print("请输入您的访问路径:");
Scanner s = new Scanner(System.in);
// 用户的请求路径 /bbbb
String key = s.nextLine(); // Tomcat服务器已经获取到了用户的请求路径了。
// Tomcat服务器应该通过用户的请求路径找对应的XXXServlet
// 请求路径和XXXServlet之间的关系应该由谁指定呢?
// 对于Tomcat服务器来说需要解析配置文件
FileReader reader = new FileReader("web.properties");
Properties pro = new Properties();
pro.load(reader);
reader.close();
// 通过key获取value
String className = pro.getProperty(key);
// 通过反射机制创建对象
Class clazz = Class.forName(className);
Object obj = clazz.newInstance(); // obj的类型对于Tomcat服务器开发人员来说不知道。
// 但是Tomcat服务器的开发者知道,你写的XXXXServlet一定实现了Servlet接口
Servlet servlet = (Servlet)obj;
servlet.service();
}
}
配置文件web.properties
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-irwkuzkM-1656924363018)(…/…/…/Pictures/JDBC/%E8%AF%B7%E6%B1%82%E8%B7%AF%E5%BE%84%E5%92%8CXXXServlet%E4%B9%8B%E9%97%B4%E5%BA%94%E8%AF%A5%E6%9C%89%E4%B8%80%E4%B8%AA%E5%AF%B9%E7%85%A7%E5%85%B3%E7%B3%BB.png)]
/aaaa=com.bjpowernode.servlet.UserListServlet
/bbbb=com.bjpowernode.servlet.UserLoginServlet
/cccc=com.bjpowernode.servlet.BankServlet
BankServlet implements Servlet
package com.bjpowernode.servlet;
import javax.servlet.Servlet;
// 充当的角色发生了改变:webapp开发者
// 只要是我们webapp开发者写的XXXServlet都要实现Servlet接口
public class BankServlet implements Servlet{
public void service(){
System.out.println("BankServlet's service...");
}
}
UserListServlet implements Servlet
package com.bjpowernode.servlet;
import javax.servlet.Servlet;
// 充当的角色发生了改变:webapp开发者
public class UserListServlet implements Servlet{
public void service(){
System.out.println("UserListServlet's service...");
}
}
UserLoginServlet implements Servlet
package com.bjpowernode.servlet;
import javax.servlet.Servlet;
// 充当的角色发生了改变:webapp开发者
public class UserLoginServlet implements Servlet{
public void service(){
System.out.println("UserLoginServlet's service...");
}
}
注意:
严格意义上来说Servlet其实并不是简单的一个接口
Tomcat服务器要遵循Servlet规范。
JavaWeb程序员也要遵循这个Servlet规范。这样Tomcat服务器和webapp才能解耦合。
第一步:在webapps目录下新建一个目录,起名crm(这个crm就是webapp的名字)。当然,也可以是其它项目,比如银行项目,可以创建一个目录bank,办公系统可以创建一个oa。
第二步:在webapp的根下新建一个目录:WEB-INF
第三步:在WEB-INF目录下新建一个目录:classes
第四步:在WEB-INF目录下新建一个目录:lib
第五步:在WEB-INF目录下新建一个文件:web.xml
注意:这个文件是必须的,这个文件名必须叫做web.xml。这个文件必须放在这里。一个合法的webapp,web.xml文件是必须的,这个web.xml文件就是一个配置文件,在这个配置文件中描述了请求路径和Servlet类之间的对照关系。
这个文件最好从其他的webapp中拷贝,最好别手写。没必要。复制粘贴
第六步:编写一个Java程序,这个小Java程序也不能随意开发,这个小java程序必须实现Servlet接口。
第七步:编译我们编写的HelloServlet
重点:你怎么能让你的HelloServlet编译通过呢?配置环境变量CLASSPATH
CLASSPATH=.;C:\dev\apache-tomcat-10.0.12\lib\servlet-api.jar
思考问题:以上配置的CLASSPATH和Tomcat服务器运行有没有关系?
package com.bjpowernode.servlet;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.ServletConfig;
import java.io.IOException;
import java.io.PrintWriter;
public class HelloServlet implements Servlet{
// 5个方法
public void init(ServletConfig config) throws ServletException{
}
public void service(ServletRequest request,ServletResponse response)
throws ServletException , IOException{
// 向控制台打印输出
System.out.println("My First Servlet, Hello Servlet");
// 设置响应的内容类型是普通文本或html代码
// 需要在获取流对象之前设置,有效。
response.setContentType("text/html");
// 怎么将一个信息直接输出到浏览器上?
// 需要使用ServletResponse接口:response
// response表示响应:从服务器向浏览器发送数据叫做响应。
PrintWriter out = response.getWriter();
// 设置响应的内容类型时不要在获取流之后设置。
//response.setContentType("text/html");
out.print("Hello Servlet, You are my first servlet!");
// 浏览器是能够识别html代码的,那我们是不是应该输出一段HTML代码呢?
out.print("hello servlet,你好Servlet
");
// 这是一个输出流,负责输出字符串到浏览器
// 这个输出流不需要我们刷新,也不需要我们关闭,这些都由Tomcat来维护。
/*
out.flush();
out.close();
*/
}
public void destroy(){
}
public String getServletInfo(){
return "";
}
public ServletConfig getServletConfig(){
return null;
}
}
第八步:将以上编译之后的HelloServlet.class文件拷贝到WEB-INF\classes目录下。
第九步:在web.xml文件中编写配置信息,让“请求路径”和“Servlet类名”关联在一起。
这一步用专业术语描述:在web.xml文件中注册Servlet类。
fdsafdsagfdsafdsa
com.bjpowernode.servlet.HelloServlet
fdsafdsagfdsafdsa
/fdsa/fd/saf/d/sa/fd/sa/fd
第十步:启动Tomcat服务器
第十一步:打开浏览器,在浏览器地址栏上输入一个url,这个URL必须是:
浏览器上编写的路径太复杂,可以使用超链接。(非常重要:html页面只能放到WEB-INF目录外面。)
以后不需要我们编写main方法了。tomcat服务器负责调用main方法,Tomcat服务器启动的时候执行的就是main方法。我们javaweb程序员只需要编写Servlet接口的实现类,然后将其注册到web.xml文件中,即可。
总结一下:一个合法的webapp目录结构应该是怎样的?
webapproot
|------WEB-INF
|------classes(存放字节码)
|------lib(第三方jar包)
|------web.xml(注册Servlet)
|------html
|------css
|------javascript
|------image
....
浏览器发送请求,到最终服务器调用Servlet中的方法,是怎样的一个过程?(以下这个过程描述的很粗糙。其中还有很多步骤我省略了。)
将CATALINA_HOME/conf/logging.properties文件中的内容修改如下:
java.util.logging.ConsoleHandler.encoding = GBK
public void service(ServletRequest request, ServletResponse response){
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print("hello servlet!
");
}
package com.bjpowernode.servlet;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.ServletConfig;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
public class StudentServlet implements Servlet{
public void init(ServletConfig config) throws ServletException{
}
public void service(ServletRequest request,ServletResponse response)
throws ServletException , IOException{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// 编写JDBC代码,连接数据库,查询所有学生信息。
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
// 注册驱动 (com.mysql.jdbc.Driver,这个已过时。)
// 新版本中建议使用:com.mysql.cj.jdbc.Driver驱动。
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取连接
String url = "jdbc:mysql://localhost:3306/bjpowernode";
String user = "root";
String password = "root";
conn = DriverManager.getConnection(url,user,password);
// 获取预编译的数据库操作对象
String sql = "select no,name from t_student";
ps = conn.prepareStatement(sql);
// 执行SQL
rs = ps.executeQuery();
// 处理查询结果集
while(rs.next()){
String no = rs.getString("no");
String name = rs.getString("name");
//System.out.println(no + "," + name);
out.print(no + "," + name + "
");
}
}catch(Exception e){
e.printStackTrace();
}finally{
// 释放资源
if(rs != null){
try{
rs.close();
}catch(Exception e){
e.printStackTrace();
}
}
if(ps != null){
try{
ps.close();
}catch(Exception e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
public void destroy(){
}
public String getServletInfo(){
return "";
}
public ServletConfig getServletConfig(){
return null;
}
}
第一步:New Project(我比较习惯先创建一个Empty Project【空工程】,然后在空工程下新建Module【模块】,这不是必须的,只是一种习惯,你可以直接新建非空的Project),这个Empty Project起名为:javaweb(不是必须的,只是一个名字而已。一般情况下新建的Project的名字最好和目录的名字一致。)
第二步:新建模块(File --> new --> Module…)
第三步:让Module变成JavaEE的模块。(让Module变成webapp的模块。符合webapp规范。符合Servlet规范的Module)
第四步(非必须):根据Web Application生成的资源中有index.jsp文件,这里我选择删除这个index.jsp文件。
第五步:编写Servlet(StudentServlet)
第六步:在Servlet当中的service方法中编写业务代码(我们这里连接数据库了。)
第七步:在WEB-INF目录下新建了一个子目录:lib(这个目录名可不能随意,必须是全部小写的lib),并且将连接数据库的驱动jar包放到lib目录下。
第八步:在web.xml文件中完成StudentServlet类的注册。(请求路径和Servlet之间对应起来)
studentServlet
com.bjpowernode.javaweb.servlet.StudentServlet
studentServlet
/servlet/student
第九步:给一个html页面,在HTML页面中编写一个超链接,用户点击这个超链接,发送请求,Tomcat执行后台的StudentServlet。
student.html
这个文件不能放到WEB-INF目录里面,只能放到WEB-INF目录外面。
student.html文件的内容
student page
student list
第十步:让IDEA工具去关联Tomcat服务器。关联的过程当中将webapp部署到Tomcat服务器当中。
第十一步:启动Tomcat服务器
第十二步:打开浏览器,在浏览器地址栏上输入:http://localhost:8080/xmm/student.html
13.3.1 默认情况下,服务器启动后,Servlet对象有没有被创建出来?
13.3.2 怎么让服务器启动的时候创建Servlet对象呢?
在servlet标签中添加子标签,在该子标签中填写整数,越小的整数优先级越高。
aservlet
com.bjpowernode.javaweb.servlet.AServlet
1
aservlet
/a
13.4.1 Servlet中的init、service、destroy方法
用户发送第一次请求的时候,控制台输出了以下内容:
AServlet无参数构造方法执行了
AServlet's init method execute!
AServlet's service method execute!
根据以上输出内容得出结论:
用户继续发送第二次请求,控制台输出了以下内容:
AServlet's service method execute!
根据以上输出结果得知,用户在发送第二次,或者第三次,或者第四次请求的时候,Servlet对象并没有新建,还是使用之前创建好的Servlet对象,直接调用该Servlet对象的service方法,这说明:
关闭服务器的时候,控制台输出了以下内容:
AServlet's destroy method execute!
通过以上输出内容,可以得出以下结论:
请问:destroy方法调用的时候,对象销毁了还是没有销毁呢?
13.4.2 Servlet对象的历程
Servlet对象更像一个人的一生:
13.4.3 Servlet类中方法的调用次数
13.4.4 当我们Servlet类中编写一个有参数的构造方法,如果没有手动编写无参数构造方法会出现什么问题?
13.4.5 Servlet的无参数构造方法可以代替掉init方法吗?
Servlet的无参数构造方法是在对象第一次创建的时候执行,并且只执行一次。init方法也是在对象第一次创建的时候执行,并且只执行一次。这个无参数构造方法不可以代替掉init方法
Servlet规范中有要求,作为javaweb程序员,编写Servlet类的时候,不建议手动编写构造方法,因为编写构造方法,很容易让无参数构造方法消失,这个操作可能会导致Servlet对象无法实例化。所以init方法是有存在的必要的。
13.4.6 init、service、destroy方法中使用最多的是哪个方法?
13.4.7 什么时候使用destroy方法呢?
编写一个GenericServlet抽象类,其中有一个抽象方法service
思考第一个问题:我提供了一个GenericServlet之后,init方法还会执行吗?
思考第二个问题:init方法是谁调用的?
思考第三个问题:init方法中的ServletConfig对象是谁创建的?是谁传过来的?
思考一下Tomcat服务器伪代码:
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方法
// ....
}
}
在GenericServlat类当中添加成员变量ServletConfig,在init方法内,初始化成员变量ServletConfig(this->ServletConfig = ServletConfig)
如果说想要在子类当中重写GenericServlat父类的init方法会导致父类init的方法不再执行,也就是说初识化操作无法完成,那么这些如何做呢?
父类中写init(无参)方法的空实现,子类重写init(无参)的方法,而在父类的init(有参)方法中调用无参的init方法,即可解决以上问题
GenericServlet实现了ServletConfig 和 Servlet接口
Servlet对象的配置信息对象。
ServletConfig对象中封装了标签中的配置信息。(web.xml文件中servlet的配置信息)
一个Servlet对应一个ServletConfig对象。
Servlet对象是Tomcat服务器创建,并且ServletConfig对象也是Tomcat服务器创建。并且默认情况下,他们都是在用户发送第一次请求的时候创建。
Tomcat服务器调用Servlet对象的init方法的时候需要传一个ServletConfig对象的参数给init方法。
ServletConfig接口的实现类是Tomcat服务器给实现的。(Tomcat服务器说的就是WEB服务器。)
public String getInitParameter(String name); // 通过初始化参数的name获取value
public Enumeration getInitParameterNames(); // 获取所有的初始化参数的name
public ServletContext getServletContext(); // 获取ServletContext对象
public String getServletName(); // 获取Servlet的name
以上方法在Servlet类当中,(在GenericServlet适配器当中也可以)都可以使用this去调用。因为GenericServlet实现了ServletConfig接口。
**详解:**可以通过
public String getInitParameter(String name); // 通过初始化参数的name获取value
这个方法来获取初始化配置信息,参数传入key,返回得到value
如果是这个方法
public Enumeration getInitParameterNames(); // 获取所有的初始化参数的name
得到的是一个name集合,通过遍历的方式也可以拿到
configTest2
com.bjpowernode.javaweb.servlet.ConfigTestServlet2
---------------这里是初始化配置信息-------------------
key
value
user
root
password
123456
---------------这里是初始化配置信息-------------------
configTest2
/test2
一个Servlet对象对应一个ServletConfig。100个Servlet对象则对应100个ServletConfig对象。
只要在同一个webapp当中,只要在同一个应用当中,所有的Servlet对象都是共享同一个ServletContext对象的。
ServletContext对象在服务器启动阶段创建,在服务器关闭的时候销毁。这就是ServletContext对象的生命周期。ServletContext对象是应用级对象。
Tomcat服务器中有一个webapps,这个webapps下可以存放webapp,可以存放多个webapp,假设有100个webapp,那么就有100个ServletContext对象。但是,总之,一个应用,一个webapp肯定是只有一个ServletContext对象。
ServletContext被称为Servlet上下文对象。(Servlet对象的四周环境对象。)
一个ServletContext对象通常对应的是一个web.xml文件。
一个教室里有多个学生,那么每一个学生就是一个Servlet,这些学生都在同一个教室当中,那么我们可以把这个教室叫做ServletContext对象。那么也就是说放在这个ServletContext对象(环境)当中的数据,在同一个教室当中,物品都是共享的。比如:教室中有一个空调,所有的学生都可以操作。可见,空调是共享的。因为空调放在教室当中。教室就是ServletContext对象。
16.4.1 通过初始化参数的name获取value
public String getInitParameter(String name); // 通过初始化参数的name获取value
public Enumeration getInitParameterNames(); // 获取所有的初始化参数的name
以上两个方法是ServletContext对象的方法,这个方法获取的是什么信息?是以下的配置信息
pageSize
10
startIndex
0
注意:以上的配置信息属于应用级的配置信息,一般一个项目中共享的配置信息会放到以上的标签当中。
如果你的配置信息只是想给某一个servlet作为参考,那么你配置到servlet标签当中即可,使用ServletConfig对象来获取。
16.4.2 获取文件的绝对路径(真实路径)
public String getContextPath();
//String contextPath = application.getContextPath();
// 获取文件的绝对路径(真实路径)
public String getRealPath(String path);
16.4.3 记录日志
通过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 访问日志
16.4.4 向ServletContext应用域中存取删
// 存(怎么向ServletContext应用域中存数据)
public void setAttribute(String name, Object value); // map.put(k, v)
// 取(怎么从ServletContext应用域中取数据)
public Object getAttribute(String name); // Object v = map.get(k)
// 删(怎么删除ServletContext应用域中的数据)
public void removeAttribute(String name); // map.remove(k)
ServletContext对象还有另一个名字:应用域(后面还有其他域,例如:请求域、会话域)
如果所有的用户共享一份数据,并且这个数据很少的被修改,并且这个数据量很少,可以将这些数据放到ServletContext这个应用域中
为什么是所有用户共享的数据?
不是共享的没有意义。因为ServletContext这个对象只有一个。只有共享的数据放进去才有意义。
为什么数据量要小?
因为数据量比较大的话,太占用堆内存,并且这个对象的生命周期比较长,服务器关闭的时候,这个对象才会被销毁。大数据量会影响服务器的性能。占用内存较小的数据量可以考虑放进去。
为什么这些共享数据很少的修改,或者说几乎不修改?
所有用户共享的数据,如果涉及到修改操作,必然会存在线程并发所带来的安全问题。所以放在ServletContext对象中的数据一般都是只读的。
数据量小、所有用户共享、又不修改,这样的数据放到ServletContext这个应用域当中,会大大提升效率。因为应用域相当于一个缓存,放到缓存中的数据,下次在用的时候,不需要从数据库中再次获取,大大提升执行效率。
// 存(怎么向ServletContext应用域中存数据)
public void setAttribute(String name, Object value); // map.put(k, v)
// 取(怎么从ServletContext应用域中取数据)
public Object getAttribute(String name); // Object v = map.get(k)
// 删(怎么删除ServletContext应用域中的数据)
public void removeAttribute(String name); // map.remove(k)
注意:以后我们编写Servlet类的时候,实际上是不会去直接继承GenericServlet类的,因为我们是B/S结构的系统,这种系统是基于HTTP超文本传输协议的,在Servlet规范当中,提供了一个类叫做HttpServlet,它是专门为HTTP协议准备的一个Servlet类。我们编写的Servlet类要继承HttpServlet。(HttpServlet是HTTP协议专用的。)使用HttpServlet处理HTTP协议更便捷。但是你需要直到它的继承结构:
jakarta.servlet.Servlet(接口)【爷爷】
jakarta.servlet.GenericServlet implements Servlet(抽象类)【儿子】
jakarta.servlet.http.HttpServlet extends GenericServlet(抽象类)【孙子】
我们以后编写的Servlet要继承HttpServlet类。
堆内存当中的字符串常量池。
堆内存当中的整数型常量池。
连接池(Connection Cache)
线程池
redis
向ServletContext应用域中存储数据,也等于是将数据存放到缓存cache当中了。
HTTP协议
W3C
超文本
解耦合
这种协议游走在B和S之间。B向S发数据要遵循HTTP协议。S向B发数据同样需要遵循HTTP协议。这样B和S才能解耦合。
什么是解耦合?
请求和响应
HTTP协议包括请求协议和响应协议
HTTP协议包括:
HTTP协议就是提前制定好的一种消息模板。
HTTP的请求协议包括:4部分
HTTP请求协议的具体报文:GET请求
GET /servlet05/getServlet?username=lucy&userpwd=1111 HTTP/1.1 请求行
Host: localhost:8080 请求头
Connection: keep-alive
sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/servlet05/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
空白行
请求体
HTTP请求协议的具体报文:POST请求
POST /servlet05/postServlet HTTP/1.1 请求行
Host: localhost:8080 请求头
Connection: keep-alive
Content-Length: 25
Cache-Control: max-age=0
sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/servlet05/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
空白行
username=lisi&userpwd=123 请求体
请求行
请求头
空白行
请求体
HTTP的响应协议包括:4部分
HTTP响应协议的具体报文:
HTTP/1.1 200 ok 状态行
Content-Type: text/html;charset=UTF-8 响应头
Content-Length: 160
Date: Mon, 08 Nov 2021 13:19:32 GMT
Keep-Alive: timeout=20
Connection: keep-alive
空白行
响应体
from get servlet
from get servlet
状态行
响应头:
空白行:
响应体:
get请求发送数据的时候,数据会挂在URI的后面,并且在URI后面添加一个“?”,"?"后面是数据。这样会导致发送的数据回显在浏览器的地址栏上。(get请求在“请求行”上发送数据)
http://localhost:8080/servlet05/getServlet?username=zhangsan&userpwd=1111
post请求发送数据的时候,在请求体当中发送。不会回显到浏览器的地址栏上。也就是说post发送的数据,在浏览器地址栏上看不到。(post在“请求体”当中发送数据)
get请求只能发送普通的字符串。并且发送的字符串长度有限制,不同的浏览器限制不同。这个没有明确的规范。
get请求无法发送大数据量。
post请求可以发送任何类型的数据,包括普通字符串,流媒体等信息:视频、声音、图片。
post请求可以发送大数据量,理论上没有长度限制。
get请求在W3C中是这样说的:get请求比较适合从服务器端获取数据。
post请求在W3C中是这样说的:post请求比较适合向服务器端传送数据。
get请求是安全的。get请求是绝对安全的。为什么?因为get请求只是为了从服务器上获取数据。不会对服务器造成威胁。(get本身是安全的,你不要用错了。用错了之后又冤枉人家get不安全,你这样不好(太坏了),那是你自己的问题,不是get请求的问题。)
post请求是危险的。为什么?因为post请求是向服务器提交数据,如果这些数据通过后门的方式进入到服务器当中,服务器是很危险的。另外post是为了提交数据,所以一般情况下拦截请求的时候,大部分会选择拦截(监听)post请求。
get请求支持缓存。
post请求不支持缓存。(POST是用来修改服务器端的资源的。)
什么时候使用GET请求,什么时候使用POST请求?
不管你是get请求还是post请求,发送的请求数据格式是完全相同的,只不过位置不同,格式都是统一的:
GoF设计模式:
JavaEE设计模式:
在模板类的模板方法当中定义核心算法骨架,具体的实现步骤可以延迟到子类当中完成。
模板类通常是一个抽象类,模板类当中的模板方法定义核心算法,这个方法通常是final的(但也可以不是final的)
模板类当中的抽象方法就是不确定实现的方法,这个不确定怎么实现的事儿交给子类去做。
HttpServlet类是专门为HTTP协议准备的。比GenericServlet更加适合HTTP协议下的开发。
jakarta.servlet.http.*;
HttpServletResponse对象是专门用来响应HTTP协议到浏览器的。
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方法好了。这样代码还能少写一点。
对于一个webapp来说,我们是可以设置它的欢迎页面的。设置了欢迎页面之后,当你访问这个webapp的时候,或者访问这个web站点的时候,没有指定任何“资源路径”,这个时候会默认访问你的欢迎页面。
我们一般的访问方式是:
如果我们访问的方式是:
第一步:我在IDEA工具的web目录下新建了一个文件login.html
第二步:在web.xml文件中进行了以下的配置
login.html
注意:设置欢迎页面的时候,这个路径不需要以“/”开始。并且这个路径默认是从webapp的根下开始查找。
第三步:启动服务器,浏览器地址栏输入地址
如果在webapp的根下新建一个目录,目录中再给一个文件,那么这个欢迎页该如何设置呢?
在webapp根下新建page1
在page1下新建page2目录
在page2目录下新建page.html页面
在web.xml文件中应该这样配置
page1/page2/page.html
注意:路径不需要以“/”开始,并且路径默认从webapp的根下开始找。
page1/page2/page.html
login.html
注意:越靠上的优先级越高。找不到的继续向下找。
当我的文件名设置为index.html的时候,不需要在web.xml文件中进行配置欢迎页面。这是为什么?
这是因为小猫咪Tomcat服务器已经提前配置好了。
实际上配置欢迎页面有两个地方可以配置:
一个是在webapp内部的web.xml文件中。(在这个地方配置的属于局部配置)
一个是在CATALINA_HOME/conf/web.xml文件中进行配置。(在这个地方配置的属于全局配置)
index.html
index.htm
index.jsp
Tomcat服务器的全局欢迎页面是:index.html index.htm index.jsp。如果你一个web站点没有设置局部的欢迎页面,Tomcat服务器就会以index.html index.htm index.jsp作为一个web站点的欢迎页面。
注意原则:局部优先原则。(就近原则)
当然可以。
你不要多想,欢迎页就是一个资源,既然是一个资源,那么可以是静态资源,也可以是动态资源。
静态资源:index.html welcome.html …
动态资源:Servlet类。
步骤:
第一步:写一个Servlet
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("welcome to bjpowernode!
");
}
}
第二步:在web.xml文件中配置servlet
welcomeServlet
com.bjpowernode.javaweb.servlet.WelcomeServlet
welcomeServlet
/fdsa/fds/a/fds/af/ds/af/dsafdsafdsa
第三步:在web.xml文件中配置欢迎页
fdsa/fds/a/fds/af/ds/af/dsafdsafdsa
HttpServletRequest是一个接口,全限定名称:jakarta.servlet.http.HttpServletRequest
HttpServletRequest接口是Servlet规范中的一员。
HttpServletRequest接口的父接口:ServletRequest
public interface HttpServletRequest extends ServletRequest {}
通过测试:org.apache.catalina.connector.RequestFacade 实现了 HttpServletRequest接口
public class RequestFacade implements HttpServletRequest {}
测试结果说明:Tomcat服务器(WEB服务器、WEB容器)实现了HttpServletRequest接口,还是说明了Tomcat服务器实现了Servlet规范。而对于我们javaweb程序员来说,实际上不需要关心这个,我们只需要面向接口编程即可。我们关心的是HttpServletRequest接口中有哪些方法,这些方法可以完成什么功能!!!!
23.5.1 怎么获取前端浏览器用户提交的数据?
Map getParameterMap() 这个是获取Map
Enumeration getParameterNames() 这个是获取Map集合中所有的key
String[] getParameterValues(String name) 根据key获取Map集合的value
String getParameter(String name) 获取value这个一维数组当中的第一个元素。这个方法最常用。
// 以上的4个方法,和获取用户提交的数据有关系。
思考:如果是你,前端的form表单提交了数据之后,你准备怎么存储这些数据,你准备采用什么样的数据结构去存储这些数据呢?
前端提交的数据格式:username=abc&userpwd=111&aihao=s&aihao=d&aihao=tt
我会采用Map集合来存储:
Map
key存储String
value存储String
这种想法对吗?不对。
如果采用以上的数据结构存储会发现key重复的时候value覆盖。
key value
---------------------
username abc
userpwd 111
aihao s
aihao d
aihao tt
这样是不行的,因为map的key不能重复。
Map
key存储String
value存储String[]
key value
-------------------------------
username {"abc"}
userpwd {"111"}
aihao {"s","d","tt"}
注意:前端表单提交数据的时候,假设提交了120这样的“数字”,其实是以字符串"120"的方式提交的,所以服务器端获取到的一定是一个字符串的"120",而不是一个数字。(前端永远提交的是字符串,后端获取的也永远是字符串。)
23.5.2 手工开发一个webapp。测试HttpServletRequest接口中的相关方法。
先测试了4个常用的方法,获取请求参数的四个方法。
Map parameterMap = request.getParameterMap();
Enumeration names = request.getParameterNames();
String[] values = request.getParameterValues("name");
String value = request.getParameter("name");
23.6.1 应用域对象
ServletContext (Servlet上下文对象。)
什么情况下会考虑向ServletContext这个应用域当中绑定数据呢?
你见过哪些缓存技术呢?
ServletContext当中有三个操作域的方法:
void setAttribute(String name, Object obj); // 向域当中绑定数据。
Object getAttribute(String name); // 从域当中根据name获取数据。
void removeAttribute(String name); // 将域当中绑定的数据移除
// 以上的操作类似于Map集合的操作。
Map map;
map.put("name", obj); // 向map集合中放key和value
Object obj = map.get("name"); // 通过map集合的key获取value
map.remove("name"); // 通过Map集合的key删除key和value这个键值对。
23.6.2 “请求域”对象
request对象实际上又称为“请求域”对象。
“请求域”对象要比“应用域”对象范围小很多。生命周期短很多。请求域只在一次请求内有效。
一个请求对象request对应一个请求域对象。一次请求结束之后,这个请求域就销毁了。
请求域对象也有这三个方法:
void setAttribute(String name, Object obj); // 向域当中绑定数据。
Object getAttribute(String name); // 从域当中根据name获取数据。
void removeAttribute(String name); // 将域当中绑定的数据移除
请求域和应用域的选用原则?
跳转
转发(一次请求)
// 第一步:获取请求转发器对象
RequestDispatcher dispatcher = request.getRequestDispatcher("/b");
// 第二步:调用转发器的forward方法完成跳转/转发
dispatcher.forward(request,response);
// 第一步和第二步代码可以联合在一起。
request.getRequestDispatcher("/b").forward(request,response);
将数据放到ServletContext应用域当中,当然是可以的,但是应用域范围太大,占用资源太多。不建议使用。
可以将数据放到request域当中,然后AServlet转发到BServlet,保证AServlet和BServlet在同一次请求当中,这样就可以做到两个Servlet,或者多个Servlet共享同一份数据。
跳转、转发(一次请求)
// 第一步:获取请求转发器对象
RequestDispatcher dispatcher = request.getRequestDispatcher("/b");
// 第二步:调用转发器的forward方法完成跳转/转发
dispatcher.forward(request,response);
// 第一步和第二步代码可以联合在一起。request.getRequestDispatcher("/b").forward(request,response);
关于request对象中两个非常容易混淆的方法:
// uri?username=zhangsan&userpwd=123&sex=1
String username = request.getParameter("username");
// 之前一定是执行过:request.setAttribute("name", new Object())
Object obj = request.getAttribute("name");
// 以上两个方法的区别是什么?
// 第一个方法:获取的是用户在浏览器上提交的数据。
request.getParameter表示接收参数,参数为页面提交的参数,包括:表单提交的参数、URL重写(就是xxx?id=1中的id)传的参数等,因此这个并没有设置参数的方法(没有setParameter),而且接收参数返回的不是Object,而是String类型。
// 第二个方法:获取的是请求域当中绑定的数据。
request.getAttribute表示从request范围取得设置的属性,必须要先setAttribute设置属性,才能通过getAttribute来取得,设置与取得的为Object对象类型 。
1)获取客户端的IP地址
String remoteAddr = request.getRemoteAddr();
2)解决后端获取前端数据出现的乱码问题(request请求)
设置请求体的字符集
get请求在请求行上提交数据。post请求在请求体中提交数据。显然这个方法是处理POST请求的乱码问题。这种方式并不能解决get请求的乱码问题。
Tomcat10之后,request请求体当中的字符集默认就是UTF-8,不需要设置字符集,不会出现乱码问题。
Tomcat9前(包括9在内),如果前端请求体提交的是中文,后端获取之后出现乱码,怎么解决这个乱码?执行以下代码。
request.setCharacterEncoding("UTF-8");
3)解决前端页面响应中文出现的乱码问题
在Tomcat9之前(包括9),响应中文也是有乱码的,怎么解决这个响应的乱码?
response.setContentType("text/html;charset=UTF-8");
注意:在Tomcat10包括10在内,响应中文的时候就不在出现乱码问题了。以上代码就不需要设置UTF-8了(默认UTF-8)。
4)解决get请求乱码问题
get请求发送的时候,数据是在请求行上提交的,不是在请求体当中提交的。
方案:修改CATALINA_HOME/conf/server.xml配置文件
注意:从Tomcat8之后,URIEncoding的默认值就是UTF-8,所以GET请求也没有乱码问题了。
5)获取应用的根路径
String contextPath = request.getContextPath();
6)获取请求方式
String method = request.getMethod();
7)获取请求的URI
String uri = request.getRequestURI(); // /aaa/testRequest
8)获取servlet path
String servletPath = request.getServletPath(); // /testRequest
使用纯粹的Servlet完成单表【对部门的】的增删改查操作。(B/S结构的。)
## 部门表
drop table if exists dept;
create table dept(
deptno int primary key,
dname varchar(255),
loc varchar(255)
);
insert into dept(deptno, dname, loc) values(10, 'XiaoShouBu', 'BEIJING');
insert into dept(deptno, dname, loc) values(20, 'YanFaBu', 'SHANGHAI');
insert into dept(deptno, dname, loc) values(30, 'JiShuBu', 'GUANGZHOU');
insert into dept(deptno, dname, loc) values(40, 'MeiTiBu', 'SHENZHEN');
commit;
select * from dept;
【前端开发工具使用HBuilder】
把HTML页面准备好,然后将HTML页面中的链接都能够跑通。(页面流转没问题。)
应该设计哪些页面呢?
什么叫做一个功能呢?
包括哪些功能?
我们应该怎么去实现一个功能呢?
第一:先修改前端页面的超链接,因为用户先点击的就是这个超链接。
第二:编写web.xml文件
list
com.bjpowernode.oa.web.action.DeptListServlet
list
/dept/list
第三:编写DeptListServlet类继承HttpServlet类。然后重写doGet方法。
package com.bjpowernode.oa.web.action;
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 DeptListServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}
第四:在DeptListServlet类的doGet方法中连接数据库,查询所有的部门,动态的展示部门列表页面.
分析list.html页面中哪部分是固定死的,哪部分是需要动态展示的。
list.html页面中的内容所有的双引号要替换成单引号,因为out.print(“”)这里有一个双引号,容易冲突。
现在写完这个功能之后,你会有一种感觉,感觉开发很繁琐,只使用servlet写代码太繁琐了。
while(rs.next()){
String deptno = rs.getString("a");
String dname = rs.getString("dname");
String loc = rs.getString("loc");
out.print(" ");
out.print(" "+(++i)+" ");
out.print(" "+deptno+" ");
out.print(" "+dname+" ");
out.print(" ");
out.print(" 删除");
out.print(" 修改");
out.print(" 详情");
out.print(" ");
out.print(" ");
}
建议:从前端往后端一步一步实现。首先要考虑的是,用户点击的是什么?用户点击的东西在哪里?
解决404的问题。写web.xml文件。
detail
com.bjpowernode.oa.web.action.DeptDetailServlet
detail
/dept/detail
编写一个类:DeptDetailServlet继承HttpServlet,重写doGet方法。
package com.bjpowernode.oa.web.action;
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 DeptDetailServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//中文思路(思路来源于:你要做什么?目标:查看部门详细信息。)
// 第一步:获取部门编号
// 第二步:根据部门编号查询数据库,获取该部门编号对应的部门信息。
// 第三步:将部门信息响应到浏览器上。(显示一个详情。)
}
}
在doGet方法当中:连接数据库,根据部门编号查询该部门的信息。动态展示部门详情页。
怎么开始?从哪里开始?从前端页面开始,用户点击删除按钮的时候,应该提示用户是否删除。因为删除这个动作是比较危险的。任何系统在进行删除操作之前,是必须要提示用户的,因为这个删除的动作有可能是用户误操作。(在前端页面上写JS代码,来提示用户是否删除。)
以上的前端程序要写到后端的java代码当中:
解决404的问题:
http://localhost:8080/oa/dept/delete?deptno=30
web.xml文件
delete
com.bjpowernode.oa.web.action.DeptDelServlet
delete
/dept/delete
编写DeptDelServlet继承HttpServlet,重写doGet方法。
package com.bjpowernode.oa.web.action;
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 DeptDelServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 根据部门编号,删除部门。
}
}
删除成功或者失败的时候的一个处理(这里我们选择了转发,并没有使用重定向机制。)
// 判断删除成功了还是失败了。
if (count == 1) {
//删除成功
//仍然跳转到部门列表页面
//部门列表页面的显示需要执行另一个Servlet。怎么办?转发。
request.getRequestDispatcher("/dept/list").forward(request, response);
}else{
// 删除失败
request.getRequestDispatcher("/error.html").forward(request, response);
}
在一个web应用中通过两种方式,转发和重定向可以完成资源的跳转
25.1.1 代码上有什么区别?
转发
// 获取请求转发器对象
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。
重定向
// 注意:路径上要加一个项目名。为什么?
// 浏览器发送请求,请求路径上是需要添加项目名的。
// 以下这一行代码会将请求路径“/oa/dept/list”发送给浏览器
// 浏览器会自发的向服务器发送一次全新的请求:/oa/dept/list
response.sendRedirect("/oa/dept/list");
25.1.2 形式上有什么区别?
25.1.3 转发和重定向的本质区别?
25.1.4 使用一个例子去描述这个转发和重定向
不一定,跳转的资源只要是服务器内部合法的资源即可。包括:Servlet、JSP、HTML.....
Servlet3.0版本之后,推出了各种Servlet基于注解式开发的。
jakarta.servlet.annotation.WebServlet
在Servlet类上使用:@WebServlet,WebServlet注解中有哪些属性呢?
name属性:用来指定Servlet的名字。等同于:
urlPatterns属性:用来指定Servlet的映射路径。可以指定多个字符串。
loadOnStartUp属性:用来指定在服务器启动阶段是否加载该Servlet。等同于:
value属性:当注解的属性名是value的时候,使用注解的时候,value属性名是可以省略的。
注意:不是必须将所有属性都写上,只需要提供需要的。(需要什么用什么。)
注意:属性是一个数组,如果数组中只有一个元素,使用该注解的时候,属性值的大括号可以省略。
@注解名称(属性名=属性值, 属性名=属性值, 属性名=属性值…)
27.1 oa项目存在的问题
上面的注解解决了配置文件的问题。但是现在的oa项目仍然存在一个比较臃肿的问题。
27.2 怎么解决类爆炸问题?
使用纯粹Servlet开发web应用,会在Servlet当中编写HTML/CSS/JavaScript等前端代码。
上面的那个Servlet(Java程序)能不能不写了,让机器自动生成。我们程序员只需要写这个Servlet程序中的“前端的那段代码”,然后让机器将我们写的“前端代码”自动翻译生成“Servlet这种java程序”。然后机器再自动将“java”程序编译生成"class"文件。然后再使用JVM调用这个class中的方法。
在java的servlet规范当中,session对应的类名:HttpSession(jarkata.servlet.http.HttpSession)
session机制属于B/S结构的一部分。如果使用php语言开发WEB项目,同样也是有session这种机制的。session机制实际上是一个规范。然后不同的语言对这种会话机制都有实现。
session对象最主要的作用是:保存会话状态。(用户登录成功了,这是一种登录成功的状态,你怎么把登录成功的状态一直保存下来呢?使用session对象可以保留会话状态。)
张三打开一个浏览器A,李四打开一个浏览器B,访问服务器之后,在服务器端会生成:
HttpSession session = request.getSession();
这行代码很神奇。张三访问的时候获取的session对象就是张三的。李四访问的时候获取的session对象就是李四的。
JSESSIONID=xxxxxx 这个是以Cookie的形式保存在浏览器的内存中的。浏览器只要关闭。这个cookie就没有了。
session列表是一个Map,map的key是sessionid,map的value是session对象。
用户第一次请求,服务器生成session对象,同时生成id,将id发送给浏览器。
用户第二次请求,自动将浏览器内存中的id发送给服务器,服务器根据id查找session对象。
关闭浏览器,内存消失,cookie消失,sessionid消失,会话等同于结束。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V95uO70H-1656924363020)(…/…/…/Pictures/Servlet/session%E5%AF%B9%E8%B1%A1.png)]
关闭浏览器之后,浏览器中保存的sessionid消失,下次重新打开浏览器之后,浏览器缓存中没有这个sessionid,自然找不到服务器中对应的session对象。session对象找不到等同于会话结束,但是session对象还存在,等待着超时销毁机制销毁(但没有sessionid了,访问不到了)。
session对象什么时候被销毁?
一种销毁:是超时销毁。
一种销毁:是手动销毁。
session.invalidate();
浏览器关闭的时候,服务器是不知道的,服务器无法检测到浏览器关闭了,所以session的悄毁要依靠session超时机制,但也有一种可能,系统提供了“安全退出”,用户可以点击这个按钮,这样服务题就知道你退出了,然后服务器会自动销毁session对象。
注意:session对象销毁和session会话结束不同。
session的实现原理中,每一个session对象都会关联一个sessionid,例如:
京东商城,在未登录的情况下,向购物车中放几件商品。然后关闭商城,再次打开浏览器,访问京东商城的时候,购物车中的商品还在,这是怎么做的?我没有登录,为什么购物车中还有商品呢?
126邮箱中有一个功能:十天内免登录
在HTTP协议中是这样规定的:当浏览器发送请求的时候,会自动携带该path下的cookie数据给服务器。(URL。)
假设现在发送的请求路径是“http://localhost:8080/servlet13/cookie/generate”生成的cookie,如果cookie没有设置path,默认的path是什么?
手动设置cookie的path
Cookie[] cookies = request.getCookies(); // 这个方法可能返回null
if(cookies != null){
for(Cookie cookie : cookies){
// 获取cookie的name
String name = cookie.getName();
// 获取cookie的value
String value = cookie.getValue();
}
}
1、在页面最上面加上:
<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %>
2、配置数据库连接的时候
jdbc.mysql.url=jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
3、项目web.xml中配置
CharacterFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
CharacterFilter
/*
4、tomcat容器中
5、1-4都是正常的解决思路,基本能解决,但是遇到1-4都无法解决时,继续排查:
因为单独在数据库中插入可以正常显示;数据库中正常的内容在页面显示也没有问题;通过在控制台输入sql语句发现,执行sql语句时都是一切正常的。
所以,锁定问题是数据库的问题。
通过下面的sql语句排查看是否已经设为UTF-8
SHOW VARIABLES LIKE 'character_set_%';
此时,发现character_set_server=latin1,我们需要把它改为utf-8即可。
设置
1、作响应的JSP页面或者servlet设置
response.setCharacterEncoding(“UTF-8”);
作提交的JSP页面设置
request.setCharacterEncoding(“UTF-8”);
2、作响应的JSP页面或者servlet设置
response.setHeader(“Content-type”, “text/html;charset=UTF-8”);
request乱码指:客户端浏览器向服务器发送的请求参数中包含中文字符,服务器获取到的请求参数的值是乱码;
response乱码指:服务器向客户端浏览器发送的数据包含中文字符,但是浏览器中显示的是乱码;
乱码产生的原因:客户端(浏览器)跟服务器端采用的编码格式不一致造成的。
将CATALINA_HOME/conf/logging.properties文件中的内容修改如下:
java.util.logging.ConsoleHandler.encoding = GBK
将上面的项目部署之后,启动服务器,打开浏览器,访问以下地址:
对JSP进行错误调试的时候,还是要直接打开JSP文件对应的java文件,检查java代码。
开发JSP的最高境界:眼前是JSP代码,但是脑袋中呈现的是java代码。
2.2.1 <% java语句; %>(java语句的输出)
2.2.2 <%! %>
2.2.3 <%= %> (表达式和变量的输出)
使用Servlet处理业务,收集数据。 使用JSP展示数据。
将之前原型中的html文件,全部修改为jsp,然后在jsp文件头部添加page指令(指定contentType防止中文乱码),将所有的JSP直接拷贝到web目录下。
完成所有页面的正常流转。(页面仍然能够正常的跳转。修改超链接的请求路径。)
Servlet中连接数据库,查询所有的部门,遍历结果集。
在JSP中:
思考一个问题:如果我只用JSP这一个技术,能不能开发web应用?
JSP文件的扩展名必须是xxx.jsp吗?
jsp文件的扩展名是可以配置的。不是固定的。
在CATALINA_HOME/conf/web.xml,在这个文件当中配置jsp文件的扩展名。
jsp
*.jsp
*.jspx
xxx.jsp文件对于小猫咪来说,只是一个普通的文本文件,web容器会将xxx.jsp文件最终生成java程序,最终调用的是java对象相关的方法,真正执行的时候,和jsp文件就没有关系了。
小窍门:JSP如果看不懂,建议把jsp翻译成java代码,就能看懂了。
同学问:包名bean是什么意思?
完成剩下所有功能的改造。
登录功能实现了,目前存在的最大的问题:
指令的作用:指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件。)
指令包括哪些呢?
指令的使用语法是什么?
关于page指令当中都有哪些常用的属性呢?
<%@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。
exception.printStackTrace()方法将错误信息打印到控制台
jakarta.servlet.jsp.PageContext pageContext 页面作用域
jakarta.servlet.http.HttpServletRequest request 请求作用域
jakarta.servlet.http.HttpSession session 会话作用域
jakarta.servlet.ServletContext application 应用作用域
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表达式的功效。
第一功效:从某个域中取数据。
四个域:
第二功效:将取出的数据转成字符串。
如果是一个java对象,也会自动调用java对象的toString方法将其转换成字符串。
第三功效:将字符串输出到浏览器。
和这个一样:<%= %>,将其输出到浏览器。
语法格式:${表达式}
将User对象存储到某个域当中。一定要存,因为EL表达式只能从某个范围中取数据。数据是必须存储到四大范围之一的。
<%
// 创建User对象
User user = new User();
user.setUsername("jackson");
user.setPassword("1234");
user.setAge(50);
request.setAttribute("userObj", user)
%>
使用EL表达式取
${这个位置写什么????这里写的一定是存储到域对象当中时的name}
要这样写:${userObj}
等同于java代码:<%=request.getAttribute("userObj")%>
你不要这样写:${"userObj"}
${abc}表示从某个域中取出数据,并且被取的这个数据的name是"abc",之前一定有这样的代码: 域.setAttribute(“abc”, 对象);
${“abc”} 表示直接将"abc"当做普通字符串输出到浏览器。不会从某个域中取数据了。
从域中取数据,取出user对象,然后调用user对象的toString方法,(没有get方法无法拿到,可以不使用驼峰原则)转换成字符串,输出到浏览器。
${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()
pageContext < request < session < application
EL表达式对null进行了预处理。如果是null,则向浏览器输出一个空字符串。
从Map集合中取数据:${map.key}
从数组和List集合中取数据:
<%@page contentType="text/html;charset=UTF-8" isELIgnored="true" %>
isELIgnored="true" 表示忽略EL表达式
isELIgnored="false" 表示不忽略EL表达式。(这是默认值)
isELIgnored=“true” 这个是全局的控制忽略EL表达式。
${username} 使用反斜杠进行局部控制忽略EL表达式。
由于EL中没有内含request对象,所以通过pageContext.request获取
pageContext
<%=request.contextPath()%>
${pageContext.request.contextPath}
//获取应用的根
param
<%=request.getParameter("xxx")%>
${param.xxx}
paramValues
<%=request.getParameters("xxx")%>
${paramValues.xxx[0]}
${paramValues.xxx[1]}
initParam
<%=application.getInitParameter("xxx")%>
//application是jsp的九大内置对象,对应ServiceContext
${initParam.xxx}
算术运算符 +、-、*、/、%
+号不会做字符串拼接,永远做的是加法运算,${10+“20”}=30("20"转换成数字20)
${10+“abc”}(转不成数字报500错误)
关系运算符 == eq != > >= < <=
==、eq、!= 都会调用equals方法,字符串比较与字符串常量池无关,只是重写了equals方法
{"abc"=="abc"},不能写成"abc"=="abc",不能写成{“abc”}=${“abc”}
逻辑运算符 ! && || not and or
有运算级,!记得加括号
条件运算符 ? :
取值运算符 [ ] 和 .
empty运算符
tomcat10之后引入的jar包是:
在IDEA当中怎么引入?
什么时候需要将jar包放到WEB-INF/lib目录下?
JSTL提供了很多种标签,你要引入哪个标签????重点掌握核心标签库。
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
这个就是核心标签库。
prefix="这里随便起一个名字就行了,核心标签库,大家默认的叫做c,你随意。"
表面使用的是标签,底层实际上还是java程序。
<%@taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core” %>
源码解析:配置文件tld解析
对该标签的描述
catch 标签的名字
org.apache.taglibs.standard.tag.common.core.CatchTag 标签对应的java类。
JSP 标签体当中可以出现的内容,如果是JSP,就表示标签体中可以出现符合JSP所有语法的代码。例如EL表达式。
对这个属性的描述
var 属性名
false false表示该属性不是必须的。true表示该属性是必须的。
false 这个描述说明了该属性是否支持EL表达式。false表示不支持。true表示支持EL表达式。
JSP....
c:if
c:forEach
c:choose c:when c:otherwise
青少年
青年
中年
老年
在前端HTML代码中,有一个标签,叫做base标签,这个标签可以设置整个网页的基础路径。
这是Java的语法,也不是JSP的语法。是HTML中的一个语法。HTML中的一个标签。通常出现在head标签中。
< base href=“http://localhost:8080/oa/”>
在当前页面中,凡是路径没有以“/”开始的,都会自动将base中的路径添加到这些路径之前。
需要注意:在JS代码中的路径,保险起见,最好不要依赖base标签。JS代码中的路径最好写上全路径。
当前的OA项目存在什么缺陷?
第一步:编写一个Java类实现一个接口:jarkata.servlet.Filter。并且实现这个接口当中所有的方法。
第二步:在web.xml文件中对Filter进行配置。这个配置和Servlet很像。
filter2
com.bjpowernode.javaweb.servlet.Filter2
filter2
*.do
或者使用注解:@WebFilter({“*.do”})
注意:
chain.doFilter(request, response);
执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet。
注意:Filter的优先级,天生的就比Servlet优先级高。
目标Servlet是否执行,取决于两个条件:
依靠filter-mapping标签的配置位置,越靠上优先级越高。
过滤器的调用顺序,遵循栈数据结构。
jakarta.servlet包下:
jakarta.servlet.http包下:
以ServletContextListener为例。
第一步:编写一个类实现ServletContextListener接口。并且实现里面的方法。
void contextInitialized(ServletContextEvent event)
void contextDestroyed(ServletContextEvent event)
第二步:在web.xml文件中对ServletContextListener进行配置,如下:
com.bjpowernode.javaweb.listener.MyServletContextListener
当然,第二步也可以不使用配置文件,也可以用注解,例如:@WebListener
注意:所有监听器中的方法都是不需要javaweb程序员调用的,当某个特殊的事件发生(特殊的事件发生其实就是某个时机到了。)之后,被web服务器自动调用。
实现oa项目中当前登录在线的人数。