JSP【java全端课26】

20 阅读9分钟

一、 JSP

1.1.1 JSP初识

JSP(Java Server Pages)是一种动态网页开发技术,它是由 Sun 公司提出的一种基于 Java 技术的 Web 页面制作技术,可以在 HTML 文件中嵌入 Java 代码,使得生成动态内容的编写更加简单。

JSP 最主要的作用是生成动态页面。它允许将 Java 代码嵌入到 HTML 页面中,以便使用 Java 进行数据库查询、处理表单数据和生成 HTML 等动态内容。另外,JSP 还可以与 Servlet 结合使用,实现更加复杂的 Web 应用程序开发。

JSP 的主要特点包括:

  1. 简单:JSP 通过将 Java 代码嵌入到 HTML 页面中,使得生成动态内容的编写更加简单。
  2. 高效:JSP 首次运行时会被转换为 Servlet,然后编译为字节码,从而可以启用 Just-in-Time(JIT)编译器,实现更高效的运行。
  3. 多样化:JSP 支持多种标准标签库,包括 JSTL(JavaServer Pages 标准标签库)、EL(表达式语言)等,可以帮助开发人员更加方便的处理常见的 Web 开发需求。 总之,JSP 是一种简单高效、多样化的动态网页开发技术,它可以方便地生成动态页面和与 Servlet 结合使用,是 Java Web 开发中常用的技术之一。

1.1.2 EL表达式

EL(Expression Language) 是为了使JSP写起来更加简单。表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方法,让Jsp的代码更加简化。

语法结构:${expression}

EL获取变量数据的方法很简单,例如:${username}。它的意思是取出某一范围中名称为username的变量。

因为我们并没有指定哪一个范围的username,所以它会依序从Page、Request、Session、Application范围查找。由于它只会从作用域中获取数据,所以被展示的数据一定要先存储到作用域,才可以使用EL表达式获取

EL 提供"."和"[ ]"两种运算符来取数据。如果被访问的数据是对象时,可以使用"."操作符获取属性,如果是数组,则可以使用"[]"获取数据。

案例代码:

<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page import="com.mytest.schedule.pojo.SysUser" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
   <%
       //1.变量测试
       int age=10;
       String name="张三丰";
       SysUser user=new SysUser();
       user.setUsername("李四");
       String[] strs={"珍珍","爱爱","莲莲"};
       request.setAttribute("strings",strs);
       request.setAttribute("u1",user);
       request.setAttribute("uage",age);
       request.setAttribute("uname",name);

       //2.内置对象测试
       request.setAttribute("reqkey","request的数据");
       session.setAttribute("sessionkey","session的数据");
       application.setAttribute("applicationkey","application的数据");
   %>
   <h1>学生姓名:${uname},年龄:${uage}</h1>
   <h1>用户姓名:${u1.username}</h1>
   <h1>猪八戒喜欢:${strings[0]},${strings[1]},${strings[2]}</h1>
   request中的数据:${reqkey}<br>
   session中的数据:${sessionkey}<br>
   application中的数据:${applicationkey}<br>

</body>
</html>

注意: EL表达式只负责取数据,无法循环遍历数据

1.1.3 JSTL标签库

JSP 标准标签库(JSP Standard Tag Library,JSTL)是一个实现 Web应用程序中常见的通用功能的定制标记库集,这些功能包括迭代和条件判断、数据管理格式化、XML 操作以及数据库访问。

JSTL使用步骤:

1.添加依赖包(这里的依赖包会因为tomcat版本以及配置环境的区别而有变化)

将两个 jar 文件拷贝到 /WEB-INF/lib/

2.jsp页面中引入核心标签库

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

3.页面中使用c:forEach标签循环数据

<c:forEach>标签有如下属性:

属性描述是否必要默认值
items要被循环的信息
begin开始的元素(0=第一个元素,1=第二个元素)0
end最后一个元素(0=第一个元素,1=第二个元素)Last element
step每一次迭代的步长1
var代表当前条目的变量名称
varStatus代表循环状态的变量名称

<c:if>标签有如下属性:

属性描述是否必要默认值
test条件
var用于存储条件结果的变量
scopevar属性的作用域page

案例代码:

<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
   <%
       List list=new ArrayList<>();
       list.add("张三1");
       list.add("李四2");
       list.add("王五3");
       list.add("赵六4");
       request.setAttribute("ulist",list);  //必须先存再取
   %>
   <table width="300px" border="1" cellspacing="0">
       <tr>
           <td>序号</td>
           <td>姓名</td>
       </tr>
       <c:if test="${ulist.size()>0}">
               <!--循环取出列表数据-->
               <c:forEach items="${ulist}" var="uname" varStatus="i">
               <tr>
                   <td>${i.count}</td>
                   <td>${uname}</td>
               </tr>
               </c:forEach>
       </c:if>
       <c:if test="${ulist.size()==0}">
           <tr>
               <td colspan="2">无数据</td>
           </tr>
       </c:if>
   </table>
    
       <!--从1循环到100-->
       <c:forEach begin="1" end="100" var="num">${num}</c:forEach>
</body>
</html>

2 过滤器

2.1.1 过滤器概述

Filter,即过滤器,是JAVAEE技术规范之一,作为目标资源的请求进行过滤的一套技术规范,是Java Web项目中最为实用的技术之一

  • Filter接口定义了过滤器的开发规范,所有的过滤器都要实现该接口;

  • Filter的工作位置是项目中所有目标资源之前,容器在创建HttpServletRequest和HttpServletResponse对象后,会先调用Filter的doFilter方法;

  • Filter的doFilter方法可以控制请求是否继续,如果放行,则请求继续,如果拒绝,则请求到此为止,由过滤器本身做出响应;

  • Filter不仅可以对请求做出过滤,也可以在目标资源做出响应前,对响应再次进行处理;

  • Filter是GOF中责任链模式的典型案例;

  • Filter的常用应用包括但不限于:登录权限检查、解决网站乱码、过滤敏感字符、日志记录、性能分析... ...;

  • 公司前台对来访人员进行审核,如果是游客则拒绝进入公司,如果是客户则放行 。客户离开时提醒客户不要遗忘物品;

  • 停车场保安对来访车辆进行控制,如果没有车位拒绝进入。如果有车位,发放停车卡并放行,车辆离开时收取请车费;

  • 地铁验票闸机在人员进入之前检查票,没票拒绝进入,有票验票后放行,人员离开时同样验票;

过滤器工作位置图解:

Filter接口API:

  • 源码
package jakarta.servlet;
import java.io.IOException;
public interface Filter {
    default public void init(FilterConfig filterConfig) throws ServletException {
    }
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException;
    default public void destroy() {
    }
}
  • API目标
API目标
default public void init(FilterConfig filterConfig)初始化方法,由容器调用并传入初始配置信息filterConfig对象
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)过滤方法,核心方法,过滤请求,决定是否放行,响应之前的其他处理等都在该方法中
default public void destroy()销毁方法,容器在回收过滤器对象之前调用的方法

2.1.2 过滤器使用

目标:开发一个日志记录过滤器。

  • 用户请求到达目标资源之前,记录用户的请求资源路径;
  • 响应之前记录本次请求目标资源运算的耗时;
  • 可以选择将日志记录进入文件,为了方便测试,这里将日志直接在控制台打印;

定义一个过滤器类,编写功能代码:

public class LoggingFilter  implements Filter {
    private SimpleDateFormat dateFormat =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 参数父转子
        HttpServletRequest request =(HttpServletRequest)  servletRequest;
        HttpServletResponse  response =(HttpServletResponse)  servletResponse;
        // 拼接日志文本
        String requestURI = request.getRequestURI();
        String time = dateFormat.format(new Date());
        String beforeLogging =requestURI+"在"+time+"被请求了";
        // 打印日志
        System.out.println(beforeLogging);
        // 获取系统时间
        long t1 = System.currentTimeMillis();
        // 放行请求
        filterChain.doFilter(request,response);
        // 获取系统时间
        long t2 = System.currentTimeMillis();
        //  拼接日志文本
        String afterLogging =requestURI+"在"+time+"的请求耗时:"+(t2-t1)+"毫秒";
        // 打印日志
        System.out.println(afterLogging);
    }
}
  • 说明
    • doFilter方法中的请求和响应对象是以父接口的形式声明的,实际传入的实参就是HttpServletRequest和HttpServletResponse子接口级别的,可以安全强转;
    • filterChain.doFilter(request,response); 这行代码的功能是放行请求,如果没有这一行代码,则请求到此为止;
    • filterChain.doFilter(request,response);在放行时需要传入request和response,意味着请求和响应对象要继续传递给后续的资源,这里没有产生新的request和response对象;

定义两个Servlet作为目标资源:

  • ServletA
@WebServlet(urlPatterns = "/servletA",name = "servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 处理器请求
        System.out.println("servletA处理请求的方法,耗时10毫秒");
        // 模拟处理请求耗时
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
  • ServletB
@WebServlet(urlPatterns = "/servletB", name = "servletBName")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 处理器请求
        System.out.println("servletB处理请求的方法,耗时15毫秒");
        // 模拟处理请求耗时
        try {
            Thread.sleep(15);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

配置过滤器以及过滤器的过滤范围:

  • web.xml
<?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">
    <!--配置filter,并为filter起别名-->
   <filter>
       <filter-name>loggingFilter</filter-name>
       <filter-class>com.mytest.filters.LoggingFilter</filter-class>
   </filter>
    <!--为别名对应的filter配置要过滤的目标资源-->
    <filter-mapping>
        <filter-name>loggingFilter</filter-name>
        <!--通过映射路径确定过滤资源-->
        <url-pattern>/servletA</url-pattern>
        <!--通过后缀名确定过滤资源-->
        <url-pattern>*.html</url-pattern>
        <!--通过servlet别名确定过滤资源-->
        <servlet-name>servletBName</servlet-name>
    </filter-mapping>
</web-app>
  • 说明

    • filter-mapping标签中定义了过滤器对那些资源进行过滤;
    • 子标签url-pattern通过映射路径确定过滤范围;
      • /servletA 精确匹配,表示对servletA资源的请求进行过滤;
      • *.html 表示对以.action结尾的路径进行过滤;
      • /* 表示对所有资源进行过滤;
      • 一个filter-mapping下可以配置多个url-pattern;
    • 子标签servlet-name通过servlet别名确定对那些servlet进行过滤;
      • 使用该标签确定目标资源的前提是servlet已经起了别名;
      • 一个filter-mapping下可以定义多个servlet-name;
      • 一个filter-mapping下,servlet-name和url-pattern子标签可以同时存在;

过滤过程图解:

2.1.3 过滤器生命周期

过滤器作为web项目的组件之一,和Servlet的生命周期类似,略有不同,没有servlet的load-on-startup的配置,默认就是系统启动立刻构造。

阶段对应方法执行时机执行次数
创建对象构造器web应用启动时1
初始化方法void init(FilterConfig filterConfig)构造完毕1
过滤请求void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)每次请求多次
销毁default void destroy()web应用关闭时1次

测试代码:

@WebServlet("/*")
public class LifeCycleFilter implements Filter {
    public LifeCycleFilter(){
        System.out.println("LifeCycleFilter constructor method invoked");
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("LifeCycleFilter init method invoked");       
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("LifeCycleFilter doFilter method invoked");
        filterChain.doFilter(servletRequest,servletResponse);
    }
    @Override
    public void destroy() {
        System.out.println("LifeCycleFilter destory method invoked");
    }
}

2.1.4 过滤器链的使用

一个web项目中可以同时定义多个过滤器,多个过滤器对同一个资源进行过滤时,工作位置有先后,整体形成一个工作链,称为过滤器链。

  • 过滤器链中的过滤器的顺序由filter-mapping顺序决定;
  • 每个过滤器过滤的范围不同,针对同一个资源来说,过滤器链中的过滤器个数可能是不同的;
  • 如果某个Filter是使用ServletName进行匹配规则的配置,那么这个Filter执行的优先级要更低;

图解过滤器链:

过滤器链功能测试:

  • 定义三个过滤器,对目标资源Servlet的请求进行过滤;

  • 目标Servlet资源代码;

@WebServlet("/servletC")
public class ServletC extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servletC service method  invoked");
    }
}
  • 三个过滤器代码:
public class Filter1  implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filter1 before chain.doFilter code invoked");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("filter1 after  chain.doFilter code invoked");
    }
}

public class Filter2 implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filter2 before chain.doFilter code invoked");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("filter2 after  chain.doFilter code invoked");
    }
}

public class Filter3 implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filter3 before chain.doFilter code invoked");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("filter3 after  chain.doFilter code invoked");
    }
}
  • 过滤器配置代码:
<?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">
    <filter>
        <filter-name>filter1</filter-name>
        <filter-class>com.mytest.filters.Filter1</filter-class>
    </filter>
    <filter>
        <filter-name>filter2</filter-name>
        <filter-class>com.mytest.filters.Filter2</filter-class>
    </filter>
    <filter>
        <filter-name>filter3</filter-name>
        <filter-class>com.mytest.filters.Filter3</filter-class>
    </filter>
    <!--filter-mapping的顺序决定了过滤器的工作顺序-->
    <filter-mapping>
        <filter-name>filter1</filter-name>
        <url-pattern>/servletC</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>filter2</filter-name>
        <url-pattern>/servletC</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>filter3</filter-name>
        <url-pattern>/servletC</url-pattern>
    </filter-mapping>
</web-app>

工作流程图解:

8.5.5 注解方式配置过滤器

@WebFilter注解的使用:

注解源码通过idea查看
  • 一个比较完整的Filter的XML配置。
<!--配置filter,并为filter起别名-->
<filter>
    <filter-name>loggingFilter</filter-name>
    <filter-class>com.mytest.filters.LoggingFilter</filter-class>
    <!--配置filter的初始参数-->
    <init-param>
        <param-name>dateTimePattern</param-name>
        <param-value>yyyy-MM-dd HH:mm:ss</param-value>
    </init-param>
</filter>
<!--为别名对应的filter配置要过滤的目标资源-->
<filter-mapping>
    <filter-name>loggingFilter</filter-name>
    <!--通过映射路径确定过滤资源-->
    <url-pattern>/servletA</url-pattern>
    <!--通过后缀名确定过滤资源-->
    <url-pattern>*.html</url-pattern>
    <!--通过servlet别名确定过滤资源-->
    <servlet-name>servletBName</servlet-name>
</filter-mapping>
  • 将xml配置转换成注解方式实现。
@WebFilter(
        filterName = "loggingFilter",
        initParams = {@WebInitParam(name="dateTimePattern",value="yyyy-MM-dd HH:mm:ss")},
        urlPatterns = {"/servletA","*.html"},
        servletNames = {"servletBName"}
)
public class LoggingFilter  implements Filter {
    /* 内部代码 略 */
}