Java内功修炼系列一拦截器
在动态代理中,我们知道在代理类中,执行真实对象的方法前后可以增加一些其他的逻辑,这些逻辑并不是真实对象能够实现的方法,比如一个租房的用户希望租一套公寓,但是中介所代理的这个房东并没有可以出租的公寓,那么这时候就需要在出租房屋之前进行一些其他操作了,比如中介拒绝用户的请求或者帮助找其他用户等。对这部分逻辑的实现我们可以提取成一个拦截器,就在用户租房之前先问中介有没有公寓可以出租,如果中介所代理的房东没有公寓,则中介不接受这个请求,否则接受请求并代理房东出租公寓,出租完成之后中介还可以执行其他操作。顾名思义,拦截器的功能就是拦截真实对象的方法,怎么拦截呢?首先在调用之前先执行一个before() 的方法,这个方法用来判断能不能调用真实对象的方法,如果能则调用,否则对其进行拦截,即不让它执行,并进行自己的处理,执行around() 方法,在调用真实对象或者around()方法之后,还可以调用after()方法进行其他操作。但是拦截器的使用是需要以来动态代理的,通过动态代理来决定各个方法的执行顺序,所以我们自己在实现一个拦截器的时候,还是要定义一个接口,这是jdk动态代理必须的。
下面以最常见的用户登陆为例,假设客户端的请求是登陆,它只关心能不能登陆成功,但是服务器在处理这个登陆请求的时候要进行一些自己的判断,不能不管三七二十一就让登陆成功了吧?比如登陆成功之前先判断用户的用户名和密码是否正确,如果成功则执行登陆请求,否则再次跳转到登陆页,让用户重新输入用户名或密码。而不管登陆成功与否,都对这次登陆过程进行日志记录。
第一步:定义一个用户登陆的拦截器接口LoginInterceptor
/**
* @author hyc
* 定义一个登陆的拦截器接口:一般由开发者实现
*/
public interface LoginInterceptor{ /**
* @param proxy 代理对象
* @param target 真实对象
* @param metod 代理方法
* @param args 方法参数
* @return
* 定义三个接口:如果before方法执行成功,则反射真实对象方法
* 如果before方法执行失败,则调用executeAction方法进行拦截
* 在真实对象方法或拦截处理方法调用之后,则调用after方法
* 应用实例:用户登陆时,对登陆方法拦截,登陆之前检查用户名密码是否正确,如果正确则调用登陆方法,否则返回登陆页面
*/ //登陆之前执行,用来验证用户名密码是否正确
public boolean before(Object proxy,Object target,Method metod,Object[] args); //如果用户名和密码不正确,则跳转到登陆页面,否则进行登陆
public void around(Object proxy,Object target,Method metod,Object[] args); //跳转到登陆页或继续登陆之后需要执行的一些方法,比如记录日志
public void after(Object proxy,Object target,Method metod,Object[] args);
}
第二步:定义一个登陆类LoginInterceptorImpl,实现上面的接口
/*
* 实现接口中的方法
*/
public class LoginInterceptorImpl implements LoginInterceptor{ public boolean before(Object proxy, Object target, Method metod, Object[] args) {
System.out.println("反射方法之前逻辑:登陆之前校验用户名和密码是否正确");
String userName = "";
String pwd = "";
boolean result = false;
try {
userName = (String) args[0];
pwd = (String) args[1];
if(userName.equals("zhangsan") && pwd.equals("123456")) {
result = true;
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
} public void around(Object proxy, Object target, Method Method , Object[] args) {
System.out.println("取代被代理对象的方法:校验失败后跳转到登陆页面"); } public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射方法之后逻辑:记录登陆日志");
} }
第三步:定义真实对象接口LoginInterface
/*
* 定义登陆接口,由登陆类实现
*/
public interface LoginInterface { /**
* 使用用户名和密码进行登陆
* @param name
* @param pwd
* LoginInterface.java
*/
public void login(String name,String pwd);
第四步:定义真实类UserLogin,实现上述接口,并添加登陆逻辑
/*
* 定义登陆类,实现具体的登陆逻辑
*/
public class UserLogin implements LoginInterface {
@Autowired
HttpServletRequest request; /**
* 这个方法中只执行登陆成功后的逻辑:比如设置session、登陆跳转等
*/
public void login(String name, String pwd) {
System.out.println("登陆成功,跳转到首页");
} }
第五步:定义中介类,实现动态代理的逻辑,动态获取代理对象
/**
* 中介类:实现动态代理的逻辑
*
* @author hyc
*
*/
public class InterceptorJdkProxy implements InvocationHandler { private Object target;//真实对象
private String interceptorClass = null;//拦截器名称 public InterceptorJdkProxy(Object target, String interceptorClassName) {
this.target = target;
this.interceptorClass = interceptorClassName;
} // 通过反射获取代理对象
public static Object bind(Object target, String interceptorClassName) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InterceptorJdkProxy(target, interceptorClassName));
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 如果拦截器为空,则执行真实对象的方法
if (interceptorClass == null) {
return method.invoke(target, args);
} Object result = null;
// 否则使用反射生成拦截器
LoginInterceptor interceptor = (LoginInterceptor) Class.forName(interceptorClass).newInstance();
// 如果拦截前方法执行成功,则执行真实对象的方法,否则执行拦截器中的方法
if (interceptor.before(proxy, target, method, args)) {
result = method.invoke(target, args);
} else {
interceptor.around(proxy, target, method, args);
} // 执行完拦截方法或真实对象方法之后,调用after方法
interceptor.after(proxy, target, method, args);
return result;
} }
第六步:用户请求,通过中介类获取代理对象并执行登陆请求
/*
* 用户请求登陆
*/
public class LoginController {
public static void main(String[] args) {
//创建真实对象
LoginInterface loginObject = new UserLogin();
//获取代理对象
LoginInterface proxy = (LoginInterface) InterceptorJdkProxy.bind(loginObject, "com.daily.interceptor.LoginInterceptorImpl");
//调用代理方法
proxy.login("zhangsan1","123456");
}
}
第七步:查看执行结果
1⃣️将用户名传zhangsan时
反射方法之前逻辑:登陆之前校验用户名和密码是否正确
登陆成功,跳转到首页
反射方法之后逻辑:记录登陆日志
1⃣️将用户名传zhangsan1时
反射方法之前逻辑:登陆之前校验用户名和密码是否正确
取代被代理对象的方法:校验失败后跳转到登陆页面
反射方法之后逻辑:记录登陆日志
从执行结果可以看到,当用户名传的值,和在拦截器中校验的相同时,登陆方法没有被拦截;反之登陆被拦截并跳转至登陆页,可见该拦截器实现了拦截的功能。
一般来说,动态代理的逻辑由架构设计者完成,他只要将接口暴露给普通开发人员,普通开发人员不需要知道动态代理的实现过程,只要定义自己的拦截器并完成拦截逻辑就可以,就像上面的例子,我们可以看到动态代理的实现过程对普通开发者是“不可见的”,他们只需根据设计者的接口提供相关的参数即可。
以上就是自己实现拦截器的过程。
Java内功修炼系列一拦截器的更多相关文章
- Java内功修炼系列一责任链模式
在上一节的拦截器中提到,程序的设计者一般会用拦截器替替代动态代理,将动态代理的逻辑隐藏起来,而把拦截器接口提供给开发者,使开发者不需要关系动态代理的具体实现过程,但是有时候需要多个拦截器,而且拦截器之 ...
- Java内功修炼系列一反射
“JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制 ...
- Java内功修炼系列一工厂模式
工厂模式是一种创建型模式,它提供了一种新的创建对象的方式,一般情况下我们都习惯用new关键字直接创建对象.有时候会遇到这种情况,我们需要根据具体的场景选择创建什么类型的对象,可能有多种类型都能选择,但 ...
- Java内功修炼系列一观察者模式
观察者模式又称发布-订阅模式,就是观察者通过订阅被观察者,或关注被观察者,从而实时更新观察者的信息.比如我们玩微博的时候,如果关注了一些博主,那么当博主发动态时,在首页微博列表中就会自动更新这些博主发 ...
- Java内功修炼系列一代理模式
代理模式是JAVA设计模式之一,网上设计模式相关的博文铺天盖地,参考它们有助于自己理解,但是所谓“尽信书不如无书”,在参考的同时也要思考其正确性,写博客也是为了记录自己理解知识点的思路历程和心路历程, ...
- Java 过滤器、监听器、拦截器的区别
原文:http://www.360doc.com/content/10/0601/09/495229_30616324.shtml 1.过滤器 Servlet中的过滤器Filter是实现了ja ...
- Java Web 中 过滤器与拦截器的区别
过滤器,是在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的 action进行业务逻辑,比如过滤掉非法u ...
- java框架之Struts2(4)-拦截器&标签库
拦截器 概述 Interceptor (拦截器):起到拦截客户端对 Action 请求的作用. Filter:过滤器,过滤客户端向服务器发送的请求. Interceptor:拦截器,拦截的是客户端对 ...
- Java Servlet 过滤器与 springmvc 拦截器的区别?
前言:在工作中,遇到需要记录日志的情况,不知道该选择过滤器还是拦截器,故总结了一下. servlet 过滤器 定义 java过滤器能够对目标资源的请求和响应进行截取.过滤器的工作方式分为四种 应用场景 ...
随机推荐
- C#实现程序开机启动
如何用c#实现开机启动?其实用c#实现程序的开机启动大致有两种方法,就是写入注册表或者采用服务程序,最近一直研究着用C#来操作注册表,下面介绍的方法便是用注册表来实现程序随开机启动(高手就不用看了,嘿 ...
- 如何上传文件到git
具体有三大步骤: 一.创建新的仓库 二.本地仓库 三.git命令上传(需要下载git) 一.创建新的仓库 二.本地仓库 其实这个本地仓库就是文件的所在地,在哪都可以 三.git命令上传(需要下载g ...
- 9个搜索引擎优化(SEO)最佳实践
作为网页设计师,搜索引擎优化重要吗?我们知道,网站设计是把屏幕上平淡无奇变成令人愉快的美感,更直观地辨认信息.这也是人与人之间在沟通想法,这样的方式一直在演变. 1. 网站结构 对于搜索引擎优化,网站 ...
- Input:type属性
1.button:定义可点击的按钮(通常与 JavaScript 一起使用来启动脚本). <input id="" type="button" name= ...
- 解决jquery ajax在跨域访问post请求的时候,ie9以下无效(包括ie9)的问题
最近在做项目的时候遇到一个问题,就是跨域请求ajax的时候ie9以下的浏览器不可以访问,直接执行error里面的代码,但是也不报错,就上网查了查,发现了一个很好用的方法,在这里记录一下,也希望可以帮到 ...
- python Selenium chromedriver 自动化超时报错:你需要使用多标签保护罩护体
在使用selenium + chrome 作自动化测试的时候,有可能会出现网页连接超时的情况 如果出现网页连接超时,将会导致 webdriver 也跟着无法响应,不能继续进行任何操作 即时是去打开新的 ...
- Android开发 layer-list详解
参考:https://blog.csdn.net/speverriver/article/details/80925686 挖坑,以后填坑
- [笔记]xshell Session
因之前正常使用的xshell5 绿色版,在重装系统之后 启动时提示缺少 MSCVP110.dll xshell5 绿色版,启动时提示缺少 MSCVP110.dll,在各网站下载了对应的Dll文件,依然 ...
- Oracle SQL性能优化【转】
(1) 选择最有效率的表名顺序(只在基于规则的优化器中有效):ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table) ...
- C++模拟实现Objective-C协议和代理模式
Objective-C的协议和代理是运用最多的特性之一,可以说在苹果系列开发中无处不在.事实上很多人都不知道其背后的原理.事实上简单点说,这就是设计模式中的代理模式的经典运用.代理模式简单点说就是为其 ...