Listener监听器 & Filter过滤器

269 阅读5分钟

监听器

监听器就是一个实现了特定接口的Java类,这个类的作用是来监听另一个方法的调用或属性的改变。当被监听的对象发生了上述事件后,监听器某个方法将会立即被执行。

用来监听其他对象的变化的。主要应用在图形化界面开发中。

  • 事件源:指被监听的对象(引发事件的东西),如汽车。JavaWeb中的事件源指的是三大域
  • 监听器:指监听的对象,如汽车报警器
  • 事件源和监听器绑定:在汽车上安装报警器
  • 事件:指事件源对象的改变,如触碰汽车;主要功能是获得事件源对象

一个入门案例

image.png

Servlet中的监听器

Servlet中有多种类型的监听器,用于监听的事件源分别是: ServletContext、HttpSession、ServletRequest 三个域对象

监听器的分类:

  • 监听三个域对象的创建和销毁的监听器(3个)
    • ServletContextListener
    • HttpSessionListener
    • ServletRequestListener
  • 监听三个域对象的属性变更(属性的添加、移除、替换)的监听器(3个)
    • ServletContextAttributeListener
    • HttpSessionAttributeListener
    • ServletRequestAttributeListener
  • 监听HttpSession中JavaBean的状态改变(钝化、活化、绑定、解除绑定)的监听器(2个)
    • HttpSessionBindingListener
    • HttpSessionActivationListener

ServletContextListener

作用:监听ServletContext域对象的创建和销毁的

ServletContext:在服务器启动的时候为每个web应用创建单独的ServletContext对象,在服务器关闭或者移除项目的时候销毁ServletContext(一个项目,只有一个)

用途:

  • 加载框架的配置文件,如Spring框架(核心监听器:ContextLoadListener)
  • 定时任务调度:服务器启动就开始计时,到达时间后执行程序

方法:

image.png

package com.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * ServletContextListener 监听器
 * 事件源:ServletContext
 * 监听器:MyServletContextListener
 * 事件源和监听器绑定:通过配置绑定
 */
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ServletContext创建");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("ServletContext销毁");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    
    <listener>
        <listener-class>com.listener.MyServletContextListener</listener-class>
    </listener>
</web-app>
image.png image.png

HttpSessionListener

作用:监听HttpSession域对象的创建和销毁的

HttpSession:在服务器端第一次调用getSession()方法的时候创建,在非正常关闭服务器、session过期、手动调用session.invalidate方法时被销毁(一次用户的getSession创建一次)

方法:

image.png

package com.listener;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * HttpSessionListener 监听器
 * 事件源:HttpSession
 * 监听器:MyHttpSessionListener
 * 事件源和监听器绑定:通过配置绑定
 */
public class MyHttpSessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("HttpSession创建");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println("HttpSession销毁");
    }
}
<session-config>
   <session-timeout>1</session-timeout>
</session-config>
<listener>
    <listener-class>com.listener.MyHttpSessionListener</listener-class>
</listener>
image.png

ServletRequestListener

作用:监听ServletRequest域对象的创建和销毁的

ServletRequest:从客户端向服务器发送一次请求时创建请求对象,服务器对这次请求作出响应之后请求销毁。(每次请求都创建一次)

方法:

image.png

package com.listener;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

/**
 * ServletRequestListener 监听器
 * 事件源:ServletRequest
 * 监听器:MyServletRequestListener
 * 事件源和监听器绑定:通过配置绑定
 */
public class MyServletRequestListener implements ServletRequestListener {
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("ServletRequest创建");
    }

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("ServletRequest销毁");
    }
}
<listener>
    <listener-class>com.listener.MyServletRequestListener</listener-class>
</listener>
image.png

案例:统计当前在线人数

需求分析:

image.png

package com.onlinecount;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class OnlineCountServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 服务器启动时,初始化一个值为0。并且存入ServletContext中
        sce.getServletContext().setAttribute("count", 0);
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("ServletContext销毁");
    }
}
package com.onlinecount;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class OnlineCountHttpSessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        // 在线的话,数值加1
        HttpSession session = se.getSession();
        System.out.println(session.getId() + "加入会议室");
        Integer count = (Integer) session.getServletContext().getAttribute("count");
        count++;
        session.getServletContext().setAttribute("count", count);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        // 离线,数值减1
        HttpSession session = se.getSession();
        System.out.println(session.getId() + "退出会议室");
        Integer count = (Integer) session.getServletContext().getAttribute("count");
        count--;
        session.getServletContext().setAttribute("count", count);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <session-config>
        <session-timeout>1</session-timeout>
    </session-config>

    <listener>
        <listener-class>com.onlinecount.OnlineCountServletContextListener</listener-class>
    </listener>

    <listener>
        <listener-class>com.onlinecount.OnlineCountHttpSessionListener</listener-class>
    </listener>
</web-app>
<body>
  <h1>会议室在线人数:${count}</h1>
</body>
image.png

域对象属性变更的监听器

ServletContextAttributeListener

作用:监听ServletContext域对象中属性变更(添加、移除、替换)的

image.png

HttpSessionAttributeListener

作用:监听HttpSession域对象中属性变更(添加、移除、替换)的

image.png

ServletRequestAttributeListener

作用:监听ServletRequest域对象中属性变更(添加、移除、替换)的

image.png

案例:

package com.listener1;

import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener {
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        System.out.println("Session中添加了属性");
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        System.out.println("Session中移除了属性");
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        System.out.println("Session中替换了属性");
    }
}
<body>
<%
    session.setAttribute("name", "name1");
    session.setAttribute("name", "name2");
    session.removeAttribute("name");
%>
</body>
image.png

JavaBean状态改变监听器

作用:监听HttpSession中JavaBean的状态改变的监听器

  • 钝化即随session对象持久化到一个存储设备中(没有关闭和启动服务器)
  • 活化即随session对象从一个存储设备中恢复(没有关闭和启动服务器)
  • 绑定到session中(setAttribute)
  • 从session中解除绑定(removeAttribute)

HttpSessionBindingListener接口、HttpSessionActivationListener接口

实现了这两个接口的类,是不需要在web.xml中进行配置的

HttpSessionBindingListener

监听绑定、解除绑定状态的方法:

image.png

package com.listener2;

import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

public class Bean1 implements HttpSessionBindingListener {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        System.out.println("Bean1绑定session");
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        System.out.println("Bean1解除绑定session");
    }
}
<body>
    <%
        Bean1 bean1 = new Bean1();
        bean1.setName("张三");
        session.setAttribute("bean1", bean1); // 绑定
        session.removeAttribute("bean1"); // 解绑
    %>
</body>
image.png

HttpSessionActivationListener

这个主要是进行Session的优化工作。 钝化、活化的状态监听方法:

image.png

package com.listener2;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import java.io.Serializable;

public class Bean2 implements HttpSessionActivationListener, Serializable {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        System.out.println("Bean2被session钝化(序列化)");
    }

    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {
        System.out.println("Bean2被session活化(反序列化)");
    }
}

过滤器

Filter过滤器,过滤从客户端向服务器发送的请求。

通过filter,可以对web服务器所管理的资源(JSP,Servlet,静态图片或静态html)进行拦截,从而实现特殊功能。

它可以让请求得到目标资源,也可以不让请求得达到目标资源。过滤器有拦截的能力。(对一组资源进行统一处理)

一个入门案例

package com.filter;

import javax.servlet.*;
import java.io.IOException;

/**
 * 编写一个类实现Filter接口
 */
public class FilterDemo1 implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("FilterDemo1执行了...");
        filterChain.doFilter(servletRequest, servletResponse);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--过滤器配置-->
    <filter>
        <filter-name>FilterDemo1</filter-name>
        <filter-class>com.filter.FilterDemo1</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FilterDemo1</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>
image.png

FilterChain对象

FilterChain 过滤器链:在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称为是一个过滤器链。

web服务器根据Filter在web.xml文件中的注册顺序(mapping的配置顺序)决定先调用哪个Filter,依次调用后面的过滤器。如果没有下一个过滤器了,会去调用目标资源。(调用完会回来)

image.png

Filter生命周期

Filter的创建和销毁,都是由web服务器负责的:

  1. web应用程序启动的时候,web服务器创建Filter的实例对象,并调用init方法进行初始化(filter只会创建一次,init方法只会执行一次)
  2. 每次filter进行拦截的时候,都会执行一次doFilter方法
  3. 当服务器关闭的时候或应用被移除的时候,服务器会销毁filter对象

FilterConfig对象

FilterConfig对象作用:用来获得Filter相关配置的对象。

方法:

image.png

案例:

package com.filter;

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;

public class FilterDemo3 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 获得过滤器的名称
        String filterName = filterConfig.getFilterName();
        System.out.println("******filterName******" + filterName); // FilterDemo3
        // 获得初始化参数
        String username = filterConfig.getInitParameter("username");
        System.out.println("******username******" + username); // root
        // 获得所有初始化参数的名称
        Enumeration<String> parameterNames = filterConfig.getInitParameterNames();
        while (parameterNames.hasMoreElements()) {
            String element = parameterNames.nextElement();
            String value = filterConfig.getInitParameter(element);
            System.out.println(element + ":" + value); // password:root111   username:root
        }
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--过滤器配置-->
    <filter>
        <filter-name>FilterDemo3</filter-name>
        <filter-class>com.filter.FilterDemo3</filter-class>
        <!-- 配置过滤器的初始化参数 -->
        <init-param>
            <param-name>username</param-name>
            <param-value>root</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>root111</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>FilterDemo3</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

过滤器相关配置

  • <url-pattern>
    • 完全路径匹配:以/开始 /aaa /aaa/bbb
    • 目录匹配:以/开始,以结束 /aaa/ /*
    • 扩展名匹配:以*开始 *.jsp
  • <servlet-name>
    • 专门以Servlet配置的名称拦截servlet
  • <dispatcher>
    • 取值1:REQUEST:默认值,默认过滤器拦截的就是请求
    • 取值2:FORWARD:转发,可以拦截转发
    • 取值3:INCLUDE:页面包含的时候进行拦截
    • 取值4:ERROR:页面出现全局错误页面跳转的时候进行拦截
<filter-mapping>
    <filter-name>FilterDemo3</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

Filter应用场景

1、执行目标资源之前做预处理工作,例如设置编码,这种试通常都会放行,只是在目标资源执行之前做一些准备工作。

(几乎所有的Servlet中都需要写request.setCharacterEncoding(),可以把它写到一个Filter中。这种过滤器不会做拦截操作,只是设置编码)

(或者分IP统计访问次数)

2、通过条件判断是否放行,例如校验当前用户是否已经登录,或者用户IP是否已经被禁用。(有拦截操作,判断Id是否在禁用区域)

(或粗粒度权限控制)

3、在目标资源执行后,做一些后续的特殊处理工作,例如把目标资源输出的数据进行处理。(批注:回程拦截)

(页面静态化)

案例

案例:分IP统计访问次数

需求:网站统计每个IP地址访问本网站的次数。

分析:

因为一个网站可能有多个页面,无论哪个页面被访问,都要统计访问次数,所以使用过滤器最为方便。

因为需要分IP统计,所以可以在过滤器中创建一个Map<String,Integer>,使用IP为key,访问次数为value

把这个Map存放到ServletContext中,使用ServletContextListener在服务器启动时完成创建(监听器)

1、首先创建一个监听器,用来创建map

package com.web.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.HashMap;
import java.util.Map;

public class AListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 服务器启动时创建Map,并保存到ServletContext
        Map<String, Integer> map = new HashMap<>();
        ServletContext servletContext = sce.getServletContext();
        servletContext.setAttribute("map", map);
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }
}

2、过滤器实现访问记录

package com.web.filter;

import javax.servlet.*;
import java.io.IOException;
import java.util.Map;

/**
 * 1、从application中获取Map
 * 2、从request中获取当前客户端的IP
 * 3、进行统计工作,结果保存到Map中
 */
public class AFilter implements Filter {
    FilterConfig config = null;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.config = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        ServletContext servletContext = config.getServletContext();
        Map<String, Integer> map = (Map<String, Integer>) servletContext.getAttribute("map");

        String ip = servletRequest.getRemoteAddr();
        if (map.containsKey(ip)) { // 不是第一次访问
            Integer count = map.get(ip);
            map.put(ip, count + 1);
        } else { // 第一次访问
            map.put(ip, 1);
        }
        servletContext.setAttribute("map", map);

        filterChain.doFilter(servletRequest, servletResponse); // 放行不拦截
    }

    @Override
    public void destroy() {
    }
}

3、项目配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <jsp-config>
        <taglib>
            <taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>
            <taglib-location>/WEB-INF/c.tld</taglib-location>
        </taglib>
    </jsp-config>

    <!--过滤器配置-->
    <filter>
        <filter-name>AFilter</filter-name>
        <filter-class>com.web.filter.AFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--监听器配置-->
    <listener>
        <listener-class>com.web.listener.AListener</listener-class>
    </listener>
</web-app>

4、页面展示结果

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>当前网页访问次数</title>
</head>
<body>
    <h1 align="center">访问次数</h1>
    <table align="center" width="30%" border="1">
        <tr>
            <th>IP</th>
            <th>访问次数</th>
        </tr>
        <c:forEach var="entry" items="${applicationScope.map}">
            <tr>
                <td>${entry.key}</td>
                <td>${entry.value}</td>
            </tr>
        </c:forEach>
    </table>
</body>
</html>
image.png

案例:登录权限验证

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录页面</title>
</head>
<body>
    <h1>登录页面</h1>
    <h3>${msg}</h3>
    <form action="${pageContext.request.contextPath}/UserServlet" method="post">
        <table border="1" width="400">
            <tr>
                <td>用户名</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>密码</td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="登录"></td>
            </tr>
        </table>
    </form>
</body>
</html>

UserServlet.java

package com.login;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class UserServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if ("root".equals(username) && "root1234".equals(password)) { // 登录成功
            req.getSession().setAttribute("user", username);
            resp.sendRedirect(req.getContextPath() + "/success.jsp");
        } else { // 登录失败
            req.setAttribute("msg", "您的用户名或密码错误");
            req.getRequestDispatcher("/login.jsp").forward(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>欢迎光临,尊敬的${user}</h1>
</body>
</html>

PrivilegeFilter.java

package com.login;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class PrivilegeFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String username = (String) request.getSession().getAttribute("user");
        System.out.println("这里是doFilter,其中username=" + username);
        if (username == null) {
            request.setAttribute("msg", "请先登录再进行操作!");
            request.getRequestDispatcher("/login.jsp").forward(request, servletResponse);
        } else {
            filterChain.doFilter(request, servletResponse);
        }
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置Servlet-->
    <servlet>
        <!--配置Servlet名称-->
        <servlet-name>UserServlet</servlet-name>
        <!--配置Servlet全路径-->
        <servlet-class>com.login.UserServlet</servlet-class>
    </servlet>

    <!--配置Servlet的一个映射-->
    <servlet-mapping>
        <!--配置Servlet名称-->
        <servlet-name>UserServlet</servlet-name>
        <!--配置访问的路径-->
        <url-pattern>/UserServlet</url-pattern>
    </servlet-mapping>

    <!--过滤器配置-->
    <filter>
        <filter-name>PrivilegeFilter</filter-name>
        <filter-class>com.login.PrivilegeFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>PrivilegeFilter</filter-name>
        <url-pattern>/login.jsp</url-pattern>
    </filter-mapping>
</web-app>

案例:粗粒度权限管理

user.jsp admin.jsp index.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>$Title$</title>
</head>
<body>
    <h1>index.jsp 游客页面</h1>
    <a href="<c:url value='/index.jsp'/>">游客入口</a> <br>
    <a href="<c:url value='/users/user.jsp'/>">会员入口</a> <br>
    <a href="<c:url value='/admin/admin.jsp'/>">管理员入口</a> <br>
</body>
</html>

login.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    ${msg}
    <form action="<c:url value='/LoginServlet'/>" method="post">
        用户名:<input type="text" name="username">
        <input type="submit" value="登录">
    </form>
</body>
</html>

LoginServlet:

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoginServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        if (username.contains("admin")) { // 管理员
            req.getSession().setAttribute("admin", username);
        } else { // 普通会员用户
            req.getSession().setAttribute("username", username);
        }
        req.getRequestDispatcher("/index.jsp").forward(req, resp);
    }
}

UserFilter:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class UserFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        String name = (String) req.getSession().getAttribute("admin");
        if (name != null) { // 管理员放行,可以访问user.jsp
            filterChain.doFilter(req, servletResponse);
            return;
        }

        name = (String) req.getSession().getAttribute("username");
        if (name != null) { // 会员放行
            filterChain.doFilter(req, servletResponse);
        } else { // 游客不放行
            req.setAttribute("msg", "您不是会员或者管理员,无权访问");
            req.getRequestDispatcher("/login.jsp").forward(req, servletResponse);
        }
    }
}

AdminFilter:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class AdminFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        String name = (String) req.getSession().getAttribute("admin");
        if (name != null) { // 管理员放行,可以访问admin.jsp
            filterChain.doFilter(req, servletResponse);
        } else {
            req.setAttribute("msg", "您不是者管理员,无权访问");
            req.getRequestDispatcher("/users/user.jsp").forward(req, servletResponse);
        }
    }
}

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <jsp-config>
        <taglib>
            <taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>
            <taglib-location>/WEB-INF/c.tld</taglib-location>
        </taglib>
    </jsp-config>

    <!--配置Servlet-->
    <servlet>
        <!--配置Servlet名称-->
        <servlet-name>LoginServlet</servlet-name>
        <!--配置Servlet全路径-->
        <servlet-class>LoginServlet</servlet-class>
    </servlet>

    <!--配置Servlet的一个映射-->
    <servlet-mapping>
        <!--配置Servlet名称-->
        <servlet-name>LoginServlet</servlet-name>
        <!--配置访问的路径-->
        <url-pattern>/LoginServlet</url-pattern>
    </servlet-mapping>

    <!--过滤器配置-->
    <filter>
        <filter-name>UserFilter</filter-name>
        <filter-class>UserFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>UserFilter</filter-name>
        <url-pattern>/users/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>AdminFilter</filter-name>
        <filter-class>AdminFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AdminFilter</filter-name>
        <url-pattern>/admin/*</url-pattern>
    </filter-mapping>
</web-app>
image.png image.png

案例:编码问题

测试页面:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <a href="<c:url value='/EncodeServlet?username=张三GET'/>">点击这里调用GET方法</a> <br>
    <form action="<c:url value='/EncodeServlet'/>" method="post">
        用户名:<input type="text" name="username" value="李四POST">
        <input type="submit" value="提交">
    </form>
</body>
</html>

过滤器:

public class EncodeFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletResponse.setContentType("text/html;charset=utf-8");
        // 处理POST请求编码问题
        servletRequest.setCharacterEncoding("utf-8");

        filterChain.doFilter(servletRequest, servletResponse);
    }
}

EncodeServlet:

public class EncodeServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        resp.getWriter().println(username);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        resp.getWriter().println(username);
    }
}

案例:页面静态化

什么是页面静态化:

页面静态化是把动态页面生成的html保存到服务器的文件上,然后再有相同请求时,不再去执行动态页面,而是直接给用户响应上次已经生成的静态页面。而且静态页面还有助于搜索引擎找到。