1 过滤器-Filter
1.1 过滤器入门
1.1.1 过滤器概念及作用
过滤器——Filter,它是JavaWeb三大组件之一。另外两个是Servlet和Listener。
它是在2000年发布的Servlet2.3规范中加入的一个接口。是Servlet规范中非常实用的技术。
它可以对web应用中的所有资源进行拦截,并且在拦截之后进行一些特殊的操作。
常见应用场景:URL级别的权限控制;中文乱码问题等等。
1.1.2 过滤器的入门案例
1)前期准备
创建JavaWeb工程
编写和配置接收请求用的Servlet
public class ServletDemo1 extends HttpServlet {
/**
* 处理请求的方法
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ServletDemo1接收到了请求");
req.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
<?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_3_1.xsd"
version="3.1"
metadata-complete="true">
<!--配置Servlet-->
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>com.web.servlet.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/ServletDemo1</url-pattern>
</servlet-mapping>
</web-app>
编写index.jsp
<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>主页面</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/ServletDemo1">访问ServletDemo1</a>
</body>
</html>
编写success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>成功页面</title>
</head>
<body>
<%System.out.println("success.jsp执行了");%>
执行成功!
</body>
</html>
2)过滤器的编写步骤
编写过滤器
public class FilterDemo1 implements Filter {
/**
* 过滤器的核心方法
* @param request
* @param response
* @param chain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("FilterDemo1拦截到了请求");
}
}
配置过滤器
<!--配置过滤器-->
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>com.web.filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/ServletDemo1</url-pattern>
</filter-mapping>
3)测试部署
部署项目
测试结果
需要对过滤器执行放行操作,才能让他继续执行,那么如何放行的?
我们需要使用FilterChain
中的doFilter
方法放行。
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//放行
chain.doFilter(req,resp);
}
1.2 过滤器的细节
1.2.1 入门案例过程及生命周期
1)生命周期
出生——活着——死亡
**出生:**当应用加载的时候执行实例化和初始化方法。
**活着:**只要应用一直提供服务,对象就一直存在。
**死亡:**当应用卸载时,或者服务器宕机时,对象消亡。
Filter的实例对象在内存中也只有一份。所以也是单例的。
2)过滤器核心方法的细节
在FilterDemo1
的doFilter
方法添加一行代码,如下:
/**
* 过滤器的核心方法
* @param request
* @param response
* @param chain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("FilterDemo1拦截到了请求");
//过滤器放行
chain.doFilter(request,response);
System.out.println("FilterDemo1放行之后,又回到了doFilter方法");
}
测试运行结果,我们发现过滤器放行之后执行完目标资源,仍会回到过滤器中:
1.2.2 多个过滤器的执行顺序
1)修改FilterDemo1和FilterDemo2两个过滤器的代码,删掉多余的代码
public class FilterDemo1 implements Filter {
/**
* 过滤器的核心方法
* @param request
* @param response
* @param chain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("FilterDemo1拦截到了请求");
//过滤器放行
chain.doFilter(request,response);
}
/**
* 初始化方法
* @param filterConfig
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("FilterDemo1的初始化方法执行了");
}
/**
* 销毁方法
*/
@Override
public void destroy() {
System.out.println("FilterDemo1的销毁方法执行了");
}
}
public class FilterDemo2 implements Filter {
/**
* 初始化方法
* @param filterConfig
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("FilterDemo2的初始化方法执行了");
}
/**
* 过滤器的核心方法
* @param request
* @param response
* @param chain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("FilterDemo2拦截到了请求");
//过滤器放行
chain.doFilter(request,response);
}
/**
* 销毁方法
*/
@Override
public void destroy() {
System.out.println("FilterDemo2的销毁方法执行了");
}
}
2)修改两个过滤器的配置,删掉多余的配置
<!--配置过滤器-->
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>com.web.filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/ServletDemo1</url-pattern>
</filter-mapping>
<filter>
<filter-name>FilterDemo2</filter-name>
<filter-class>com.web.filter.FilterDemo2</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo2</filter-name>
<url-pattern>/ServletDemo1</url-pattern>
</filter-mapping>
3)测试运行结果
此处我们看到了多个过滤器的执行顺序,它正好和我们在web.xml中的配置顺序一致
在过滤器的配置中,有过滤器的声明和过滤器的映射两部分,到底是声明决定顺序,还是映射决定顺序呢?
答案是:<filter-mapping>
的配置前后顺序决定过滤器的调用顺序,也就是由映射配置顺序决定。
注解配置:按照类名的字符串比较规则比较,值小的先执行
1.2.3 过滤器的五种拦截行为
我们的过滤器目前拦截的是请求,但是在实际开发中,我们还有请求转发。默认情况下过滤器是不参与过滤的,要想使用,需要我们配置。配置的方式如下:
设置dispatcherTypes属性
1. REQUEST:默认值。浏览器直接请求资源
2. FORWARD:转发访问资源
3. INCLUDE:包含访问资源(了解)
4. ERROR:错误跳转资源(了解)
5. ASYNC:异步访问资源(了解)
@WebFilter(value = "/demo2.jsp",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})
public class FilterDemo5 implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("遇见强盗5号");
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
}
1.3 案例:解决中文乱码
1. 编写Servlet
@WebServlet("/login")
public class ServletDemo2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("username = " + username);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
2. 编写JSP页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/login" method="post">
用户名:<input type="text" name="username">
<input type="submit" value="提交">
</form>
</body>
</html>
3. 编写Filter
@WebFilter("/login")
public class FilterDemo6 implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//解决请求中文乱码问题
req.setCharacterEncoding("UTF-8");
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
}
1.4 案例:验证用户是否登录
在昨日的案例中加上过滤器, 验证用户是否登录,没登录不让访问主页和列表页
@WebFilter("/*")
public class LoginFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//1.获取当前请求路径
HttpServletRequest request = (HttpServletRequest) req;
String uri = request.getRequestURI();
//2.判断当前请求是否是与登录相关的请求
if(uri.contains("/loginServlet") || uri.contains("/checkCode") || uri.contains("/login.jsp") ||
uri.contains("/css") || uri.contains("/fonts") || uri.contains("/js")){
//是登录的资源
chain.doFilter(req, resp);
}else{
//不是登录相关的请求
//3.判断是否登录, Session中是否存在user
HttpSession session = request.getSession();
Object user = session.getAttribute("loginAdmin");
if(user != null){
//用户已登录,放行
chain.doFilter(req, resp);
}else{
//用户未登录,返回登录页面,给出提示
req.setAttribute("msg","请重新登陆");
req.getRequestDispatcher("/login.jsp").forward(req,resp);
}
}
}
public void init(FilterConfig config) throws ServletException {
}
}
2 监听器-Listener
2.1 观察者设计模式
在介绍监听器之前,先跟同学们普及一个知识,观察者设计模式。因为所有的监听器都是观察者设计模式的体现。
那什么是观察者设计模式呢?
它是事件驱动的一种体现形式。就好比在做什么事情的时候被人盯着。当对应做到某件事时,触发事件。
观察者模式通常由以下三部分组成:
事件源:触发事件的对象。
事件:触发的动作,里面封装了事件源。
监听器:当事件源触发事件时,要做的事情。一般是一个接口,由使用者来实现。
下图描述了观察者设计模式组成:
2.2 监听器的使用
2.2.1 ServletContextListener的使用
第一步:创建工程
第二步:编写监听器
public class ServletContextListenerDemo implements ServletContextListener {
/**
* 对象创建时,执行此方法
* @param sce
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("监听到了对象的创建");
//1.获取事件源对象
ServletContext servletContext = sce.getServletContext();
System.out.println(servletContext);
}
/**
* 对象销毁时,执行此方法
* @param sce
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("监听到了对象的销毁");
}
}
第三步:在web.xml中配置监听器
<!--配置监听器-->
<listener>
<listener-class>com.web.listener.ServletContextListenerDemo</listener-class>
</listener>
第四步:测试结果
2.3 监听对象
-
监听对象
ServletContextListener(重点)
HttpSessionListener
ServletRequestListener -
监听属性变化
ServletContextAttributeListener
HttpSessionAttributeListener
ServletRequestAttributeListener -
会话相关的感知型
HttpSessionBindingListener
HttpSessionActivationListener