Java Web三大组件-Servlet、过滤器、监听器

2023-11-24 16:00 李志远 513

目标

  • 过滤器实现后台管理的权限控制
  • 监听器 - 实现统计当前在线人数
  • Ajax -1 原生JS实现

1.回顾

  • 会话-session - 客户端浏览器与服务端的一次连接[多次的请求与响应]过程
  • 会话跟踪 - 会话的过程中可以保持数据共享的状态这样一种技术 - 解决HTTP协议的无状态性
  • Cookie
  • 创建Cookie new Cookie(name,value); --key-value[字符串]
  • 设置cookie存活期 setMaxAge(秒) - 0立即删除cookie, -1 [关闭浏览器时删除]
  • 发送至浏览器 response.addCookie(ck);
  • 在页面读取请求对象中的cookie 数据Cookie[] cks= request.getCookies()-- EL表达式 {{cookie.name.value}
  • HttpSession - getId() ,创建时间,isNew(),非活动时间间隔setInactiveInterval(), invalidate()
  • 会话如何产生: request.getSession(),request.getSession(true|false)
  • 会话失效有三种方式: 会话对象.invalidate(); 非活动时间超时; 关闭浏览器

2.过滤器

  • Java Web 服务端三大组件
  • Servlet 、过滤器Filter、监听器Listener
  • web.xml 进行配置运行
  • @WebServlet
  • @WebFilter
  • @WebListener

2-1 过滤器简介

  • 驻留在服务端,针对客户端与服务端之前的请求与响应信息进行过滤
  • 主要两个作用:

   设置请求的字符集编码

   权限控制—必须登录的用户才能访问后台

  • 过滤器入门示例
step1: 创建过滤器类 MyFilter实现接口javax.servlet.Filter;

step2: 重写接口中的方法
  public class MyFilter implements Filter{

        //执行过滤处理
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
                        throws IOException, ServletException {
                System.out.println("1.请求到达MyFilter....");
        }
}  

step3: 配置过滤器
    web.xml:
  <!-- Filter -->
        <filter>
                <filter-name>MyFilter</filter-name>
                <filter-class>com.woniu.web.MyFilter</filter-class>
        </filter>
        
        <filter-mapping>
                <filter-name>MyFilter</filter-name>
                <url-pattern>/hello</url-pattern>
        </filter-mapping>
            
-----------------------------------------
 <!-- Filter -->
        <filter>
                <filter-name>MyFilter</filter-name>
                <filter-class>com.woniu.web.MyFilter</filter-class>
        </filter>
        
        <filter-mapping>
                <filter-name>MyFilter</filter-name>
                <url-pattern>*.jsp</url-pattern>  
             <!-- /hello 斜杠必须加    *.jsp *.html *.css *.* 则不能加/ -->
        </filter-mapping>

step4:请求目标资源servlet,请求会被过滤器拦截   

当然,同Servlet一样,我们也可以使用注解来配置Filter

@WebFilter("/*")
public class MyFilter implements Filter{
      
}

2-2 过滤器应用-1

  • 将请求的中的字符集编码进行统一处理
  • 这样就不需要在每个 Servlet中处理请求字符集编码
package com.woniu.mall.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;

/**
 * 所有请求执行字符集编码过滤
 */
@WebFilter(urlPatterns = "/*",initParams = @WebInitParam(name = "charset",value = "UTF-8"))
public class EncodingFilter implements Filter {
        private FilterConfig filterConfig;
        /**
         * @see Filter#init(FilterConfig)
         */
        public void init(FilterConfig filterConfig) throws ServletException {
                this.filterConfig = filterConfig;
        }

        /**
         * 执行过滤处理
         */
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                //System.out.println("EncodingFilter...");
                //获取初始化配置参数charset
                String charSet = filterConfig.getInitParameter("charset");
                request.setCharacterEncoding(charSet);
                // 放行
                chain.doFilter(request, response);
        }
}

2-3 过滤器应用-2

  • 对项目后台进行权限控制 - 没有登录的情况下,对后台管理的任何操作都会执行过滤处理,将请求不做放行
实现步骤:
step1: 添加过滤器,将所有后台管理的请求路径设置为过滤器执行的路径模式
/**
         * 过滤
         */
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                
                 //获取当前请求相关联的session对象
                HttpServletRequest req = (HttpServletRequest)request; //类型转换
                HttpSession ses = req.getSession();
                //获取session作用域中登录的管理员
                Admin loginAdmin = (Admin)ses.getAttribute("loginAdmin");
                if(loginAdmin == null) {
                        request.setAttribute("msg", "对不起,没有登录的管理员无权执行该操作!");
                        request.getRequestDispatcher("/manage/login.jsp").forward(request, response);
                }else {
                        //放行
                        chain.doFilter(request, response);
                }
        }


step2:解决例外情况
    
 /**
         * 过滤
         */
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                HttpServletRequest req = (HttpServletRequest)request; //类型转换
                
                
                //例外情况 -  管理员登录
                //获取请求的uri
                String uri= req.getRequestURI();
                //String url = req.getRequestURL().toString();
                System.out.println("uri:"+uri);
                //System.out.println("url:"+url);
                //如果是登录的请求,则放行
                if(uri.endsWith("/manage/admin") && req.getParameter("opr").equals("login")) {
                        System.out.println("当前请求为登录的请求,将被放行....");
                        chain.doFilter(request, response);
                        return;
                }
                
                HttpSession ses = req.getSession();
                //获取session作用域中登录的管理员
                Admin loginAdmin = (Admin)ses.getAttribute("loginAdmin");
                if(loginAdmin == null) {
                        request.setAttribute("msg", "对不起,没有登录的管理员无权执行该操作!");
                        request.getRequestDispatcher("/manage/login.jsp").forward(request, response);
                }else {
                        //放行
                        chain.doFilter(request, response);
                }
        }   


step3:进入登录页面直接转发
//进入登录页面
    if(uri.endsWith("/manage/login.jsp") || uri.endsWith("/manage") ||
       uri.endsWith("/manage/")) {
        request.getRequestDispatcher("/manage/login.jsp").forward(request, response);
        return;
    }

3.监听器

  • Web服务端三大组件之一
  • 监视web服务端某一个事件发生,自动的调用处理方法
  • 监听器根据所监听事件进行分类:
  • 监听作用域 ,三个
  • 监听ServletContext,HttpSession,HttpServletRequest对象的生命周期的监听器
  • 监听器入门示例:
  • 监听ServletContext对象创建与销毁
public class MyListener implements ServletContextListener {
        public void contextInitialized(ServletContextEvent sce) {
                System.out.println("MyListener监听器初始化....");
        }

        public void contextDestroyed(ServletContextEvent sce) {
                
                System.out.println("MyListener监听执行Destroyed....");
        }
}

web.xml:
  <listener>
          <listener-class>com.woniu.mall.listener.MyListener</listener-class>
  </listener>
  
        
也可以使用注解替代xml配置:
@WebListener
public class MyListener implements ServletContextListener {
        public void contextInitialized(ServletContextEvent sce) {
                System.out.println("MyListener监听器初始化....");
        }

        public void contextDestroyed(ServletContextEvent sce) {
                System.out.println("MyListener监听执行Destroyed....");
        }
}
  • 监听器应用
  • 统计当前系统中的在线人数 - 当前系统中的会话数量
(1) 定义监听器- HttpSessionListener
 /**
 * 用于统计在线人数的监听器
 *
 */
@WebListener
public class OnlineNumListener implements HttpSessionListener {


        /**
     * 当有客户端与服务器连接时,创建一个会话时,执行此方法
     */
    public void sessionCreated(HttpSessionEvent se)  {
            ServletContext ctx = se.getSession().getServletContext();
            
            //获取全局作用域中的人数
            Integer num = (Integer)ctx.getAttribute("num");
            if(num == null) {
                    //第一个会话
                    num = 1;
            }else {
                    num++;
            }
            
            //将num存入全局作用域 
            ctx.setAttribute("num", num);
    }

        /**
     * 会话失效-销毁时 invalidate(), 超时,关闭浏览器
     */
    public void sessionDestroyed(HttpSessionEvent se)  { 
            ServletContext ctx = se.getSession().getServletContext();
            System.out.println("会话销毁.....................");
            //获取全局作用域中的人数
            Integer num = (Integer)ctx.getAttribute("num");
            if(num>0) {
                    num--;
            }
            //将num存入全局作用域 
            ctx.setAttribute("num", num);
    }
        
}   
    

(2) 在页面中访问在线人数
当前在线人数:<span style="color:red;">{{applicationScope.num}</span>    

4.Ajax异步请求

4-1 Ajax概述

Ajax - Asynchornous javascript and  xml [JSON]
  |-异步的js和xml
  [无刷新页面技术,局部更新页面技术]
  
不是一种编程语言,而是多种技术的综合而成

Google 谷歌公司推广 [2004~2005] 提升用户体验

 
同步(Synchronous)和异步(Asynchronous)的概念
  |-同步请求 代表着你必须等待这次请求结束并且刷新整个界面之后,你才能进行下一步操作
    异步请求则可以不刷新界面,它会立即返回,进行局部更新,界面也可以继续执行其它的操作 


可以说,AJax改变了Web开发的格局

4-2 原生Ajax示例

  • 实现根据code异步加载对应的省份与城市数据
(1)创建 XMLHttpRequest 对象的语法:  -- ajax的核心对象

variable=new XMLHttpRequest();


(2) 建立与服务器连接
xmlhttp.open("GET","test1.txt",true);

method:请求的类型;GET 或 POST
•url:文件在服务器上的位置
•async:true(异步)或 false(同步)

(3) 设置回调
req.onreadystatechange = function(){}

(4) 发送请求
req.send(null)

(5) 在回调函数中接受响应的文本内容
 req.responseText属性
<script>
//步骤1: 创建XMLHttpRequest  Javascript对象  --也称为ajax的引擎对象
//所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用 ActiveXObject)。
let req = new XMLHttpRequest();

function getResult(){
        if(req){  //创建XHR对象成功
                //步骤2: 与服务建立连接
                let url = "load?code="+document.getElementById("code").value;
                req.open("GET",url,true);
                
                //步骤3:设置回调CallBack -- 当异步请求执行后,收到了服务器响应的数据,此时调用页面的函数
                req.onreadystatechange = loadData; //只写函数名称
        
            //步骤4: 发送ajax异步请求
            req.send(null); 
        }
}

//回调
function loadData(){
        //alert("ajax引擎对象的状态码:"+req.readyState);
        if(req.readyState==4 && req.status==200){ //服务器处理完成,并没有发生错误 
                //接收服务端的异步响应的消息内容
                let result = req.responseText;
            let arr = result.split(",");
            document.getElementById("province").value=arr[0];
            document.getElementById("city").value=arr[1];
        }
}
</script>
</head>
<body>
    邮编:<input type="text" name="code" id="code" onblur="getResult()" /><br/>
    省份:<input type="text" name="province" id="province"/><br/>
    城市:<input type="text" name="city" id="city"/>
    <marquee>我是滚动的文本</marquee>
</body>

LoadServlet:

@WebServlet("/load")
public class LoadServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;

        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                doGet(request, response);
        }
   
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                System.out.println("请求到达LoadServlet....");
                String code = request.getParameter("code");
                
                //查询数据库
                String province = "";
                String city = "";
                if(code.equals("430000")) {
                        province="湖北省";
                        city="武汉市";
                }else if(code.equals("420000")) {
                        province="湖南省";
                        city="长沙市";
                }else {
                        province="其他";
                        city="其他";
                }
                
                //输出到客户端 注:异步的请求不能执行服务器跳转 [无效]
                //request.getRequestDispatcher("/index.jsp").forward(request, response);
                
                //输出响应的消息内容
                response.setContentType("text/html;charset=UTF-8");
                PrintWriter out = response.getWriter();
                out.println(province+","+city);
                out.flush();
                out.close();
        }
}