filter 的拦截方式
filter 默认只拦截 Request(浏览器过来的请求,包括重定向),不拦截 Forward(服务器内部的请求,即请求转发)
如果想让filter拦截服务器内部的请求:
- 注解方式
配置 dispatcherTypes 属性,属性值新增 DispatcherType.FORWARD
@WebFilter(dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD})
该注解属性配置既会拦截浏览器过来的请求,又会拦截服务器内部的请求
xml 方式配置
在 <filter-mapping> 的标签中添加标签 <dispatcher>FORWARD</dispatcher> ,但是这种配置将 filter 默认的拦截方式给覆盖了,只会拦截服务器内部的请求,不会再拦截默认的浏览器请求了
想让 filter 拦截服务器内部的请求又拦截浏览器过来的请求:
<filter-mapping> <filter-name>MyFilter2</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> <!-- 配置拦截服务器内部请求 --> <dispatcher>REQUEST</dispatcher> <!-- 配置拦截浏览器的请求 --> </filter-mapping>
使用案例
解决全站乱码
浏览器发出的任何请求,通过过滤器统一处理中文乱码。
@WebFilter(filterName = "EnCodingFilter",urlPatterns = "/*") public class EnCodingFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { // 在执行servlet获取数据和响应数据之前执行该代码 req.setCharacterEncoding("utf-8"); resp.setContentType("text/html;charset=utf-8"); // 放行之前 处理中文乱码 chain.doFilter(req, resp); } @Override public void destroy() {}
非法字符的拦截
@WebFilter(filterName = "FeifaFilter",urlPatterns = "/sd3") public class FeifaFilter implements Filter { private List<String> list; //服务器启动就执行 且只执行一次 @Override public void init(FilterConfig config) throws ServletException { // 加载一个非法字符的文档(properties) //底层:会自动去src下加载后缀名为properties的文件 ResourceBundle bundle = ResourceBundle.getBundle("feifa"); String value = bundle.getString("feifa"); // 读取文档中的数据,放入到集合中 String[] arr = value.split(","); list = Arrays.asList(arr); } @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { // 获取用户发表的言论 HttpServletRequest request=(HttpServletRequest)req; HttpServletResponse response=(HttpServletResponse)resp; String info = request.getParameter("info"); // 判断言论是否合法 // 遍历循环集合,将集合的非法字符和接受到的数据做包含 // 包含上:就是非法 没包含上:就是合法 for(String value:list){ if(info.contains(value)){ // 不合法--不放行 直接给浏览器响应信息 // 包含上:就是非法 response.getWriter().print("你发表的言论有非法行为,请思考后再发表..."); return; } } //合法--直接放行 chain.doFilter(request, resp); } @Override public void destroy() {}
Listener:监听器
概述
web 监听器是一种 Servlet 中的特殊的类,可以帮助开发者监听web中的特定事件,比如 ServletContext, HttpSession, ServletRequest 的创建和销毁等。可以在某些动作前后增加处理,实现监控。
Listener 的作用:监听 ServletContext, HttpSession, ServletRequest 三大域对象的状态变化(三个域对象的创建和销毁)
使用场景:
- 系统启动时初始化信息
ServletContextListenner 用来监听 ServletContext 对象的创建和销毁的
当项目启动,servletContext 对象被创建时,会调用 ServletContextListenner 的 contextInitialized 方法,所以可以在此方法中初始化项目需要的信息
注:spring 框架会使用到它
统计在线人数
每当一个用户访问项目的时候,都会创建一个 session 会话。所以当前 session 会话被创建,当前在线用 户 +1,每当 session 会话被销毁,当前在线用户 -1
- HttpSessionListener 可以用来监听 session 对象的创建和销毁的,所以可以在 HttpSessionListener 中的监听 session 对象创建和销毁的方法中控制在线人数的加减
java 提供了监听器规范,不同的监听器规范要监听的内容不一样
常见监听器:
ServletContextListener(常用):监听 ServletContext 对象的创建和销毁
例如:框架的配置文件在服务器启动就执行
ServletRequestListener :监听 ServletRequest 对象的创建和销毁
HttpSessionListener :监听 HttpSession 对象的创建和销毁
listener 的使用
API
- ServletContextListenner 接口
// 监听 servletcontext 对象的创建 void contextInitialized(ServletContextEvent sce) // 监听 servletcontext 对象的销毁 void contextDestroyed(ServletContextEvent sce)
ServletRequestListener 接口
// 监听 ServletRequest 对象的创建 void requestInitialized(ServletRequestEvent servletRequestEvent) // 监听 ServletRequest 对象的创建 void requestDestroyed(ServletRequestEvent servletRequestEvent)
HttpSessionListener 接口
// 监听 HttpSession 对象的创建 void sessionCreated(HttpSessionEvent httpSessionEvent) // 监听 HttpSession 对象的创建 void sessionDestroyed(HttpSessionEvent httpSessionEvent)
使用步骤(以 ServletContextListenner 监听器为例)
- 创建一个类实现 ServletContextListenner 接口,并实现方法 contextInitialized() 和contextDestroyed()
// 监听servletContext对象的创建和销毁。需实现java提供的监听器规范(ServletContextListener) //@WebListener public class ServletContextLoaderListener implements ServletContextListener { // 当servletContext对象被创建了,监听器触发,该方法执行 @Override public void contextInitialized(ServletContextEvent servletContextEvent) { System.out.println("servletContext对象被创建了..."); // 让服务器一加载,就读取框架的配置信息,进行加载 ServletContext servletContext = servletContextEvent.getServletContext(); String value = servletContext.getInitParameter("spring"); System.out.println("就可以正式根据获取到的配置文件名,对该文件的内容进行加载了:"+value); System.out.println("正在加载该配置文件中...."); } @Override //当servletContext对象被销毁了 监听器触发 该方法执行 public void contextDestroyed(ServletContextEvent servletContextEvent) { System.out.println("servletContext对象被销毁了..."); } }
注册监听器
- 方式1:在实现类上添加注解 @WebListene
- 方式2:在 xml 中配置
<listener> <listener‐class>com.itheima.listenner.MyServletContextListenner1</listener‐class> </listener>
案例
统计网站当前在线人数 HttpSessionListener
session 监听器,统计 session 个数
import javax.servlet.ServletContext; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; @WebListener() public class MyHttpSession implements HttpSessionListener { //当前在线人数的属性名称 private static final String NUMBER = "NUMBER"; /* * session 创建时触发 */ @Override public void sessionCreated(HttpSessionEvent event) { //会话创建了,有新用户访问网站 HttpSession session = event.getSession(); //新创建的会话对象 ServletContext servletContext = session.getServletContext(); //获取当前项目环境对象 System.out.println("session创建了" + session.getId()); Integer n = (Integer) servletContext.getAttribute(NUMBER); //当前在线人数 if( n== null){ //如果 n==null 说明是第一个用户 ,直接设置n=1 servletContext.setAttribute(NUMBER, 1); }else{ //如果n != null 直接累加 servletContext.setAttribute(NUMBER, ++n); } //如果 n==null 说明是第一个用户 ,直接设置n=1 //如果 n != null 直接累加 } /* * session销毁时触发 */ @Override public void sessionDestroyed(HttpSessionEvent event) { //会话销毁了,有用户的会话对象销毁了 HttpSession session = event.getSession(); //新创建的会话对象 ServletContext servletContext = session.getServletContext(); //获取当前项目环境对象 System.out.println("session销毁了" + session.getId()); Integer n = (Integer) servletContext.getAttribute(NUMBER); //当前在线人数 //直接减1 servletContext.setAttribute(NUMBER, ‐‐n); } }
拓展
浏览器和服务器交互中文乱码处理
- 浏览器页面的中文数据请求给服务器,如果使用的 post 提交,服务器接受会发生乱码
解决:处理请求的中文数据乱码
request.setCharacterEncoding("utf-8");
服务器给浏览器中文数据的时候,浏览器会发生乱码行为
解决: 处理响应的中文数据乱码
response.setContentType("text/html;charset=utf-8");
服务器给浏览器附件框的时候,附件框上有中文,会发生乱码行为(下载才会有附件框)
解决:
- 获取用户的浏览器版本
火狐版本的浏览器:返回去的附件中文设置 base64 编码
其它版本的浏览器:返回去的附件中文设置 utf-8 编码
String value = request.getHeader("user-agent"); String encode; if(value.contains("Firefox")){ //火狐浏览器中文(base64) Base64.Encoder encoder = Base64.getEncoder(); encode = "=?utf-8?B?" + encoder.encodeToString(fileName.getBytes("utf-8")) + "?="; } else { //其它浏览器中文(utf-8) encode = URLEncoder.encode(fileName, "utf-8"); } //小说.txt --%E5%B0%8F%E8%AF%B4.txt response.setHeader("content-disposition", "attachment;filename=" + encode);
请求转发和重定向的区别
- 重定向多次请求,请求转发一次请求
- 重定向是浏览器向服务器发送访问,请求转发是服务器内部资源互相访问
- 重定向是response对象的API,请求转发是request对象的API
- 重定向可以访问自己的项目也可以访问别的项目,请求转发只能访问当前自己的项目a
// 重定向。可以访问外部项目 response.sendRedirect(request.getContextPath() + "/sd11"); // 请求转发。只能访问当前项目 request.getRequestDispatcher("/sd7").forward(request,response);
域对象的总结
域对象可以在多个 servlet/jsp 之间进行数据传递
java 后端的域对象有 3 个:
ServletContext(公共数据)
创建:服务器只要启动就创建,且只创建一个
销毁:服务器关闭就销毁
存入数据的作用范围:整个 web 项目的所有 servlet 都共享
因为整个 web 项目只有一个 ServletContext,所有 servlet 中获取的都是同一个 ServletContext 对象
request
创建:请求一次创建一次
销毁:响应即销毁
存入数据的作用范围:只能是一次请求的多次转发中共享数据
只有在一次请求的多次转发中涉及到的多个 servlet 使用的才是一个 request 对象
session(私有数据)
创建:java 认为当浏览器首次执行到了 request.getSession 方法的时候创建 session 对象
销毁:
被动销毁:默认当前 session 30 分钟不使用会被销毁
主动销毁:session.invalidate()
服务器非正常关闭 session 会销毁
服务器正常关闭 session 不会销毁,会持久化到硬盘上,服务器开启后,会默认从硬盘上回到服务器中
存入数据的作用范围:在一次会话中涉及的多个servlet可以共享数据
因为一次会话中,多个 sevlet 中获取的 session 是同一个
域对象操作的 API
// 存放数据 void setAttribute(String name, Object value) // 获得数据 Object getAttribute(String name) // 删除数据 void removeAttribute(String name)