言成言成啊 | Kit Chen's Blog

Filter和Listener

发布于2020-07-06 21:50:56,更新于2020-07-08 08:41:19,标签:web jsp  转载随意,文章会持续修订,请注明来源地址:https://meethigher.top/blog

javaweb三大组件,Servlet、Filter和Listener。这三者都需要配置,都可以通过web.xml或者注解来配置

一、Filter过滤器

1.1 概念

生活中的过滤器:如净水器、空气净化器,打劫。

web中的过滤器:在浏览器到服务器的请求之间,或者服务器对浏览器的响应之间,进行拦截,以此来完成特殊的功能。

过滤器的作用:一般用于完成通过的操作。如登录验证、统一编码处理、敏感字符过滤

1.2 快速入门

步骤:

  1. 定义一个类,实现Filter接口
  2. 重写方法
  3. 配置拦截路径

两种配置方式:

  1. web.xml配置(参照1.3)
  2. 注解配置(下面使用的就是注解配置)

在过滤器中,要考虑是否放行,如果不放行,访问的资源内容就不会被加载

放行执行的代码filterChain.doFilter(servletRequest, servletResponse); 将request跟response传进去。

jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
测试过滤器..
</body>
</html>

java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 过滤器快速入门
*/
/*
/* 表示访问所有资源之前,都会执行该过滤器
/index.jsp 表示访问index.jsp之前,执行该过滤器
*/
@WebFilter("/*")
public class Demo01Filter implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filter被执行了...");

//考虑是否放行,如果不放行,访问的资源内容就不会被加载
//下面进行放行
filterChain.doFilter(servletRequest, servletResponse);
}

@Override
public void destroy() {

}
}

1.3 过滤器细节

web.xml配置

此处带有之前的servlet的配置,我就不删掉了。为了方便阅读,删掉其实比较好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?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-name>demo01</servlet-name>
<servlet-class>demo01_servlet.Demo01Servlet</servlet-class>
</servlet>
<!--Servlet映射-->
<servlet-mapping>
<servlet-name>demo01</servlet-name>
<url-pattern>/demo01</url-pattern>
</servlet-mapping>

<!--配置Servlet-->
<servlet>
<servlet-name>demo02</servlet-name>
<servlet-class>demo01_servlet.Demo02Servlet</servlet-class>
<!--指定Servlet的创建时机
1. 第一次被访问时,创建。<load-on-startup>为负数,一般为-1
2. 在服务器启动时,创建。<load-on-startup>为0或正整数,一般为0-10
-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--Servlet映射-->
<servlet-mapping>
<servlet-name>demo02</servlet-name>
<url-pattern>/demo02</url-pattern>
</servlet-mapping>

<!--配置Filter-->
<filter>
<filter-name>filter01</filter-name>
<filter-class>demo10_filter.Demo02Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter01</filter-name>
<!-- 拦截路径, /* 表示,访问所有资源都要经过过滤器 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

过滤器执行流程

如果去了解整个底层工作原理,会发现,本质就是一个方法的顺序执行。

  1. 执行过滤器
  2. 放行
  3. 执行放行后的资源
  4. 回来执行过滤器

java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@WebFilter("/*")
public class Demo03Filter implements Filter {
public void destroy() {
}

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//在放行之前,可以对request消息对象进行增强
System.out.println("filter03执行..");

chain.doFilter(req, resp);

//在放行之后,可以对response消息对象进行增强
System.out.println("filter03回来了..");
}

public void init(FilterConfig config) throws ServletException {

}

}

jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
测试过滤器..
<%
System.out.println("访问到index.jsp");
%>
</body>
</html>

过滤器生命周期方法

  1. init:在服务器启动后,就会创建Filter对象,然后调用init方法,只执行一次。用于加载资源。
    • Servlet中默认是Servlet第一次被访问创建时,仅执行一次。此时Filter的init会比Servlet的先执行
    • Servlet也可以配置在启动服务器即执行,一般用于加载资源。经过测试,此时也仍然是Filter的init先执行
  2. doFilter:每一次请求,被拦截资源时执行。具体的拦截操作逻辑,放在这里
  3. destroy:在服务器正常关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次,用于释放资源

过滤器配置详解

  1. 拦截路径配置

    • 具体的资源路径:/index.jsp,只有访问index.jsp资源时,过滤器才会被执行。不过这种情况一般比较少,因为过滤器通常是用来拦截通用操作的。比方说,登录验证、敏感词过滤、统一编码
    • 目录拦截:/user/*,访问/user下的所有资源时,过滤器都会被执行
    • 后缀名拦截:*.jsp,访问所有后缀名为jsp资源时,过滤器都会被执行
    • 拦截所有资源:/*,访问所有资源时,过滤器都会被执行
  2. 拦截方式配置:资源被访问的方式

    • 注解配置:设置dispatcherTypes属性

      • REQUEST:默认值。浏览器直接请求资源

      • FORWARD:转发访问资源

      • INCLUDE:包含访问资源

      • ASYNC:异步访问资源

      • ERROR:错误跳转资源

    • web.xml配置:在filter-mapping下配置<dispatcher>REQUEST</dispatcher>,取值也是上面5个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//浏览器请求资源时,该过滤器会被执行
//@WebFilter(value="/filter/index.jsp",dispatcherTypes = DispatcherType.REQUEST)
//浏览器转发资源时,该过滤器会被执行
//@WebFilter(value="/filter/index.jsp",dispatcherTypes = DispatcherType.FORWARD)

//浏览器请求资源或者转发资源时,该过滤器会被执行
@WebFilter(value="/filter/index.jsp",dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST})
public class Demo05Filter implements Filter {
public void destroy() {
}

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("filter05..");
chain.doFilter(req, resp);
}

public void init(FilterConfig config) throws ServletException {

}

}

过滤器链(配置多个过滤器)

执行顺序

执行顺序:如果有两个过滤器,分别为过滤器1和过滤器2

  1. 过滤器1
  2. 过滤器2
  3. 资源执行
  4. 过滤器2
  5. 过滤器1

过滤器先后顺序

注解配置:按照类型的字符串比较规则,值小的先执行。

  • 如AFilter和BFilter,A小,故A先执行;Demo6和Demo17比较,Demo17先执行

web.xml配置:<filter-mapping>哪个定义在上边,哪个先执行

1.4 过滤器案例

登录验证

需求分析

需求:

  1. 如果登录,则放行
  2. 如果没有登录,则跳转登录页

分析:

  1. 判断是否是登录相关资源。如果不排除登录资源,会陷入死循环。
    • 是,放行
    • 否,验证是否登录
  2. 判断是否登录
    • 是,放行
    • 否,返回登录页

代码实现

LoginFilter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 登录验证过滤器
*/
@WebFilter("/filter/*")
public class LoginFilter implements Filter {
public void destroy() {
}

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//强制转换,不然没法使用其子类方法
HttpServletRequest request= (HttpServletRequest) req;

//1.获取资源请求路径
String requestURI = request.getRequestURI();
//2.判断是否包含登录相关的资源
if (requestURI.contains("/filter/login.jsp") || requestURI.contains("/filter/loginServlet")||requestURI.contains("/filter/checkCodeServlet")||requestURI.contains("/css/")||requestURI.contains("/js/")) {
//放行
System.out.println("登录资源,放行");
chain.doFilter(req,resp);
}else{
//验证是否登录
HttpSession session = request.getSession();
if(session.getAttribute("user")!=null){
System.out.println("已登录,放行");
chain.doFilter(req,resp);
}else{
request.setAttribute("login_error","您尚未登录");
request.getRequestDispatcher("login.jsp").forward(request,resp);
}
}

}

public void init(FilterConfig config) throws ServletException {

}

}

LoginServlet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@WebServlet("/filter/loginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 设置request编码
request.setCharacterEncoding("utf-8");
HttpSession session = request.getSession();
// 2. 获取参数Map集合
// Map<String, String[]> map = request.getParameterMap();
String username = request.getParameter("username");
String password = request.getParameter("password");
String checkcode = request.getParameter("checkcode");
// 3. 获取验证码
String code = (String)session.getAttribute("checkcode");
//这是为了使验证码保持一次性使用
session.removeAttribute("checkcode");
//忽略大小写
if(code!=null&&code.equalsIgnoreCase(checkcode)){
if("胡列娜".equals(username)&&"fairy".equals(password)){
//存储信息
session.setAttribute("user",username);
//重定向
response.sendRedirect("/filter/success.jsp");
}else{
//存储信息
request.setAttribute("login_error","用户名或者密码错误");
//转发
request.getRequestDispatcher("/filter/login.jsp").forward(request,response);
}
}else{
//存储信息
request.setAttribute("cc_error","验证码错误");
//转发
request.getRequestDispatcher("/filter/login.jsp").forward(request,response);
}
// 4. 将用户信息封装到User对象
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}

CheckCodeServlet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@WebServlet("/filter/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int width=100;
int height=30;

//1.创建对象,在内存中画图(验证码图片对象)
BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//2.美化图片
//2.1填充背景色
Graphics g=image.getGraphics();//绘图对象
g.setColor(new Color(255,255,255,255));
g.fillRect(0,0,width,height);
//2.2画边框
g.setColor(Color.RED);
g.drawRect(0,0,width-1,height-1);
//2.3写验证码
String str="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
Random r=new Random();
//StringBuilder在处理多个字符串拼接的时候,效率要比+拼接要高很多。
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 4; i++) {
//设置字体,绘制字符
g.setFont(new Font(Font.SANS_SERIF,Font.BOLD,20));
char c = str.charAt(r.nextInt(str.length()));
stringBuilder.append(c);
g.drawString(String.valueOf(c),width/5*(i+1),height/2+10);
}
//将验证码存入session
String checkcode=stringBuilder.toString();
request.getSession().setAttribute("checkcode",checkcode);
//2.4 画干扰线
g.setColor(Color.green);
for (int i = 0; i < 10; i++) {
int x1=r.nextInt(width);
int y1=r.nextInt(height);
int x2=r.nextInt(width);
int y2=r.nextInt(height);
g.drawLine(x1,y1,x2,y2);//画线
//画点
// g.drawOval(x1,y1,5,5);
// g.fillOval(x1,y1,5,5);
// g.drawOval(x2,y2,5,5);
// g.fillOval(x2,y2,5,5);
}
//3.将图片输出到页面展示
ImageIO.write(image,"jpg",response.getOutputStream());
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}

login.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
<style>
* {
margin: 0;
padding: 0;
}

.box {
background-color: #74C3B5;
color: #fff;
width:100vw;
height:100vh;
position:fixed;
}
.container {
width: 600px;
margin: 80px auto;
text-align: center;
}

.logo {
width: 250px;
height: 70px;
margin: auto;
border-radius: 5px;
margin-bottom: 40px;
font-size:44px;
}


input {
color: #fff;
width: 250px;
height: 40px;
/*opacity:.5;*/
transition: all ease .4s;
background-color: rgba(255, 255, 255, .2);
text-align: center;
font-size: 20px;
border: 2px solid rgba(255, 255, 255, .4);
outline: none;
border-radius: 5px;
}
img {
outline: 0;
border: 1px solid rgba(255, 255, 255, 0.4);
background-color: rgba(255, 255, 255, 0.2);
width: 250px;
height: 70px;
border-radius: 3px;
padding: 5px 8px;
margin: 0 auto 5px auto;
display: block;
text-align: center;
color: white;
opacity: .9;
box-sizing: border-box;
}

input:focus {
color: #75DFB7;
width: 300px;
background-color: #fff;
}

button {
width: 250px;
height: 40px;
border: none;
background-color: rgba(255, 255, 255, .9);
font-size: 20px;
color: #75DFB7;
cursor: pointer;
border-radius: 5px;
}

ul {
list-style: none;
}

li {
line-height: 50px;
}

form a {
text-decoration: none;
color: #fff;
}

input::placeholder {
color:#fff;
}
.bg-bubbles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
pointer-events: none;
}
.bg-bubbles li {
position: absolute;
list-style: none;
display: block;
width: 40px;
height: 40px;
background-color: rgba(255, 255, 255, 0.15);
bottom: -160px;
-webkit-animation: square 25s infinite;
animation: square 25s infinite;
-webkit-transition-timing-function: linear;
transition-timing-function: linear;
}
.bg-bubbles li:nth-child(1) {
left: 10%;
}
.bg-bubbles li:nth-child(2) {
left: 20%;
width: 80px;
height: 80px;
-webkit-animation-delay: 2s;
animation-delay: 2s;
-webkit-animation-duration: 17s;
animation-duration: 17s;
}
.bg-bubbles li:nth-child(3) {
left: 25%;
-webkit-animation-delay: 4s;
animation-delay: 4s;
}
.bg-bubbles li:nth-child(4) {
left: 40%;
width: 60px;
height: 60px;
-webkit-animation-duration: 22s;
animation-duration: 22s;
background-color: rgba(255, 255, 255, 0.25);
}
.bg-bubbles li:nth-child(5) {
left: 70%;
}
.bg-bubbles li:nth-child(6) {
left: 80%;
width: 120px;
height: 120px;
-webkit-animation-delay: 3s;
animation-delay: 3s;
background-color: rgba(255, 255, 255, 0.2);
}
.bg-bubbles li:nth-child(7) {
left: 32%;
width: 160px;
height: 160px;
-webkit-animation-delay: 7s;
animation-delay: 7s;
}
.bg-bubbles li:nth-child(8) {
left: 55%;
width: 20px;
height: 20px;
-webkit-animation-delay: 15s;
animation-delay: 15s;
-webkit-animation-duration: 40s;
animation-duration: 40s;
}
.bg-bubbles li:nth-child(9) {
left: 25%;
width: 10px;
height: 10px;
-webkit-animation-delay: 2s;
animation-delay: 2s;
-webkit-animation-duration: 40s;
animation-duration: 40s;
background-color: rgba(255, 255, 255, 0.3);
}
.bg-bubbles li:nth-child(10) {
left: 90%;
width: 160px;
height: 160px;
-webkit-animation-delay: 11s;
animation-delay: 11s;
}
@-webkit-keyframes square {
0% {
-webkit-transform: translateY(0);
transform: translateY(0);
}
100% {
-webkit-transform: translateY(-700px) rotate(600deg);
transform: translateY(-700px) rotate(600deg);
}
}
@keyframes square {
0% {
-webkit-transform: translateY(0);
transform: translateY(0);
}
100% {
-webkit-transform: translateY(-700px) rotate(600deg);
transform: translateY(-700px) rotate(600deg);
}
}
error {
color:deeppink;
}
</style>
</head>
<body>
<%
String login_error = (String) request.getAttribute("login_error");
String cc_error = (String) request.getAttribute("cc_error");
%>
<div class="box">
<div class="container">
<div class="logo">
住房公积金
</div>
<%
if(login_error!=null){
out.write("<error>"+login_error+"</error>");
}
if(cc_error!=null){
out.write("<error>"+cc_error+"</error>");
}
%>
<form action="/filter/loginServlet" method="post">
<ul>
<li><input type="text" name="username" placeholder="用户名" autocomplete="off"></li>
<li><input type="text" name="password" placeholder="密码" autocomplete="off"></li>
<li><input type="text" name="checkcode" placeholder="不区分大小写"></li>
<li><img id="checkcode" src="/filter/checkCodeServlet" alt=""></li>
<li>
<button>登录</button>
</li>
</ul>
</form>
</div>
<ul class="bg-bubbles">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<script>
document.querySelector("#checkcode").onclick=function (){
let url = "/filter/checkCodeServlet";
let date=new Date().getTime();
this.setAttribute("src", url+"?"+date)
}
</script>
</body>
</html>

success.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>主页</title>
<style>
* {
margin: 0;
padding: 0;
}

.box {
background-color: #74C3B5;
color: #fff;
width: 100vw;
height: 100vh;
position: fixed;
}
</style>
</head>
<body>
<div class="box">
<%
String user = (String) request.getSession().getAttribute("user");
out.write(user+"欢迎你");

%>
</div>
</body>
</html>

注意事项:

应当注意,像login.jsp,LoginServlet.java,CheckCodeServlet.java,css,js这些都属于登录资源,访问这些的时候,直接放行。

敏感词过滤验证

需求分析

需求:表单中含有胡列娜或者江厌离或者邱若水就自动转为***

分析:在过滤器中,对request传过来的内容进行修改,将修改内容后的request进行放行(request中并没有setParameter这个方法)

解决方法:

  1. 对request对象的getParameter方法进行增强(通过getParameter获取到的内容就是已经过滤的了)。产生一个新的request对象
  2. 放行。将新的request对象传入

重点分析:如何对geParameter方法进行增强?

增强对象的功能的途径:

  • 设计模式:一些通用的解决固定问题的方式,参考
    • 装饰模式
    • 代理模式
      • 概念:真实对象(比如小米公司)、代理对象(比如小米旗舰店),代理模式(代理对象代替真实对象,达到增强真实对象的目的)
      • 静态代理(有一个类文件描述代理模式)
      • 动态代理(在内存中形成代理类)
        • 实现步骤
          • 代理对象和真实对象实现相同的接口
          • 代理对象=Proxy.newProxyInstance()
          • 使用代理对象调用方法
          • 增强方法
        • 增强方式
          • 增强参数列表
          • 增强返回值类型
          • 增强方法体执行逻辑

理解动态代理demo

下面通过动态代理模拟一个现实生活中的例子,买电脑。

接口SellComputer

1
2
3
4
5
public interface SellComputer {
public String sale(double money);

public void show();
}

接口SellComputer的实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 真实类
* @author https://github.com/meethigher
*/
public class Lenovo implements SellComputer {
@Override
public String sale(double money) {
System.out.println("花钱"+money+"买电脑");
return "联想电脑";
}

@Override
public void show() {
System.out.println("展示电脑...");
}
}

动态代理增强对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class ProxyTest {
public static void main(String[] args) {
//1.创建真实对象
Lenovo lenovo = new Lenovo();


//2.调用方法
//但是我们不应该直接通过真实对象来处理,我们需要通过一个增强对象
//因为直接通过真实对象方法处理并不满足我们需求,就比方说request的getParameter实现敏感词过滤
// String sale = lenovo.sale(8000);//花钱8000买电脑
// System.out.println(sale);//联想电脑

//2.动态代理增强lenovo对象

/**
* 三个参数
* 1. 类加载器:真实对象.getClass().getClassLoader()
* 2. 接口数组:真实对象.getClass().getInterfaces()
* 3. 处理器:new InvocationHandler(),这个就是增强对象的核心方法
*/
SellComputer proxy_lenovo = (SellComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {

/**
* 代理逻辑编写的方法:代理对象调用的所有方法,都会触发该方法执行
* @param proxy 代理对象,一般我们不会用
* @param method 代理对象调用的方法,被封装的对象
* @param args 代理对象调用方法时,传递的实际参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 判断是否是sale方法
* 若是,对其增强再返回
* 若否,直接原样返回
*/
if(method.getName().equals("sale")){
//1.增强参数
double money = (double)args[0];
money=money*1.2;
//使用真实对象调用该方法
System.out.println("专车接你...");
System.out.println("免费送货...");
String obj = (String)method.invoke(lenovo, money);
return obj+"&&鼠标垫";
}else{
Object obj = method.invoke(lenovo, args);
return obj;
}

}
});
String sale = proxy_lenovo.sale(8000);//专车接你...免费送货...花钱9600.0买电脑
System.out.println("买到了"+sale);//买到了联想电脑&&鼠标垫
proxy_lenovo.show();//展示电脑...
}
}

代码实现

index.jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="testServlet" method="post">
<textarea name="remark"></textarea>
<input type="submit" value="提交">
</form>
</body>
</html>

SensitiveFilter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@WebFilter("/filter_proxy/*")
public class SensitiveFilter implements Filter {
public void destroy() {
}

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//1.创建代理对象,增强getParameter方法
ServletRequest proxy_req = (ServletRequest)Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断getParameter,getParameterMap,getParameterValue
//此处指拿getParameter举个例子
if(method.getName().equals("getParameter")){
//增强返回值。先获取返回值,进行过滤,再返回
// req.setCharacterEncoding("utf-8");
String value= (String)method.invoke(req, args);
if(value!=null){
for(String str:list){
if(value.contains(str)){
value=value.replaceAll(str,"***");
}
}
}
return value;
}else{
return method.invoke(req,args);
}
}
});
//2.放行
chain.doFilter(proxy_req, resp);
}

private List<String> list=new ArrayList<String>();//敏感词汇集合
public void init(FilterConfig config) throws ServletException {
//加载配置文件
try {
//1.获取文件真实路径
ServletContext sc = config.getServletContext();
String realPath = sc.getRealPath("/WEB-INF/classes/sensitivewords.txt");
//2.读取文件
BufferedReader br = new BufferedReader(new FileReader(realPath, StandardCharsets.UTF_8));
String line=null;
while((line=br.readLine())!=null){
list.add(line);
}
System.out.println("加载敏感词");
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}

}

TestServlet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
@WebServlet("/filter_proxy/testServlet")
public class TestServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
String remark = request.getParameter("remark");
response.getWriter().write("<h1>"+remark+"</h1>");
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}

二、Listener监听器

2.1 概念

web的三大组件之一。

事件监听机制

  • 事件:一件事情。比方说点击按钮
  • 事件源:事件发生的地方
  • 监听器:一段代码或者一个对象
  • 注册监听:将事件、事件源、监听器绑定在一起,当事件源上发生某个事件后,执行监听器代码

2.2 ServletContextListener

这是个接口,用来监听ServletContext对象的创建和销毁

两个方法

  • void contextDestroyed(ServletContextEvent sce):ServletConext对象被销毁之前,会调用该方法
  • void contextInitialized(ServletContextEvent sce):ServletContext对象被创建之后,会调用该方法

用途:一般用于全局资源文件的加载

步骤

  1. 定义一个类,实现ServletContextListener接口
  2. 重写方法
  3. 配置
    • web.xml配置
    • 注解配置:直接在类上面添加@WebListener

web.xml

1
2
3
4
5
6
7
8
9
10
<!--配置监听器,此处可以通过@WebListener来代替-->
<listener>
<listener-class>demo14_listener.ContextLoaderListener</listener-class>
</listener>

<!--指定要加载配置文件的初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>

demo演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@WebListener
public class ContextLoaderListener implements ServletContextListener {
/**
* 监听ServletContext对象创建的。ServletContext对象服务器启动后自动创建
* 在服务器启动后自动调用
* @param sce
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
//加载资源文件
//1.获取ServletContext对象
ServletContext servletContext = sce.getServletContext();
//2.加载资源文件
String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
//3.获取真实路径
String realPath = servletContext.getRealPath(contextConfigLocation);
//4.加载进内存
try {
FileInputStream fis = new FileInputStream(realPath);
System.out.println(fis);
} catch (FileNotFoundException e) {
e.printStackTrace();
}


System.out.println("ServletContext对象被创建了");
}

/**
* 在服务器关闭后,ServletContext对象被销毁。
* 当服务器被正常关闭后,该方法被调用
* @param sce
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext对象被销毁了");
}
}
发布:2020-07-06 21:50:56
修改:2020-07-08 08:41:19
链接:https://meethigher.top/blog/2020/filter-and-listener/
标签:web jsp 
付款码 打赏 分享
shift+ctrl+1可控制目录显示