Java-第十三部分-Web

229 阅读18分钟

资源分类

  • 静态资源,所有用户访问后,得到的结果都是一样的。html/css/js
  • 动态资源,每个用户访问相同的资源后,得到的结果不一样。servlet/jsp/php/asp
  • 访问静态资源时,浏览器中,有静态资源的解释引擎,解析静态资源
  • 动态资源被访问时,先转换为静态资源,再返回给浏览器响应

网络通信三要素

  • 找到服务器的电脑
  • IP 电子设备在网络中的唯一标识
  • 端口 应用程序在计算机中的唯一标识 0~65536
  • 传输协议 数据传递的规则,网络上交流的语言方式。TCP,安全协议,三次握手,四次挥手,速度稍慢;UDP,不安全协议,速度快

Web服务器软件

  • 服务器,安装了服务器软件的计算机
  • 服务器软件,接受用户的请求,处理请求,做出响应
  • Web服务器软件,可以部署web项目,让用户通过浏览器来访问这些项目
  • 常见web服务器软件,weblogic,支持所有的JavaEE规范,是企业级开发中使用的技术规范的总和,一共规定13项大的规范;webSphereJBOSSTomcat,apache基金组织,中小型,仅仅支持少量的JavaEE规范

mac

  • 安装 brew install tomcat
  • 启动 catalina run
  • 关闭 catalina stop
  • 配置文件,修改端口/usr/local/Cellar/tomcat/10.0.11/libexec/conf/server.xml
  • 可以把端口改成8080是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 image.png

细节

  • 一次可以发送多个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>