Servlet进阶
1.过滤器-Filter
1.1.什么过滤器?
过滤器,即具有拦截过滤的作用的器具。例如:筛子,渔网等等,都是拦截器。
而在JavaWeb程序中,可以将WEB中的请求进行拦截过滤的程序就被称之为过滤器.
1.2.为什么要存在过滤器?
因为服务器是对外开放,所有的客户端都可以进行访问。但是在访问中,存在非法的或无效的等类型的访问。但是,访问一段传递给了程序,程序就会进行处理。就会进行运算,这样会导致,服务器因此增大服务器压力,也可能大致服务器运算异常。所以,需要对WEB的请求,进行过滤筛选,若是不合法的请求,进行特殊的处理。
1.3.如何使用过滤器?
在JavaWeb中,规定了接口Filter,只要实现了Filter接口就是一个过滤器,然后注册到服务器,且配置要进行过滤的地址。
-
创建类实现Filter接口 重写相关方法
package com.sxt.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; /** * @ClassName: FilterDemo * @Description: 过滤器 filter 示例 * @author: Mr.T * @date: 2020年2月16日 下午3:52:57 */ public class FilterDemo implements Filter { /** * 当Filter 初始化调用的方法 */ @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("============init==========="); } /** * 具体进行过滤的方法 * 当使用过滤器链对象,将请求对象和响应对象向下传递 则表示放行 * request : 请求对象 * response : 响应对象 * chain : 过滤器链 */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("==============doFilter==============="); System.out.println("放行前!!!"); //进行放行 chain.doFilter(request, response); System.out.println("放行后!!!"); } /** * 当服务器停止后 销毁对象时 调用的方法 */ @Override public void destroy() { System.out.println("============destroy=============="); } }
-
在web.xml中进行过滤器的配置
注册过滤
配置会进行拦截的地址
<!-- 配置过滤器 --> <!-- 注册过滤器 --> <filter> <filter-name>filterDemo</filter-name> <filter-class>com.sxt.filter.FilterDemo</filter-class> </filter> <!-- 配置要进行过滤的地址 --> <filter-mapping> <filter-name>filterDemo</filter-name> <!-- 如果当前访问的URL 符合下面配置的规则,则会进行过滤 --> <url-pattern>/*</url-pattern><!-- 所有的地址都会进行过滤 --> </filter-mapping>
1.4.过滤器的生命周期
在web程序中,过滤器在服务器启动时,会默认初始化,创建过滤器对象,调用init方法进行初始化。
当有符合过滤规则的请求,就会调用doFilter方法,进行过滤。
当服务器,停止时,会调用destroy方法,释放内存。
注意:
init 和 destroy 方法都只会执行一次
1.5.过滤器链
如果存在多个过滤器,且一次访问也符合多个过滤条件,那么相关的过滤器都会执行。
且先执行的过滤器后执行执行完。在web.xml的配置中,自上而下的过滤器,配置在上面的优先执行。
过滤器链.png1.6.过滤器使用场景
过滤器在开发中很常用,例如:
1.编码过滤器,利用过滤器的特性,会在具体的程序执行前执行。可以在执行前设置编码,且会在具体的程序执行完后执行,可以在执行完后设置响应数据的编码。
2.登录过滤器,在实际项目中,有些功能,需要用户登录登录后才能进行相关操作。所以,可以检测URL,从而判断是否是需要登录的,若是需要登录的,则获取当前登录用户,若存在,则放行,不存在则跳转到登录页面,让用户登录。
3.数据采集。利用过滤对符合条件的URL都能过滤的特性。可以将同类的URL进行过滤,如:查询的URL(*query.do)的URL,可以获取所以查询参数,可以将查询参数进行保存。
4.权限过滤器。利用过滤的特性,检查请求的URL,根据当前用户的角色,判断用户是否存在当前权限,若存在放行,若不存在则提示没有权限访问。
1.7.过滤器案例
1.7.1.编码过滤器
编码过滤器即将请求和响应的编码设置为需要的编码
package com.sxt.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;
/**
* @ClassName: CharsetEncodingFilter
* @Description: 编码过滤器
* @author: Mr.T
* @date: 2020年2月16日 下午4:55:07
*/
public class CharsetEncodingFilter implements Filter{
//设置过滤器的默认编码
String charset = "UTF-8";
/**
* init 方法 是初始化方法 会优先于 doFilter
* 可以初始化一些成员变量
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//过滤器配置信息
//获取配置的编码
String initCharset= filterConfig.getInitParameter("charset");
if(initCharset != null && initCharset !="") {
//将配置文件中编码 覆盖掉默认编码
charset = initCharset;
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("此时的编码为:"+charset);
//设置编码
request.setCharacterEncoding(charset);
response.setCharacterEncoding(charset);
//放行请求
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
<filter>
<filter-name>charsetEncodingFilter</filter-name>
<filter-class>com.sxt.filter.CharsetEncodingFilter</filter-class>
<!-- 过滤器初始化参数 -->
<init-param>
<param-name>charset</param-name>
<param-value>ISO-8859-1</param-value>
</init-param>
</filter>
<!-- 配置要进行过滤的地址 -->
<filter-mapping>
<filter-name>charsetEncodingFilter</filter-name>
<!-- 如果当前访问的URL 符合下面配置的规则,则会进行过滤 -->
<url-pattern>/*</url-pattern><!-- 所有的地址都会进行过滤 -->
</filter-mapping>
1.7.2.登录过滤器
一般用户登录,会放入session中。‘
登录过滤器,需要根据rquest对象,拿到用户放问的URL,判断该URL是否是需要登录的URL,若是需要登录的URL,则检测是否已经登录,若没有登录则跳转到登录页面,若已登录则放行。
以stmng为例:
目前用户没有登录,可以访问任何地址。限制: 若用户没有登录,则用户只能访问login.jsp.
package com.sxt.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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @ClassName: LoginFilter
* @Description: 登录过滤器
* 需要过滤的: 除不需要过滤的都要进行过滤
* 不需要过滤的: 登录页面 、js文件、css文件、图片文件等静态资源文件、验证码请求
*
*
* @author: Mr.T
* @date: 2020年2月16日 下午5:12:06
*/
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse resp = (HttpServletResponse)response;
//1. 获取请求的URI
//获取请求的资源路径
String uri = req.getRequestURI();
System.out.println("请求的资源路径为: "+uri);
//2. 获取当前的登录用户
Object user = req.getSession().getAttribute("user");
//2.1 如果用户已经登录 则直接放行
if(user != null) {
System.out.println("用户已经登录放行");
chain.doFilter(req, resp);
return;
}
//2.2 如果用户没有登录,则判断是否是不需要过滤的请求,是则放行 不是则跳转到登录页面
//不需要过滤的: 登录页面 、js文件、css文件、图片文件等静态资源文件、验证码请求
System.out.println();
//如果是以 login.jsp 结尾 放行
//如果是js文件、css文件、图片文件等静态资源文件 则判断是否是 resources开头
//要根据 /项目/resources 开头 stmng
System.out.println("项目根目录:"+req.getContextPath());
//静态资源的访问目录
String staticPath = req.getContextPath() +"/resources";
//登录页面 和 静态资源文件目录 验证码 放行
if(uri.endsWith("login.jsp") || uri.startsWith(staticPath) || uri.endsWith("checkCode.do")) {
chain.doFilter(req, resp);
return;
}
//具体的登录请求 和 注册请求也要放行
String service = req.getParameter("service");
if(uri.endsWith("user.do") && ("login".equals(service)||"register".equals(service))) {
chain.doFilter(req, resp);
return;
}
//其他情况 跳转到 登录页面
resp.sendRedirect("login.jsp");
}
@Override
public void destroy() {
}
}
<!-- 配置过滤器 -->
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.sxt.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.监听器-Listener
2.1.什么是监听器?
监听器就是起到监视侦听的器具,可以随时监视事物的变化。例如:古代战争中用于监视敌人是否袭来的城墙边上的瓮,烽火台。而在JavaWeb中的监听器,是用于监视WEB中的作用域状态的程序。主要监视,作用域的声明周期以及属性的变化。
在JavaWeb中,Servlet有3大作用域:
HttpServletRequest request
HttpSession session
ServletContext application
注意:
jsp由于其特殊性,存在9大内置对象,4大作用域,其中pageContext是其特有的,其他3个作用域,与Servlet的作用域是一致。
而WEB中监听器注意监听的是3个对象的声明周期以及属性的变化。基于这点,WEB提供了2类接口:
监听声明周期的接口:
HttpServletRequest的生命周期监视器接口:ServletRequestListener
HttpSession的生命周期监视器接口:HttpSessionListener
ServletContext的生命周期监视器接口: ServletContextListener
监听属性变化的接口:
HttpServletRequest的属性监视器接口:ServletRequestAttributeListener
HttpSession的属性监视器接口:HttpSessionAttributeListener
ServletContext的属性监视器接口: ServletContextAttributeListener
2.2.为什么会存在监听器?
监视对象的生命周期,即监视对象的创建和销毁。监视对象的属性变化,即监视对象的活动。根据对象的声明和销毁,对象的属性发生改变从而调用相应的方法,可以在不改变原有编码的基础上,干预程序的运算。
2.3.如何使用监听器?
监听器的使用相对简单,因为每个监听器只有固有的作用,如果想监听对象的生命周期,则实现相应的生命周期的接口,如果想监听属性的变化,则实现监听属性变化的接口。在配置文件注册监听器。
HttpServletRequest的监听器
package com.sxt.listener;
import java.io.UnsupportedEncodingException;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
/**
* @ClassName: RequestListener
* @Description: HttpServletRequest的生命周期和属性监听器
* ServletRequestListener 生命周期监听器
* requestInitialized : 当有request对象被实例化时会调用
* requestDestroyed : 当有request 对象被销毁时调用
*
* 现在RequestListener 实现了接口 就是遵循监听器的规范。RequestListener此时
* 只是一个符合监听器规范的程序。还需要配置 监听器才能真正生效
*
* @author: Mr.T
* @date: 2020年2月17日 上午9:49:53
*/
public class RequestListener implements ServletRequestListener,ServletRequestAttributeListener {
/**
* @Title: requestInitialized
* @author: Mr.T
* @date: 2020年2月17日 上午9:51:44
* @Description: 当存在request 对象被实例化时 会被调用
* @param sre
* @return: void
*/
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("request 对象 被创建了");
//拿到当前已经创建的request 对象
HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
}
/**
* @Title: requestDestroyed
* @author: Mr.T
* @date: 2020年2月17日 上午9:52:28
* @Description: 当request 对象 被销毁时 调用
* @param sre
* @return: void
*/
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("request 对象 被销毁了");
}
/**
* 当作用域中有新增的属性会触发该方法
*/
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
System.out.println("有新增的属性了");
String name = srae.getName();
Object value = srae.getValue();
System.out.println("新增的属性的name值:"+name);
System.out.println("新增属性的value值:"+value);
System.out.println("发生属性变化的对象:"+srae.getServletRequest());
if(name.equals("sex")) {
String sex = value.equals("1")?"男":"女";
//此处修改了属性值
srae.getServletRequest().setAttribute(name, sex);
}
}
/**
* 当作用域中发生属性修改会触发该方法
*/
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
System.out.println("当属性值发生修改时触发");
// 被修改的数据的name值
String name = srae.getName();
//被修改的数据的之前的值
Object value = srae.getValue();
//被修改的数据的新值
System.out.println(srae.getServletRequest().getAttribute(name));
System.out.println("attributeReplaced中的name:"+ name);
System.out.println("attributeReplaced中的value:"+ value);
}
/**
* 当作用域中发生属性的删除会触发该方法
*/
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
System.out.println("当属性中发生删除时触发");
String name = srae.getName();
Object value = srae.getValue();
System.out.println(name+":"+value);
}
}
<listener>
<listener-class>com.sxt.listener.RequestListener</listener-class>
</listener>
Session的监听器
package com.sxt.listener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* @ClassName: SessionListener
* @Description: session的监听器
* HttpSessionListener : session 生命周期监听器
* sessionCreated : 当session创建时触发
* sessionDestroyed : 当session销毁时触发
* HttpSessionAttributeListener : session的属性监听器
*
*
*
*
* @author: Mr.T
* @date: 2020年2月17日 上午10:47:55
*/
public class SessionListener implements HttpSessionListener,HttpSessionAttributeListener {
/**
* Session创建时触发
*/
@Override
public void sessionCreated(HttpSessionEvent se) {
//注意:每次会话 执行一次
System.out.println("session创建了");
}
/**
* session 销毁时触发
*/
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// System.out.println("session 销毁时触发");
// //当session 与request的绑定关系 此时session 等待被gc回收
// //则调用该方法
// HttpSession session = se.getSession();
// //获取到这个被等待回收的session
// System.out.println(session);
// //从session 中获取属性值
// System.out.println(session.getAttribute("me"));
}
@Override
public void attributeAdded(HttpSessionBindingEvent se) {
System.out.println("当session新增属性时触发");
}
@Override
public void attributeReplaced(HttpSessionBindingEvent se) {
System.out.println("当session中 发生属性修改时触发");
}
@Override
public void attributeRemoved(HttpSessionBindingEvent se) {
System.out.println("当session中发生属性删除时触发");
//被删除的数据的 name值
System.out.println("删除中的name:"+se.getName());
//被删除数据的value值
System.out.println("删除中的value:"+se.getValue());
}
}
<listener>
<listener-class>com.sxt.listener.SessionListener</listener-class>
</listener>
注意:
当session调用销毁方法时,还会调用删除属性方法,session会自动的将其中所有属性进行删除,则会触发多次
attributeRemoved方法。
ServletContext常用功能介绍
package com.sxt.controller;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/test3.do")
public class TestController3 extends HttpServlet {
private static final long serialVersionUID = -5123674255870472885L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//ServletContext 是一个全局容器 整个应用程序 有且只有一个
ServletContext context = req.getServletContext();
//getRealPath("")
//getRealPath("") : 获取项目在服务器中WebRoot目录的绝对路径
//getRealPath 是获取项目的WebRoot 或者WebRoot指定的资源的绝对路径
System.out.println(context.getRealPath("img"));
//当使用文件操作时,涉及到流数据操作,必须使用绝对路径,所以getRealPath一般用于文件上传下载
//上传 需要将文件保存在一个物理磁盘中 需要使用绝对路径
//下载 需要从一个物理磁盘 将文件变为流数据传给浏览器 也需要绝对路径
//getContextPath(): 获取的项目访问名称
System.out.println(context.getContextPath());
String rootPath = context.getContextPath();
//在WEB开发中,一般URL路径,使用绝对路径: 协议:地址:端口/资源路径 这样的形式。
//在WEB开发中,浏览器中的URL地址是变化的。相对路径是相对当前路径,当前URL路径
//容易出现路径错误,如果使用绝对路径,则没有该问题
//绝对路径 适用于url 路径是任何情况
resp.sendRedirect(rootPath+"/index.jsp");
}
}
ServletContext的监听器
package com.sxt.listener;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* @ClassName: ApplicationListener
* @Description: ServletContext 监听器
* ServletContextListener : ServletContext 生命周期监听器
* contextInitialized : 当ServletContext对象被实例化时调用 只会调用一次
* 当服务器启动时就会调用
* contextDestroyed : 当ServletContext 对象被销毁时调用 也只会调用一次
* 当服务器停止时 调用
* ServletContextAttributeListener :ServletContext 属性监听器
*
* @author: Mr.T
* @date: 2020年2月17日 下午2:15:36
*/
public class ApplicationListener implements ServletContextListener,ServletContextAttributeListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext 实例化");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext 销毁");
}
/**
* 当ServletContext中新增属性时 调用
*/
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
System.out.println("新增属性");
}
/**
* 当发生属性修改时调用
*/
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
System.out.println("修改了属性");
}
/**
* 当发生属性删除时调用
*/
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
System.out.println("删除了属性");
}
}
<listener>
<listener-class>com.sxt.listener.ApplicationListener</listener-class>
</listener>
注意:
- tomcat可以通过配置发布非tomcat服务器中webapps中的项目
<Context docBase="F:\10listener" path="/listener" reloadable="true" />
-
由于ServletContext的特殊性,ServletContext是整个WEB项目中最先被实例化的对象,且只会被实例化一次,全局都共享。基于这点,ServletContext的生命周期方法最常使用,特别是初始方法.因为可以在项目启动时就将一些配置信息通过该方法进行读取,配置。这样,实际使用时只需要进行调用即可。虽然程序启动相对变慢,但是实际使用时提高了效率。例如:
- JDBC的连接,在创建连接时需要读取配置文件中配置信息,加载驱动。然后再创建连接。可以在ServletContext初始化方法中,直接读取配置文件,加载驱动,创建一个连接池。这样在真正需要使用连接的地方只需要调用即可。
- 在WEB开发,每层可能会其他层调用。则需要类对象,可以在实例化时,创建类对象,需要使用的地方,只需要获取类对象,然后调用方法。不需要自己去创建。
2.4.ServletContext的初始化参数配置
<!-- 配置ServletContext的初始化参数 -->
<context-param>
<param-name>name1</param-name>
<param-value>value1</param-value>
</context-param>
<context-param>
<param-name>name2</param-name>
<param-value>value2</param-value>
</context-param>
//获取到ServletContext
ServletContext context = sce.getServletContext();
//根据参数的key 获取对应的值
String initParameter1 = context.getInitParameter("name1");
System.out.println(initParameter1);
String initParameter2 = context.getInitParameter("name2");
System.out.println(initParameter2);
网友评论