监听器

介绍

  • 一组来自于Servlet规范中的接口【有8个接口】
  • 存在于Tomcat中Servlet-api.jar
  • 监听器的接口要求开发人员亲自实现,【jar包中并没有对应的实现类】
  • 监听器接口用于监控【作用域对象生命周期变化时刻】和【作用域对象共享数据变化时刻】
  • 一般网站中监听器接口实现类 都在 listener 包下

什么叫做作用域对象

  1. 在Servlet规范中,认为在服务端内存中可以在某些条件下为两个Servlet进行数据共享的对象,被称为作用域对象
  2. ServletContext 全局作用域对象
  3. HttpSession 会话作用域对象
  4. HttpServletRequest 请求作用域对象

  5. 监听器接口实现类开发规范

  6. 根据监听的实际情况,选择对应的监听器接口进行实现

  7. 重写监听器接口中声明的【监听事件处理方法】
  8. 在web.xml文件中将监听器接口实现类注册到Http服务器中

    1. <!--将监听器接口实现类注册到Tomcat-->
    2. <listener>
    3. <!--在里面写上接口实现类的路径-->
    4. <listener-class>com.yixuexi.listener.OneListener</listener-class>
    5. </listener>

    ServletContextListener接口

    作用:

    通过这个接口可以合法的检测全局作用域对象被初始化时刻和被销毁时刻

    两个方法

    1. public class listener implements ServletContextListener {
    2. @Override
    3. public void contextInitialized(ServletContextEvent sce) {
    4. //此方法在全局作用域对象被Http服务器创建的时候调用
    5. }
    6. @Override
    7. public void contextDestroyed(ServletContextEvent sce) {
    8. //此方法在全局作用域对象被Http服务器销毁的时候调用
    9. }
    10. }
  • 因为全局作用域对象是Tomcat服务器负责声明/销毁
  • 所以在服务器打开的时候同时也声明了全局作用域对象所以执行 init..
  • 服务器关闭的时候同时销毁全局作用域对象执行des..

ServletContext application = sce.getServletContext(); // 得到全局作用域对象

ServletContextAttributeListener接口

通过这个接口合法的检测全局作用域对象共享数据变化时刻

三个方法

  1. public class OneListener implements ServletContextAttributeListener {
  2. @Override
  3. public void attributeAdded(ServletContextAttributeEvent scae) {
  4. //在全局作用域对象添加共享数据的时候被触发
  5. }
  6. @Override
  7. public void attributeRemoved(ServletContextAttributeEvent scae) {
  8. //在全局作用域对象删除共享数据的时候被触发
  9. }
  10. @Override
  11. public void attributeReplaced(ServletContextAttributeEvent scae) {
  12. //在全局作用域对象更新共享数据的时候被触发
  13. }
  14. }

全局作用域对象的变化时刻

  • ServletContext application = request.getServletContext(); //得到全局作用域对象
  • application.setAttribute(“key1”,100); // 新增共享数据
  • application.setAttribute(“key1”,200); // 更新 key1从100变200
  • application.removeAttribute(“key1”) // 通过key 删除对应的共享数据

过滤器

介绍

  • 来自Servlet规范下的接口 在Tomcat存在 Servlet-api.jar包中
  • Filter接口实现类由开发人员负责提供,Http服务器不负责提供
  • Filter接口在Http服务器调用资源文件之前,对Http服务器进行拦截
  • 开发过滤器接口实现类放到filter包下

具体作用

  • 拦截Http服务器帮助Http服务器检测当前请求的合法性
  • 拦截Http服务器,对当前请求进行增强操作

Filter接口实现类开发步骤

  1. 创建一个Java类,实现Filter接口 (选择javax.servlet包下的Filter接口)
  2. 重写Filter接口中的doFilter方法
  3. 在web.xml中将过滤器接口实现类注册得到Http服务器上

    1. <!--将过滤器类文件的路径交给Tomcat-->
    2. <filter>
    3. <!--变量名 里面存储了Filter的文件路径-->
    4. <filter-name>oneFilter</filter-name>
    5. <!--里面填写Filter的文件路径-->
    6. <filter-class>com.yixuexi.Filter</filter-class>
    7. </filter>
    8. <!-- 通知tomcat在调用那个资源文件时,需要被当前的过滤器拦截-->
    9. <filter-mapping>
    10. <!--告诉这个oneFilter过滤器-->
    11. <filter-name>oneFilter</filter-name>
    12. <!--在调用mm.jpg 文件的时候 要进行过滤操作-->
    13. <url-pattern>/mm.jpg</url-pattern>
    14. </filter-mapping>

    具体实现

    ```java public class OneFilter implements Filter{ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

    1. //filterChain对象负责 在判断合法的情况下把请求对象 和响应对象交还给Tomcat 【称为放行】
    2. //1.通过拦截的请求对象,得到请求包中的请求信息,从而得到用户真事年龄【访问该网站需要>18】
    3. String age = servletRequest.getParameter("age");
    4. //2. 根据年龄,帮助Http服务器判断
    5. if (Integer.valueOf(age) < 18){
    6. //如果小于18岁,则代替服务器 拒绝本次访问,【响应对象的响应体中有数据的话(tomcat会认为这次[请求+响应结束])】
    7. servletResponse.setContentType("text/html;charset=utf-8");
    8. PrintWriter out = servletResponse.getWriter();
    9. out.print("<center><font style='color:red;font-size:0pt; color:#6a8759;">本次作业为满18岁禁止访问</font></center>");
    10. }else{
    11. // 如果大于等于18的话就可以访问了
    12. // 将拦截的请求对象和响应对象交换给tomcat filterChain.doFilter() 交还给Tomcat
    13. filterChain.doFilter(servletRequest,servletResponse);

    }

}

  1. > 以上是验证请求的合法性
  2. - 如果有100百个Servlet实现类 post请求) 那么在这100个实现类都需要对其进行servletRequest.setCharacterEncoding("utf-8"); 此时就可以通过过滤器为其进行增强操作
  3. - <url-pattern>/*</url-pattern> <!--通知tomcat在调用所有资源文件前 都需要去调用OneFilter进行拦截-->
  4. <a name="xPite"></a>
  5. ## filter过滤器拦截地址格式(web.xml)
  6. <a name="0QE4y"></a>
  7. ### 命令格式
  8. ```xml
  9. <filter-mapping>
  10. <!--告诉这个oneFilter过滤器-->
  11. <filter-name>oneFilter</filter-name>
  12. <!--在调用拦截地址文件的时候 要进行过滤操作-->
  13. <url-pattern>/拦截地址</url-pattern>
  14. </filter-mapping>

命令作用

拦截地址通知Tomcat在调用何种文件的时候之前需要调用OneFilter过滤及进行拦截

拦截命令的几种写法

  • 要求Tomcat在调用某一个具体文件之前,来调用OneFilter拦截

/img/mm.jpg

  • 要求Tomcat在调用某一个文件夹下所有的资源文件之前,来调用OneFilter拦截

/文件夹/*

  • 要求Tomcat在调用任意一个文件夹下的某种类型文件之前,都要用OneFilter过滤

*.jpg
不管是哪里的图片 访问时 都会被拦截
不加 /

  • 要求Tomcat在调用网站中任意文件时,都要调用OneFilter来拦截

/*

防止用户恶意登录

问题:用户避开登陆验证, 直接通过地址栏访问内部资源文件,被称为恶意登录

令牌机制

用户通过login.html登录成功后 会配发一块令牌,在调用后续的资源文件时,动态资源文件会首先判断有没有session
令牌 == 信物 (Session 私人储物柜)
在登录验证的时候给用户创建一个Session 使用无参创建
在用户调用动态资源文件的时候,使用 HttpSession session = request.getSession(false); 如果返回的是null 就说明用户没有使用登陆验证,属于恶意登录

缺点:

  1. 增加开发难度,一个每个Servlet都要写 HttpSession session = request.getSession(false);
  2. 不能对静态文件进行保护(只能保护动态文件【class文件】)

    使用过滤器解决 令牌机制的缺点

    通知Tomcat在调用任意文件之前都要先调用过滤器
    /* ```java 在doFilter()方法中 向拦截的请求索要他的HttpSession //父接口中没有 getSession()方法 向下转型 转成 HttpServletRequest HttpServletRequest request = (HttpServletRequest)servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse;

HttpSession session = req.getSession(false); if(session == null){ //拒绝服务 response.sendRedirect() }else{
// 有令牌 放行 filterChain.doFilter(req,resp); }

  1. <a name="dEFPX"></a>
  2. ### 出现的问题
  3. 全部的文件都被过滤器拦截了
  4. <a name="XSqd7"></a>
  5. ### 解决方案
  6. 放行登录页面 和登录验证
  7. ```java
  8. //父接口中没有 getSession()方法 向下转型 转成 HttpServletRequest
  9. HttpServletRequest request = (HttpServletRequest)servletRequest;
  10. // 得到uri 判断里面包含不包含 login 如果包含就放行
  11. String uri = request.getRequestURI();
  12. // 返回当前字符串中 在该字符串中第一次出现的位置 如果没有出现 返回-1
  13. // 或者是默认访问 默认访问的uri为 /myWeb/ 也需要放行
  14. if(uri.indexOf("login") != -1 || "/myWeb/".equals(uri)){
  15. // 放行
  16. filterChain.doFilter(servletRequest,servletResponse);
  17. return;
  18. }
  19. //如果本次的请求访问的是其他的文件需要得到令牌
  20. HttpSession session = request.getSession(false);
  21. //如果session不是null 说明用户通过了登录验证
  22. if (session != null) {
  23. //放行
  24. filterChain.doFilter(servletRequest,servletResponse);
  25. return;
  26. }
  27. //程序能执行到这里说明没有柜子【令牌】 输于恶意登录 请求转发
  28. request.getRequestDispatcher("/User_login_error.html").forward(servletRequest,servletResponse);

JSP

为什么学习JSP?

  • 响应对象只适合将较少的数据量写入到响应体中,如果Html代码多,用jsp
  • 在servlet接口实现类中 写的HTML代码特别多【冗余】就可以用jsp代替响应对象将处理结果写入到响应体中

    什么是JSP规范

  • JSP来自JAVAEE规范中的一种

  • JSP制定了如何开发jsp文件代替响应对象将输出结果写入响应体中的开发流程
  • JSP规范制定了Http服务器应该如何管理调用这些JSP文件

    JSP特点

  • JSP文件在执行中,自动将文件所有内容写入到响应体中,从而节省了书写 out.print();

  • 只能将存在于 当前网站下的 web文件夹下

    如果在jsp中书写java命令

    在jsp文件中,只有书写在执行标记中内容才会被当作java命令
    <%
    int i = 10 ;
    int j = 20;
    ……..
    %>

    • 可以声明java变量
    • 运算表达式 【数学运算】【关系运算】【逻辑运算】
    • 控制语句

将java命令的运行结果输出到浏览器
输出执行标记 <%= 变量名 %>
可以写运算表达式

输出控制语句


<%
for (int i = 0; i <= 100; i++){
%>
i = <%= i %>
// 在三不管的地方写

<%
}
%>

JSP内置对象

request(请求对象)


1.读取请求包中的
信息 http://localhost:8080/myWeb/index2.jsp?username=zhangsan&password=123456
<%
String username = request.getParameter(“username”);
String password = request.getParameter(“password”);
//System.out.println(username + “:” + password);
%>
来访用户姓名:<%= username %>

来访用户密码:<%= password %>

session


不需要再进行 request.getSession() 自带的 直接用 即可 【session】

application


直接application.setAttribute(); 添加属性

Servlet和jsp实现分工操作

Servlet和JSP的分工

Servlet:处理业务,并得到处理结果
【饭店的大厨】
JSP:不负责业务的处理,主要任务将Servlet中处理的结果写入到响应体中
【饭店中的传菜员】

Servket和JSP之间的调用关系

在Servlet工作完毕后,一般是通过请求转发的方式,向tomcat申请调用JSP

Servlet和JSP之间如何实现数据共享

我们的Servlet将处理结果去添加到请求作用域对象中,jsp文件在运行的时候,从请求作用域对象中得到处理结果

面试题

Http服务器调用JSP文件步骤?

  1. Http服务器首先将JSP文件内容【编辑】为一个Servlet接口实现类 .java
  2. Http服务器将Servlet接口实现类【编译】为一个class文件 .class
  3. Http服务器负责创建这个class的实例对象,【Servlet对象】
  4. Http服务器通过Servlet实例对象调用一个_jspService方法来将jsp文件上的内容写入到响应体中

Http服务器【编辑】和【编译】JSP文件的位置

  • C:\Users\【登录windows系统的角色名称】.IntelliJIdea2019.2\system\tomcat\Tomcat_9_0_35_01Servlet_19\work\Catalina\localhost\myWeb\org\apache\jsp
  • 标准答案:我在work文件夹下看到了jsp文件被编译成了Servlet

EL表达式

概述

什么是EL工具包

  1. 由JAVA开发的一个jar包
  2. 作用是降低JSP文件开发时java命令的难度
  3. Tomcat服务器本身自带了EL工具包(lib/el-api.jar)

    JSP的作用

  • 代替响应的对象,将doGet/doPost的执行结果写入到响应体中【输出】

    传统JSP开发步骤

  • 从【全局/请求/会话】作用域中拿到key对应的value

  • 强转成自己需要的类型的数据
  • 输出到页面上 <%= %>

    EL简化JSP的开发步骤

  • 值:${requestScope.key1}

  • 值:${applicationScope.key1}
  • 值:${sessionScope.key1}

    命令格式

    ${ 作用域对象别名.key}
    key不需要引起来

    命令作用

  • EL表达式是EL工具包提供一种特殊命令格式【表达式命令格式】

  • EL表达式在JSP文件上来使用
  • 负责在JSP文件上面从作用域对象中读取指定的共享数据,并写到响应体中

    EL表达式中作用域的别名

    JSP文件可以使用的作用域对象

  1. ServletContext application : 全局作用域对象
  2. HttpSession session : 会话作用域对象
  3. HttpServletRequest request : 请求作用域对象
  4. 【只能在JSP出现的作用域对象】PageContext pageContext : 当前页作用域对象

JSP中独有的 Servlet中不存在。
在当前页作用域对象中存放的数据 仅能在当前jsp页面中使用
不能共享给其他Servlet和JSP文件
主要开发中主要用于 JSTL标签和JSP文件之间数据共享给

EL表达式提供作用域对象别名

  • ${applicationScope.共享数据名} 全局作用域对象别名
  • ${sessionScope.共享数据名} 会话作用域对象别名
  • ${requestScope.共享数据名} 请求作用域对象别名
  • ${pageScope.共享数据名} 当前页作用域对象别名

    EL表达式将引用对象属性写入响应体

  • 格式:${作用域对象别名.key.属性名}

  • 作用:从作用域对象中读取指定共享数据所关联的引用对象的属性值
  • 并自动写入到响应体中
  • 属性名:一定要与引用类型属性名完全一致 包括大小写 【私有属性也可以直接调用。】

EL表达式中没有提供遍历集合的方法,因此无法从作用域中的集合中得到元素 JSTL技术有效的弥补EL不能遍历集合输出的问题

EL表达式简化版(常用)

命令格式

${共享数据名}
少了作用域对象别名

命令作用

EL表达式允许在开发的时候省略作用域对象别名

工作原理

  1. 由于EL简化版没有指定作用域对象,所以在执行的时候 用的【猜的算法】
  2. 首先到【pageContext】定位共享数据,如果存在那么就直接读取结束执行
  3. 如果没有就去下一个 【request】 ——>【session】 ———【application】
  4. 如果都没有 就返回一个null

    存在隐患

  • 降低程序执行的速度【南辕北辙】
  • 容易导致数据定位错误

    应用场景

  • 设计目的:简化从当前页作用域对象【pageContext】中读取共享数据并输出的难度

  • EL简化版尽管存在了很多隐患 但是在实际开发过程中,开发人员为了节省时间 都是用简化版,而不是用标准版

    EL支持的运算表达式

    前提

  • 在JSP文件有时需要将读取共享数据进行一番运算之后,将运算结果写入到响应体中

  • 允许将key对应的value做数学运算 【不用考虑类型转换,String自动转换成Integer 】

    都支持那些运算

  • 数学运算 + - ……….

  • 关系运算 > < != …….
  • 三目运算符 ? :
  • 逻辑运算 && || !
  • EL表达式支持的内置对象

    命令格式 : ${param.请求参数名}

  • 作用:通过请求对象读取当前请求包中的请求参数内容,并将请求参数的内容写入到响应体【通过key得到value 】(得到来访者的姓名/密码….)

    命令格式:${paramValues.请求参数名称[下标]}

  • 如果浏览器发送的请求参数是一个请求参数是一个请求参数关联多个值

  • Http://localhost:8080/myWeb/index_2.jsp?pageNo=1&pageNo=2&pageNo=3
  • 就有一个值 在请求包中 以数组的形式存在 下标从0开始
  • 此时可以通过paramValues.* 来读取请求参数下指定位置的值,并且写入响应体

${initParam.value}

在web.xml中编写下面代码表示在tomcat启动的时候写到全局作用域对象中去

value
Hello World

用initParam.value就可以读取 到 hello world
EL表达式 常见异常
javax.el.PropertyNotFoundException // 在对象中没有找到指定的属性

EL表达式 缺陷问题

  1. 执行读取作用域对象数据,不能向作用域中写入数据和更改数据
  2. 不支持控制语句 if while …….
  3. 如果单独使用EL表达式,无法确保JSP文件中所有的JAVA命令都被替换
  4. 实际开发中 jsp文件中不能出现任何一条java命令【规矩】

相对路径和绝对路径的区别

JSTL标签工具类

介绍

JSTL = JSP Standrad(标准) tag(标签) lib jsp中标准的标签工具类库

出现的原因

  • 在JSP文件中不能出现java命令【以持续10多年(风气)】
  • 为了不出现java命令,就出现了JSTL 【披着html的java命令】
  • 由sun公司提供

    组成

  • 核心标签【java在jsp基本功能进行封装 if while】

  • sql标签 【JDBC在jsp上使用功能】
  • xml标签【DOM4J在JSP使用功能】
  • Format标签【JSP文件格式转换】

    配置

  • 导入依赖的jar:jstl.jar standrad.jar

  • 先把jar包赋值到WEB-INF 下面新建一个lib文件夹 放到里面
  • 在file ——->Project Structure ——->Modules——-> 选中需要导包的项目 ———>Dependencies ——-> 右边加号添加jar包
  • 在jsp文件引入JSTL中core包依赖约束
  • <%@ taglib uri=”http://java.sun.com/jsp/jstl/core“ prefix=”c” %>

    标签的使用介绍

    :

    作用:在jsp文件上设置作用域对象的共享数据
    使用:
    代替:<%
    session.setAttribute(“key”,”10”);
    %>

    其中的属性含义:

    scope 范围 指定当前要操作的作用域对象
    var 声明域对象中的关键字 也就是key
    value 存入的共享数据

    EL表达式和JSTL联合使用






    ${requestScope.age}

    作用 在JSP文件上控制那些内容可以写入到响应体中

    内容

    作用:在jsp文件上实现多分支选择判断,决定哪一个内容写入响应体

    内容
    ……..
    内容

    一个c:when 相当于一个else if
    c:otherwise相当于else 都不执行 他执行

    第一种使用方案


    var 定义一个变量i 开始值为1 结束值为5[<=] step:以一递增[可省略]
    会自动的把i 放到当前页作用域对象中 ${pageScope.i}
    第二种使用方案
    遍历list和set集合


    ${stu.age}



    items表示遍历那个集合,用el表达式来得到
    stu表示的是 Student stu
    遍历Map集合


    告诉要遍历那个集合,声明一个循环变量
    循环变量里面保存了键值对 【key_Value.key得到key】【key_Value.value得到value】 【key_Value.value.id】 得到value中的id