Filter&Listener&Json
掌握filter的编写
能够使用filter实现权限过滤和统一字符编码
一、Filter
1.1、Filter概述
- Filter表示过滤器,是JavaWeb三大组件(Servlet、Filter、Listener)之一
- 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能
- 如下图所示,浏览器可以访问服务器上的所有资源(Servlet、jsp、html等)
- 访问到这些资源之前可以使过滤器拦截下来,也就是说在访问资源之前会先经过Filter
- 过滤器功能
- 过滤器一般完成一些通用的操作
- 比如每个资源都要写一些代码完成某个功能,我们总不能在每个资源中都写一样的代码
- 而此时可以将这些代码写在过滤器中,因为请求每一个资源都要经过过滤器
- 过滤器一般完成一些通用的操作
1.2、Filter入门
1.2.1、开发步骤
-
Filter
开发分成以下三个步骤-
1.定义类,实现
Filter
接口,并重写其所有方法-
public class FilterDemo implements Filter { public void init(FilterConfig filterConfig){...} public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain){...} public void destroy(){...} }
-
-
2.配置
Filter
拦截资源的路径:在类上定义@WebFilter
注解,其注解的value
属性值跟@WebServlet
的匹配规则基本一致-
拦截所有资源:
@WebFilter("/*")
-
@WebFilter(/*) public class FilterDemo implements Filter {....}
-
-
3.在
doFilter(...)
方法中编写拦截资源的逻辑代码-
public void doFilter(....){ System.out.println("filter 被执行了..."); // 放行 filterChain.doFilter(servletRequest, servletResponse); }
-
其中放行的意思是,对当前客户端访问服务端的资源,不设置拦截;否则客户端无法访问
-
-
1.2.2、Filter执行流程
-
上图是过滤器的执行流程,不难发现,其特点为
- 放行后访问对应的资源,资源访问结束后,还会继续执行过滤器中放行后的逻辑代码
-
总结
Filter
的执行流程
1.2.3、Filter拦截路径配置
- 拦截路径概念
- 拦截路径表示
Filter
会对哪些请求的资源进行拦截,使用@WebFilter
注解进行配置。如:@WebFilter("拦截路径")
- 拦截路径表示
- 拦截路径的四种配置方式
- 拦截具体的资源
/index.jsp
:只有访问index.jsp
文件的时候才会被拦截
- 目录拦截
/user/*
:访问/user
下的所有资源,都会被拦截
- 后缀名拦截
*.jsp
:访问后缀名为jsp
的资源,都会被拦截
- 拦截所有资源:
/*
:访问所有资源,都会被拦截
- 拦截具体的资源
- 总结
- 可以发现拦截路径的配置方式和
servlet
的请求资源路径配置一样,但是表示的含义不同
- 可以发现拦截路径的配置方式和
servlet的补充
- tomcat或者其他的servlet容器,都提供了两个servlet:DefaultServlet和JspServlet
- JspServlet的处理路径为”*.jsp”:用来处理所有jsp请求(jsp本质上也是一个servlet),第一次请求这个jsp文件的时候,JspServlet就会将这个jsp文件装成java文件,然后编译成class文件,class文件才能执行.生成了响应
- DefaultServlet的处理路径为”/”,用来处理其他servlet都处理不了的请求,找对应的静态资源,若找到就把静态资源(流)读进来,生成响应代码. 若也没有找到这个资源,就会通过tomcat或者其他的servlet容器生成404状态码
1.2.4、过滤器链
- 概念
- 过滤器链是值在一个Web应用中,可以配置多个过滤器,这多个过滤器成为过滤器链
- eg
- 过滤器链的执行流程
- 执行
Filter1
的放行前逻辑代码 - 执行
Filter1
的放行代码 - 执行
Filter2
的放行前逻辑代码 - 执行
Filter2
的放行代码 - 访问到资源
- 执行
Filter2
的放行后逻辑代码 - 执行
Filter1
的放行后逻辑代码
- 执行
PS:
- 过滤器的执行流程和类的名称有一定的关系
- 当使用注解方式配置Filter的拦截路径的时候,其会根据类名的ascii码值的大小来分出执行顺序
- 如Filter2和Filter3,就会先执行Filter2过滤器
- 如BFilterDemo和AFilterDemo,就会先执行AFilterDemo过滤器
1.3、权限过滤案例
1.3.1、需求
- 当访问服务器的时候,需要先进行验证,如果没有登录,则自动跳转到登录页面
1.3.2、代码实现
-
Filter
代码-
@WebFilter("/*") public class LoginFilter implements Filter throws ServletException, IOException{ public void init(FilterConfig config) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { HttpServletRequest req = (HttpServletRequest) request; //1. 判断session中是否有user(PS:当用户成功登录后,会将用户的部分信息保存至Session) HttpSession session = req.getSession(); Object user = session.getAttribute("user"); //2. 判断user是否为null if(user != null){ // 登录过了 //放行 chain.doFilter(request, response); }else { // 没有登陆,存储提示信息,跳转到登录页面 req.setAttribute("login_msg","您尚未登陆!"); req.getRequestDispatcher("/login.jsp").forward(req,response); } } public void destroy() { } }
-
1.3.3、代码可能出现的问题
-
不难看出,因为过滤器拦截了所有的资源,甚至连静态资源都不能访问,同时主页页面也会被拦截;显然,这种设计并不合理
-
完整过滤器代码
-
@WebFilter("/*") public class LoginFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { HttpServletRequest req = (HttpServletRequest) request; //判断访问资源路径是否和登录注册相关 //1,在数组中存储登陆和注册相关的资源路径 String[] urls = {"/login.jsp","/imgs/","/css/","/loginServlet","/register.jsp","/registerServlet","/checkCodeServlet"}; //2,获取当前访问的资源路径 String url = req.getRequestURL().toString(); //3,遍历数组,获取到每一个需要放行的资源路径 for (String u : urls) { //4,判断当前访问的资源路径字符串是否包含要放行的的资源路径字符串 /* 比如当前访问的资源路径是 /brand-demo/login.jsp 而字符串 /brand-demo/login.jsp 包含了 字符串 /login.jsp ,所以这个字符串就需要放行 */ if(url.contains(u)){ //找到了,放行 chain.doFilter(request, response); //break; return; } } //1. 判断session中是否有user HttpSession session = req.getSession(); Object user = session.getAttribute("user"); //2. 判断user是否为null if(user != null){ // 登录过了 //放行 chain.doFilter(request, response); }else { // 没有登陆,存储提示信息,跳转到登录页面 req.setAttribute("login_msg","您尚未登陆!"); req.getRequestDispatcher("/login.jsp").forward(req,response); } } public void init(FilterConfig config) throws ServletException { } public void destroy() { } }
-
1.4、统一编码过滤器
1.4.1、需求
- CURD操作中,当想要添加某一条数据,或者修改某一条数据的时候,很有可能会出现中文乱码的问题;因为Tomcat中对POST请求并没有进行编码限制(Tomcat8及以上只对GET请求进行了编码限制)
1.4.2、代码实现
-
@WebFilter("/*") public class CharacterEncodingFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // Filter.super.init(filterConfig); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 向下转型 HttpServletRequest request = (HttpServletRequest) servletRequest; // 判断请求是否为POST请求 String method = request.getMethod(); if ("post".equalsIgnoreCase(method)) { // 如果是POST请求则设置编码 request.setCharacterEncoding("utf-8"); } // 不管是不是,最后都要放行;不用分别在if和else中都写放行 filterChain.doFilter(request, servletResponse); } @Override public void destroy() { // Filter.super.destroy(); } }
1.5、请求方式过滤
-
过滤器默认只过滤从浏览器直接发过来的请求。
- 由其他Servlet请求转发过来的请求并不会拦截
-
解决方法–修改默认拦截方式
-
@WebFilter(value = "/c/*",dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST})
-
DispatcherTpye
属性设置了之后,其过滤器的默认值就会失效 -
同时
DispatcherType
属性可以设置多个值
-
二、Listener
2.1、概念
- Listener表示监听器,是JavaWeb三大组件(Servlet、Filter、Listener)之一。
- 监听器可以监听就是在
application
,session
,request
三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件。 application
概念application
是ServletContext
类型的对象ServletContext
代表整个Web应用,在服务器启动的时候,Tomcat会自动创建该对象。在服务器关闭的时候会自动销毁该对象
2.2、监听器分类
-
监听器 监听器名称 作用 ServletContext监听 ServletContextListener 用于对ServletContext对象进行监听(创建、销毁) ServletContextAttributeListener 对ServletContext对象中属性进行监听(增删改属性) Session监听 HttpSessionListener 对Session对象的整体状态的监听(创建、销毁) HttpSessionAttributeListener 对Session对象中的属性进行监听(增删改属性) HttpSessionBindingListener 对Session对象的绑定和解除进行监听 HttpSessionActivationListener 对Session数据的钝化和活化进行监听 Request监听 ServletRequestListener 对Request对象进行监听(创建、销毁) ServletRequestAttributeListener 对Request对象中的属性进行监听(增删改属性)
这八类监听器之中,ServletContextListener在后期学习中最常使用
- 其接口中主要有以下两种方法
void contextInitialized(ServletContextEvent sce)
:ServletContext
对象被创建了会自动执行的方法void contextDestroyed(ServletContextEvent sce)
:ServletContext
对象被销毁时会自动执行的方法
-
代码演示
-
@WebListener public class ContextLoaderListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { //加载资源 System.out.println("ContextLoaderListener..."); } @Override public void contextDestroyed(ServletContextEvent sce) { //释放资源 } }
-
三、JSON
3.1、概念
-
JavaScript Object Notation:Java对象表示法
-
JavaScript中对象的创建方法
-
{ name:"zhangsan", age:23, city:"北京" }
-
-
JSON的创建格式
-
{ "name":"zhangsan", "age":23, "city":"北京" }
-
-
-
对比发现
- 通过上面 js 对象格式和 json 格式进行对比,发现两个格式特别像。只不过 js 对象中的属性名可以使用引号(可以是单引号,也可以是双引号)
-
JSON的作用
- 由于其语法结构简单,层次结构鲜明,现在多是作为数据载体,在网络中进行数据传输。
3.2、JSON基础语法
3.2.1、定义格式
- JSON对象
- 使用
{}
,里面的内容是键值对 {user:"张三", age:"18", ...}
- 使用
- JSON数组
- 使用
[]
,里面的内容是一个元素 ["a", "zhangsan", 18, true]
- 使用
- JSON数组比较少用,同时也可以将其视为JavaScript中的数组,并没有什么区别
3.2.2、JSON示例
-
JSON
本质是一个字符串,但是该字符串内容是有一定的格式要求-
let 变量名 = '{"key": value, ...}';
-
JSON
字符串的键要求必须使用双引号括起来,而且值要根据表示的类型来确定
-
-
JSON中的数据类型
- 数字(整数或浮点数)
- 字符串(使用双引号括起来)
- 逻辑值(true或者false)
- 数组(在方括号中)
- 对象(在花括号中)
- null
-
示例
-
<script> //定义一个对象 let user1 = {"name":"张无忌","age":18,"sex":"男"}; console.log(user1.age); //定义json数组:描述张家的三代人 let arr = [ {"name":"张三丰","age":98,"sex":"男"}, {"name":"张翠山","age":38,"sex":"男"}, {"name":"张无忌","age":18,"sex":"男"} ]; console.log(arr[1].name); //描述韦小宝: 7个老婆 一个师傅 let user2 = { "name":"韦小宝", "age":18, "sex":"男", "master":{ "name":"杨广武", "age":29, "sex":"中" }, "wives":[ {"name":"双儿","sex":"女","age":22}, {"name":"建宁","sex":"女","age":16} ] }; console.log(user2.wives[0].name); </script>
-
3.3、JSON和JavaScript对象的相互转换
-
parse(str)
:将 JSON串转换为 js 对象。使用方式是:var jsObject = JSON.parse(jsonStr);
-
stringify(obj)
:将 js 对象转换为 JSON 串。使用方式是:var jsonStr = JSON.stringify(jsObject)
-
代码演示
-
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> //1. 定义JSON字符串 var jsonStr = '{"name":"zhangsan","age":23,"addr":["北京","上海","西安"]}' alert(jsonStr); //2. 将 JSON 字符串转为 JS 对象 let jsObject = JSON.parse(jsonStr); alert(jsObject) alert(jsObject.name) //3. 将 JS 对象转换为 JSON 字符串 let jsonStr2 = JSON.stringify(jsObject); alert(jsonStr2) </script> </body> </html>
-
3.4、JSON和Java对象的相互转换
3.4.1、Fastjson概述
Fastjson
是阿里巴巴提供的一个Java语言编写的高性能完善的JSON
库,是目前Java语言中最快的JSON
库,可以实现java
对象和JSON
字符串的相互转换- 不过还有其他比较不错的工具类
jackson
json-lib
3.4.2、Fastjson的使用
-
Fastjson
的使用步骤-
1.导入坐标
-
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency>
-
-
2.Java对象转为JSON
-
String jsonStr = JSON.toJSONString(obj);
-
将 Java 对象转换为 JSON 串,只需要使用
Fastjson
提供的JSON
类中的toJSONString()
静态方法即可。
-
-
3.JSON字符串转成Java对象
-
User user = JSON.parseObject(jsonStr, User.class);
-
将 json 转换为 Java 对象,只需要使用
Fastjson
提供的JSON
类中的parseObject()
静态方法即可。
-
-
3.4.3、代码实现
-
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.coolman.pojo.User; import org.junit.Test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class FastJsonTest { @Test // Java对象转JSON public void testJavaBeanToJson() { User user = new User(1, "coolman", "110", "123"); // 将Java对象转成json字符串 String jsonStr = JSON.toJSONString(user); System.out.println("jsonStr = " + jsonStr); // jsonStr = {"id":1,"name":"coolman","password":"123","phoneNumber":"110"} } @Test // JSON转Java public void testJsonToJavaBean() { String jsonStr = "{\"id\":1,\"name\":\"coolman\",\"password\":\"123\",\"phoneNumber\":\"110\"}"; User user = JSON.parseObject(jsonStr, User.class); System.out.println("user = " + user); } @Test //Map转JSON public void testMapToJson() { HashMap<String, Object> map = new HashMap<>(); map.put("name","平平"); map.put("age",35); map.put("sex","女"); //将map类型转成json字符串 String jsonStr = JSON.toJSONString(map); System.out.println(jsonStr);//{"sex":"女","name":"平平","age":35} } @Test // public void testJsonToMap() { String jsonStr = "{\"id\":1,\"name\":\"coolman\",\"password\":\"123\",\"phoneNumber\":\"110\"}"; Map map = JSON.parseObject(jsonStr, Map.class); System.out.println(map); } @Test //List转JSON public void testListToJson(){ List<User> userList = new ArrayList<>(); User user = new User(); user.setName("杨广武"); user.setId(1); user.setPassword("123"); user.setPhoneNumber("666"); User user1 = new User(); user1.setId(2); user1.setName("hehe"); user1.setPassword("123"); user1.setPhoneNumber("777"); userList.add(user); userList.add(user1); //将list转成json字符串 String jsonStr = JSON.toJSONString(userList); System.out.println("jsonStr = " + jsonStr); //jsonStr = [{"id":1,"name":"杨广武","password":"123","phoneNumber":"666"},{"id":2,"password":"123","phoneNumber":"777"}] } @Test public void testJsonToList() { String jsonStr = "[{\"id\":1,\"name\":\"杨广武\",\"password\":\"123\",\"phoneNumber\":\"666\"},{\"id\":2,\"password\":\"123\",\"phoneNumber\":\"777\"}]"; List<User> users = JSON.parseArray(jsonStr, User.class); System.out.println("users = " + users); } }