资源分类
- 静态资源,所有用户访问后,得到的结果都是一样的。
html/css/js
- 动态资源,每个用户访问相同的资源后,得到的结果不一样。
servlet/jsp/php/asp
- 访问静态资源时,浏览器中,有静态资源的解释引擎,解析静态资源
- 动态资源被访问时,先转换为静态资源,再返回给浏览器
响应
网络通信三要素
- 找到服务器的电脑
IP
电子设备在网络中的唯一标识端口
应用程序在计算机中的唯一标识 0~65536传输协议
数据传递的规则,网络上交流的语言方式。TCP
,安全协议,三次握手,四次挥手,速度稍慢;UDP
,不安全协议,速度快
Web服务器软件
- 服务器,安装了服务器软件的计算机
- 服务器软件,接受用户的请求,处理请求,做出响应
- Web服务器软件,可以部署web项目,让用户通过浏览器来访问这些项目
- 常见web服务器软件,
weblogic
,支持所有的JavaEE
规范,是企业级开发中使用的技术规范的总和,一共规定13项大的规范;webSphere
;JBOSS
;Tomcat
,apache基金组织,中小型,仅仅支持少量的JavaEE
规范
mac
- 安装
brew install tomcat
- 启动
catalina run
- 关闭
catalina stop
- 配置文件,修改端口
/usr/local/Cellar/tomcat/10.0.11/libexec/conf/server.xml
- 可以把端口改成
80
,80
是http协议默认的端口,访问时就不需要添加端口号
部署项目
- 直接将项目放到
webapps
的目录中,/usr/local/Cellar/tomcat/10.0.11/libexec/webapps
,放文件hello/hello.html
。访问http://localhost:8080/hello/hello.html
;或者直接部署war
包 - 修改
server.xml
配置文件,启动http://localhost:8080/nihao/hello.html
//docBase项目存放路径 path虚拟目录
<Context docBase="/Users/apple/Desktop/hello" path="nihao" />
- 添加配置文件。
/usr/local/etc/tomcat/Catalina/localhost
目录下,添加任意名称的xml
文件,虚拟目录就是这个文件的名称,访问http://localhost:8080/properties/hello.html
;热部署方式,如果不需要,直接改名,加下划线_bak
或者其他内容
<Context docBase="/Users/apple/Desktop/hello"/>
目录结构
- Java目录结构。根目录包含
WEB—INF
和静态资源;WEB—INF
包含web.xml
,web项目的核心配置文件、classes目录
,放置字节码文件、lib目录
,放置依赖的jar包
IDEA启动tomcat
- run->edit configurations->配置tomcat
- 修改配置
update resources
,更新静态文件即可重新部署
Servlet
- server applet,运行在服务端的小程序;就是一个接口,定义了java类能被浏览器(tomcat识别)访问到的规则,处理动态资源访问时,要根据用户访问的信息,返回不同的业务信息。使用时,需要实现servlet借口,复写方法
使用
- 实现
Servlet类
- web.xml配置
<!--配置servlet-->
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>main.com.java.web.servlet.ServletDemo1</servlet-class>
</servlet>
<!--配置映射-->
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
tomcat
将全类名的字节码文件加载进内存;创建Servlet
对象;调用service
方法- 当服务器接收到客户端浏览器的请求后,解析url路径,获取访问的
Servlet
资源路径;查找web.xml
文件,是否有对应的url-pattern
标签内容,通过这个标签名找到对应的字节码文件,加载进内存,创建对象
生命周期
- 初始化方法,用于加载资源,在servlet创建时,执行,只执行一次;默认情况下,第一次被访问时被创建;可以执行Servlet的创建时机;servlet在内存中只存在一个,是单例的,多个用户同时访问时,可能存在线程安全问题,尽量不要在Servlet中定义成员变量,即使定义了成员变量,也不要对其修改
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init...");
}
//在web.xml的servlet标签下配置
<!--Servlet的创建时机 值为负数,第一次访问时创建;值为0或正整数,服务器启动时创建-->
<load-on-startup>5</load-on-startup>
- 获取ServletConfig对象,配置对象
public ServletConfig getServletConfig() {
return null;
}
- 提供服务方法,每一次Servlet被访问时,都会执行
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("hello servlet");
}
- 获取Servlet一些信息,版本、作者...
public String getServletInfo() {
return null;
}
- 销毁方法,只有在服务器正常关闭时,执行,执行一次。在Servlet被销毁之前执行,一般用于释放资源
public void destroy() {
System.out.println("destroy...");
}
注解配置
- 配置映射资源路径
@WebServlet(urlPatterns = "/demo", loadOnStartup = 5)
//value属性默认就是 urlPatterns,可以用下面的形式
@WebServlet("/demo2")
文件关系
- 一个项目的tomcat访问的是
/Users/apple/Library/Caches/JetBrains/IntelliJIdea2021.1/tomcat/1fadeb49-d2d2-4621-a3bc-7ff38e4d2eea
,可以通过conf/Catalina/localhost
下的xml
文件访问部署的项目文件 - 真正部署的项目在当前的工作目录下
/Users/apple/Desktop/java/java-program/java-web/servlet/target/servlet-1.0-SNAPSHOT
WEB-INF
目录下的资源是不能被浏览器直接访问的
继承的体系结构
Servlet(接口) -> GenericServlet(抽象类) -> HttpServlet(抽象类)
GenericServlet
,只抽象了service
方法,其他方法默认空实现HttpServlet
,对Http协议的封装和描述,注重数据的获取和处理
,根据请求方式,重写对应的方法
public class ServletDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
相关配置
urlpattern
访问虚拟路径,一个servlet可以定义多个访问路径,@WebServlet({"/demo1", "/demo4"})
- 路径定义
/xxx
,/*
访问优先级最低;/xx/xx
目录结构进行访问;*.do
,@WebServlet("*.do")
,只能是这个形式,否则报错
ServletContext
- 代表整个web应用,可以和程序的容器(服务器)来通信
- 获取
//一次运行,只生成一个ServletContext对象
ServletContext servletContext1 = req.getServletContext();
ServletContext servletContext2 = this.getServletContext();
- 获取
Mime
类型,在互联网通信过程一种文件数据类型,格式大类型/小类型
text/html
image/jpeg
ServletContext context = this.getServletContext();
//定义文件名
String filename = "a.jpg";
//获取Mime类型
String mimeType = context.getMimeType(filename);
- 域对象,共享数据,所有用户所有请求的数据
//A资源设置数据
ServletContext context = this.getServletContext();
context.setAttribute("name","zhangsan");
//B资源获取数据
ServletContext context = this.getServletContext();
Object name = context.getAttribute("name");
System.out.println(name);
- 获取文件真实(服务器)路径
ServletContext context = this.getServletContext();
//webapp下
String realPath1 = context.getRealPath("b.txt");
//WEB-INF下
String realPath2 = context.getRealPath("/WEB-INF/a.txt");
//java路径下,java-sources资源下
String realPath3 = context.getRealPath("/WEB-INF/classes/c.txt");
Servlet注入无效
- web.xml配,加载配置
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext*.xml</param-value>
</context-param>
- 重写方法
@Override
public void init() throws ServletException {
super.init();
ServletContext servletContext = this.getServletContext();
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, servletContext);
}
Http
- Hyper Text Transfer Protocol 超文本传输协议
- 定义了客户端与服务端通信时,发送的数据格式
- 基于TCP/IP的高级协议;基于请求/响应模型,一次请求对应一次响应;无状态的,每次请求之间相互独立
- 版本 1.0 每次请求,都会建立新的连接;1.1 复用连接
- 路径
相对路径,通过相对路径不可以确定唯一资源,不以斜杠开头,以点杠开头,如果在同一目录下可以省略点杠;找到访问的当前资源和目标资源的相对位置关系
绝对路径,通过绝对路径可以确定为一资源,以斜杠开头的路径;定义的路径是给水用的,给客户端浏览器,需要加虚拟目录(项目访问路径);给服务器用,不需要虚拟目录
请求
消息格式
- 请求行。请求方式7种,常用2种。
GET
请求参数在url中,长度有限制;POST
请求参数在请求体中,长度没有限制
请求方式 请求url 请求协议/版本
GET /servlet_war_exploded/demo3 HTTP/1.1
- 请求头,客户端浏览器告诉服务器信息
//请求的主机地址
Host: localhost:8080
//连接类型,长链接可以被复用
Connection: keep-alive
//升级请求
Upgrade-Insecure-Requests: 1
//浏览器告诉服务器,使用的浏览器的版本信息;可以在服务器端获取客户端浏览器的信息,处理兼容的问题
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 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
//压缩格式
Accept-Encoding: gzip, deflate, br
//语言环境
Accept-Language: zh-CN,zh;q=0.9
//告诉服务器当前请求从哪里来,防盗链,阻止非法访问;统计工作,记录来源数量
Referer: http://localhost:8080/servlet_war_exploded/login.html
- 请求空行,就是一个空行,分割
POST
请求头和请求体 - 请求体,
GET
方式没有请求体,POST
方式有。封装POST
请求参数的
Request
tomcat
服务器根据请求的url中的资源路径,进行访问,并创建request和response
对象,request
对象封装请求消息数据,并将request和respose
传递给对应的Servlet
对象,调用service
方法,做对应的业务处理。服务器在给浏览器做出响应之前,会从response
对象中,取响应消息数据request和response
是由服务器创建的,request
获取请求消息,response
来设置响应消息
继承体系
ServletRequest(接口) -> HttpServletRequest(接口) -> org.apache.catalina.connector.RequestFacade(实现类)
使用
- 请求行数据
//获取请求方式
String method = req.getMethod();
//获取虚拟目录
String contextPath = req.getContextPath();
//获取资源路径
String servletPath = req.getServletPath();
//获取get请求参数
String queryString = req.getQueryString();
//相对访问路径,统一资源标识符,共和国
String requestURI = req.getRequestURI();
//完整的绝对访问路径,统一资源定位符,中华人民共和国
StringBuffer requestURL = req.getRequestURL();
//协议版本
String protocol = req.getProtocol();
//获取客户端ip地址
String remoteHost = req.getRemoteHost();
- 请求头数据
//获取指定请求头内容
String accept = req.getHeader("accept");
//获取所有请求头
Enumeration<String> hn = req.getHeaderNames();
while (hn.hasMoreElements()) {
String name = hn.nextElement();
System.out.println(name + "-" + req.getHeader(name));
}
//获取浏览器信息
String agent = req.getHeader("user-agent");
if (agent.contains("Chrome") || agent.contains("Safari")) {
System.out.println("Yes");
}
//从外站跳转到这个页面,才会有值
String Referer = req.getHeader("Referer");
System.out.println(Referer);
if (Referer.contains("/servlet_war_exploded")) {
System.out.println("success");
} else {
System.out.println("fail");
}
- 获取请求体数据(只有post方式可以),先获取流对象,再从流对象中拿数据
//字符操作流
BufferedReader reader = req.getReader();
String s = null;
while ((s = reader.readLine()) != null) {
System.out.println(s);
}
- 获取请求参数通用方式(get和post都行)
//获取指定参数的值
String username = req.getParameter("username");
//获取指定参数的参数数组,复选框多选项
String[] hobbies = req.getParameterValues("hobbies");
//获取所有参数名
Enumeration<String> parameterNames = req.getParameterNames();
while (parameterNames.hasMoreElements()) {
System.out.println(parameterNames.nextElement());
}
//获取参数的键值对
Map<String, String[]> parameterMap = req.getParameterMap();
parameterMap.entrySet().forEach(stringEntry -> System.out.println(stringEntry.getKey() + "-" + Arrays.toString(stringEntry.getValue())));
- 获取请求参数,
post
请求体中文乱码
//设置流的字符编码与页面相同
req.setCharacterEncoding("utf-8");
- 请求转发,
forward
,一种在服务器内部的资源跳转的方式,将浏览器的请求,转发给另一个资源,只是让另一个资源处理信息;浏览器地址栏路径不发生变化;只能转发给当前服务器内部资源中;浏览器仅仅只发送一次请求,转发过程由服务器进行处理
req.getRequestDispatcher("/demo4").forward(req, resp);
- 数据共享。域对象,一个有作用范围的对象,可以在范围内共享数组。
request域
代表一次请求的范围,一般用于请求转发的多个资源中共享数据
String username = req.getParameter("username");
String newUsername = req.getRequestURI() + username;
//存储数据
req.setAttribute("newUsername", newUsername);
//另一个资源处理
//获取值
Object newUsername = req.getAttribute("newUsername");
System.out.println(newUsername);
//移除键值对
req.removeAttribute("newUsername");
- 获取
ServletContext
ServletContext servletContext = req.getServletContext();
Beanutils
- javaBean,标准的java类;必须被public修饰;有空构造器;参数被private修饰;有setter和getter
- 封装数据
- 直接通过
参数的map集合
封装javaBean
对象
User user = new User();
Map<String, String[]> parameterMap = req.getParameterMap();
try {
BeanUtils.populate(user, parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
- 操作的是类的属性。属性,setter和getter方法截取后的那部分,有时候会不一样
- 设置和获取
User user = new User();
BeanUtils.setProperty(user, "username", "zhangsan");
String username = BeanUtils.getProperty(user, "username");
System.out.println(username);
响应
- 服务器端发送给客户端的数据
- 状态码,服务器告诉客户端浏览器本次请求和相应的一个状态,3位数字,共5类
1xx 服务器接受客户端消息,但是没有接收完成,等待一段时间后,发送1xx状态码
2xx 成功,200
3xx 重定向。302,另外访问一个资源地址;304,访问缓存
4xx 客户端错误。404,请求路径没有对应资源;405,请求方式没有对应的方法
5xx 服务端错误。500,服务器内部错误,代码异常
- 重定向,
redirect
,地址栏发生变化;可以访问服务器的资源;两次请求,第二次请求重定向的资源路径。不能共享数据
消息格式
- 响应行
协议/版本 响应状态码 状态码描述
HTTP/1.1 200 OK
- 响应头
//服务器告诉客户端浏览器,本次响应消息体的数据格式以及编码格式
Content-Type: text/html;charset=utf-8
//服务器告诉客户端以什么格式打开响应体的数据
Content-disposition: in-line(默认值,当前页面内打开)/ attachment;filename=xxx(以附件的形式打开响应体,文件下载)
Content-Length: 34
Date: Wed, 22 Sep 2021 08:31:43 GMT
Keep-Alive: timeout=20
Connection: keep-alive
- 响应空行,分割响应头和响应体
- 响应体,返回的数据
Response
- 设置响应消息的数据
- 重定向,访问A最终访问B。A资源告诉浏览器需要重定向,告诉浏览器B资源的路径
resp.sendRedirect("demo2");
- 设置响应行
resp.setStatus(302);
- 设置响应头
//设置重定向地址
resp.setHeader("location","/demo2");
- 设置响应体,通过输出流输出
//设置流的默认编码
resp.setCharacterEncoding("gbk");
//告诉浏览器消息体数据的编码
//resp.setHeader("content-type","text/html;charset=utf-8");
resp.setContentType("text/html;charset=utf-8");
//字符输出流
PrintWriter pw = resp.getWriter();
pw.write("hello response");
pw.write("你好");
//获取字节输出流
ServletOutputStream os = resp.getOutputStream();
os.write("hello".getBytes());
os.write("你好".getBytes());
- 将图片输出到页面
int width = 100;
int height = 50;
//创建图片
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//...设计图片...
//输出到页面
//formatName格式
ImageIO.write(image,"jpg",resp.getOutputStream());
会话
- 浏览器第一次给服务器资源发送请求,会话建立,直到一方断开为止
- 在一次会话的范围内的多次请求间共享数据
- cookie,客户端会话技术,存在客户端
- session,服务器端会话技术,存在服务器端
Cookie
- 将数据保存到客户端
- 基本使用
//A资源 创建cookie对象
Cookie cookie = new Cookie("msg", "heelo");
//发送cookie
resp.addCookie(cookie);
//B资源 获取
Cookie[] cookies = req.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
String name = cookie.getName();
String value = cookie.getValue();
System.out.println(name + "-" + value);
}
}
- cookie如果涉及到存储空格需要进行
url编码
//URL编码
str_date = URLEncoder.encode(str_date, "utf-8");
//解码
value = URLDecoder.decode(time, "utf-8");
实现原理
- 客户端请求A资源,A资源响应头中带
set-cookie:msg=hello
,http协议约定了,如果客户端收到cookie
会保存下来 - 客户端请求同服务器的其他资源(包括资源A)时,请求头将带上这个cookie
细节
- 一次可以发送多个cookie,使用response多次调用
addCookie
方法即可 - 浏览器中保存时间。默认情况下,存在浏览器内存中,当浏览器关闭后,cookie数据被销毁;设置cookie生命周期,持久化存储
//正数,持久化存储,cookie存储时间
//负数,默认值
//零,删除cookie信息
cookie.setMaxAge(10);
tomcat8后
可以存储中文数据;8之前,需要将中文数据转码,一般采用URL编码- 默认情况下,cookie只能共享给同一个虚拟目录下的其他资源
//设置为/,同一个服务器的其他虚拟目录(项目)也可以访问
cookie.setPath("/");
//不同的tomcat服务器间cookie共享
//一级域名相同,实现多个服务器之间cookie可以共享
cookie.setDomain("xxxx");
特点
- cookie存储数据在客户端浏览器
- 浏览器对单个cookie大小有限制,对同一个域名下的总cookie数量也有限制
- 一般存储存储少量的不太敏感的数据,在不登陆的情况下,完成服务器对客户端的身份识别,保存一些设置
Session
- 在一次会话的多次请求间共享数据,将数据保存在服务器的对象中
- 使用
//A资源设置
HttpSession session = req.getSession();
session.setAttribute("name","zhangsan");
//B资源获取
HttpSession session = req.getSession();
Object name = session.getAttribute("name");
//删除
session.removeAttribute("name");
- Session是依赖Cookie的,第一次获取Session,没有Cookie,会在内存中创建一个新的Session对象,并生成一个Session编号
JSESSIONID
放入Cookie头中,发给客户端;客户端下一次请求的时候,会携带这个Cookie,服务器端就可以通过Cookie头的JSESSIONID
,找到内存中id为这个Cookie的Session对象
细节
- 客户端关闭后,服务器不关闭,session对象不是同一个
HttpSession session = req.getSession();
//设置客户端关闭后,session也可以相同
Cookie cookie = new Cookie("JSESSIONID", session.getId());
cookie.setMaxAge(60);
resp.addCookie(cookie);
- 客户端不关闭,服务器关闭后,session对象不一样,但是要确保数据不丢失。session的
钝化
,在服务器正常关闭之前,把session对象序列化到硬盘上;活化
,将session文件转换为内存中的seesion对象。通过tomcat
生成,会自动进行活化钝化,而idea会将work文件夹删除再创建 - 什么时候被销毁。服务器关闭;session对象调用
invalidate()
;默认三十分钟后失效,可以在/usr/local/etc/tomcat/web.xml
或者项目中的web.xml
配置session-config
特点
- 用于存储一次会话的多次请求的数据,存储在服务器端
- 可以存储任意类型,任意大小的数据
Session和Cookie
- session存储在服务器端,比较安全;cookie在客户端
- session没有数据大小限制,cookie有
JSP
- Java Server Pages,Java服务器页面,既可以写java代码,又可以定义html标签
- 简化书写
- 原理,客户端访问jsp文件,jsp被编译成java文件,生成class字节码文件,有这个字节码文件生成响应,本质上就是一个servlet
- 脚本,jsp定义java代码的方式
<% ...java... %> //定义在service方法中,service可以定义什么,这个区域就可以定义什么
<%! ...java... %> //定义成员变量和成员方法,在java类的成员位置
<%= ...java... %> //输出里面的变量在页面上,就近原则,相同属性名,输出作用域小的
指令
- 用于配置jsp页面,导入资源文件
<%@ 指令名称 属性名1=属性名1 属性名2=属性名2...%>
- page,配置jsp页面
contentType 设置响应体的mime类型以及字符集;设置当前jsp页面的编码(适用于idea)
pageEncoding 设置当前jsp页面的编码
buffer 缓冲区的大小
import 导java包
errorPage 当前页面发生异常后,跳转后指定的错误页面
isErrorPage 标识当前页面是否是错误页面
- include,页面包含的,导入页面资源文件
<%@ include file="home.jsp"%>
- taglib,导入标签库导入,类似导包
//前缀自定义
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
内置对象
- 在jsp中不需要获取和创建,可以直接使用的对象,在jsp文件转换成java文件后,后台自动声明了的对象
- request 一次请求访问多个资源(转发)
- response,响应对象
- out,可以将数据输出到页面上,字符输出流对象,与
response.getWriter()
类似,但是response.getWriter()
先于out
输出,tomcat
服务器先去找response
缓冲区的数据 - pageContext,当前页面共享数据,获取其他内置对象
- session,一次会话的多个请求间共享数据
- application
servletContext
,所有用户间共享资源,服务器被创建就存在一个对象 - page,当前类对象,this
- config
servletConfig
,配置对象 - exception,异常对象,只适用于
isErrorPage=true
的页面
EL表达式
- expression language,表达式语言
- 简化jsp页面java代码的编写
- 语法
${...表达式...}
- jsp默认支持el表达式,page指令的
isELIgnored
可以忽略el表达式;\${3 > 4}
忽略单条el表达式
使用
- 运算,算数运算符;比较运算符;逻辑运算符;空运算符
empty
,判断字符串、集合、数组对象是否为null,并且长度是否为0
//算数
${3+4}
${3/4}
${3 div 4}
${4 % 3}
${4 mod 3}
//比较
${3 == 4}
//逻辑
${3 < 4 and 5 < 6}
${3 > 4 && 5 < 6}
${not (3 > 4)}
//空运算符,空为true
${empty sessionScope.list}
- 获取值,只能从域对象中获取值
${域名称.键名}
,从指定域,获取指定键的值;没取到值,直接显示空字符串;${键名}
,从最小的域中开始查找
${requestScope.name}
${sessionScope.age}
${sessionScope.list}
//从最小的域开始查找
${age}
- 获取对象值,只要这个对象有
get方法
就可以输出对应的属性
${requestScope.user.name}<br>
${requestScope.user.age}<br>
${requestScope.user.birthday}<br>
- 获取list集合的值,如果角标越界,返回空字符串
${list.get(0)}
${list[1]}
${list.get(2)}
- 获取map集合
${map.get("age")}
${map.name}
${map.user.birthStr}
${map["age"]}
${map.get("user").name}
隐式对象
- 可以直接拿来用,类似内置对象
pageContext
,获取jsp内置对象
//动态获取虚拟目录
${pageContext.request.contextPath}
JSTL标签
- JavaServer Page Tag Library,jsp标准标签库
- 用于简化和替换jsp上的java代码
- 导入标签库
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
使用
- if,没有else。
test
属性,接受boolean表达式,如果为true,显示表前提内容
<c:if test="${not empty list}">
<h1>true</h1>
</c:if>
- choose,相当于switch
<c:choose>
<c:when test="${number == 1}"> //相当于case
<h1>星期1</h1>
</c:when>
<c:when test="${number == 2}">
<h1>星期2</h1>
</c:when>
<c:when test="${number == 3}">
<h1>星期3</h1>
</c:when>
<c:otherwise> //相当于default
<h1>不知道</h1>
</c:otherwise>
</c:choose>
- foreach,for循环
类似 for(int i = 1; i <= 10; i+= 2) {sout(i)}
<c:forEach begin="1" end="10" var="i" step="2">
<h1>${i}</h1>
</c:forEach>
类似 for(int l : list) {sout(l)}
index表示容器下标;count表示第几次循环
<c:forEach items="${list}" var="l" varStatus="s">
<h1>${l} ${s.index} ${s.count}</h1>
</c:forEach>
案例
<table border="1" width="500" align="center">
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
<th>生日</th>
</tr>
<c:forEach varStatus="s" items="${userList}" var="user">
<c:if test="${s.count % 2 == 0}">
<tr bgcolor="#faebd7">
<td align="center">${s.count}</td>
<td align="center">${user.name}</td>
<td align="center">${user.age}</td>
<td align="center">${user.birthStr}</td>
</tr>
</c:if>
<c:if test="${s.count % 2 != 0}">
<tr>
<td align="center">${s.count}</td>
<td align="center">${user.name}</td>
<td align="center">${user.age}</td>
<td align="center">${user.birthStr}</td>
</tr>
</c:if>
</c:forEach>
</table>
Filter
- 过滤器,浏览器访问服务器之前,拦截请求,完成一些特殊功能
- 用于完成通用的操作,登陆验证、统一的编码处理、敏感字符过滤
使用
- 定义一个类,实现
filter
接口
//访问所有资源之前,都会执行该过滤器
@WebFilter("/*")
public class FilterDemo1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("dofilter...");
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
- web.xml配置
<filter>
<filter-name>demo1</filter-name>
<filter-class>com.filter.impl.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>demo1</filter-name>
<!--拦截路径-->
<url-pattern>/*</url-pattern>
</filter-mapping>
执行流程
doFilter
执行
//对req的请求消息增强
System.out.println("dofilterenter...");
//放行,输出对应的资源
filterChain.doFilter(servletRequest,servletResponse);
//对resp增强
System.out.println("dofilterback...");
- 对方法的增强,可以通过设计模式进行增强,共
23种
。装饰模式/代理模式
- 动态代理,实现敏感词过滤。代理对象代理真实对象,达到增强只是对象功能的目的;代理对象和真实对象实现相同的接口
private List<String> list = new ArrayList<>();
public void init(FilterConfig config) throws ServletException {
//加载敏感词汇配置文件
ServletContext servletContext = config.getServletContext();
String realPath = servletContext.getRealPath("/WEB-INF/classes/sensitiveWords.txt");
//读取文件
try {
BufferedReader br = new BufferedReader(new FileReader(realPath));
String line = null;
while ((line = br.readLine()) != null) {
list.add(line);
}
br.close();
System.out.println(list);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(
//被增强的真实对象
request.getClass().getClassLoader(),
request.getClass().getInterfaces(),
new InvocationHandler() {
@Override
//method 方法
//args 参数
//return 方法的返回值
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("getParameter")) {
//增强返回值
String value = (String) method.invoke(request,args);
if (value != null) {
for (String s : list) {
if (value.contains(s)) {
value = value.replace(s, "***");
}
}
}
return value;
}
return method.invoke(request, args);
}
});
chain.doFilter(proxy_req, response);
}
生命周期
- 在服务器启动后,创建filter对象,调用init
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init");
}
- 每一次请求被拦截资源时被执行,执行多次
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
- 服务器关闭后,filter对象被销毁,正常关闭,执行destroy
public void destroy() {
System.out.println("destroy");
}
配置
- 拦截路径配置
具体资源路径 /index.jsp,只访问这个资源时会被拦截
拦截目录 /user/* ,访问user目录下的所有资源,会被拦截
后缀名拦截 *.jsp,访问后缀为jsp的资源,会被拦截
拦截所有资源 /*
- 拦截方式配置,资源被访问的方式
//注解配置,设置dispatcherTypes属性
REQUEST 默认,浏览器直接请求资源
FORWARD 只有转发访问的资源,才能被拦截,路径为/*时,只有转发资源才会被拦截
INCLUDE 包含访问资源
ERROR 错误跳转资源
ASYNC 异步访问资源
//web.xml配置
<filter-mapping>
<filter-name>demo1</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
过滤器链
- 注解配置,按照类名的字符串比较规则,值小的限制性,
AFilter
先于BFilter
执行 - web.xml
<filter-mapping>
谁定义在上面,谁先执行
监听
- 事件:一件事情;事件源:事件发生的地方;监听器:一个对象;
- 注册监听:将事件、事件源、监听器绑定在一起。当事件源上发生了某个事件后,执行监听器代码
使用
- 实现类
//注解配置
@WebListener
public class ContextLoadListener implements ServletContextListener {
@Override
//监听对象创建,服务器启动后创建
public void contextInitialized(ServletContextEvent sce) {
//加载资源文件
ServletContext context = sce.getServletContext();
String contextConfigLocation = context.getInitParameter("contextConfigLocation");
String realPath = context.getRealPath(contextConfigLocation);
//加载进内存
try {
FileInputStream fis = new FileInputStream(realPath);
System.out.println(fis);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
System.out.println("contextInitialized....");
}
@Override
//服务器正常关闭后,被销毁
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed...");
}
}
- web.xml配置
<!--注册监听-->
<listener>
<listener-class>com.listener.impl.ContextLoadListener</listener-class>
</listener>
<!--指定初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>