拦截器已经是各个开源软件必不可少的功能。 在讨论各种问题的时候也经常听说这个对象被拦截了等等。那么在JAVA的世界里, 是怎么实现拦截器的功能的呢 ? 要了解这些, 必须先从代理类(Proxy)说起,但是我们在这里不打算从这里介绍,我们直接上Mybatis的测试代码。
public class PluginTest {
@Test
public void mapPluginShouldInterceptGet() {
Map map = new HashMap();
map = (Map) new AlwaysMapPlugin().plugin(map);
assertEquals("Always", map.get("Anything"));
}
@Test
public void shouldNotInterceptToString() {
Map map = new HashMap();
map = (Map) new AlwaysMapPlugin().plugin(map);
assertFalse("Always".equals(map.toString()));
}
@Intercepts({
@Signature(type = Map.class, method = "get", args = {Object.class})})
public static class AlwaysMapPlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return "Always";
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
}
MyBatis 直接抽象了一个plugin的概念,中文意思就是“插头”吧,很形象。我要对一个对象拦截,我就要把这个插头插入到目标对象中:
map = (Map) new AlwaysMapPlugin().plugin(map);
这里生成了一个AlwaysMapPlugin,并调用plugin方法,把自己插入到Map的一个实例对象中;我们先不管plugin方法里面到底做了什么,我们只要知道它还是给我返回了一个Map的实例。
assertEquals("Always", map.get("Anything"));
接着我们就调用了Map实例的get方法,我们纳闷了,我们并没有给Map实例中添加任何数据,但是却能得到"Always”。
很神奇吧...
要解开这个神奇,还是要回到AlwaysMapPlugin().plugin(Object object)的方法上来。
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
直接委托给了Plugin类的一个静态方法:
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
我们先抛开乱七八糟的逻辑,我们看到在满足一定的条件的情况下,会返回一个Proxy.newProxyInstance类的一个实例。也就是说AlwaysMapPlugin().plugin(Object object)的方法返回的实例,是有可能和原来的实例不是同一个,而是原来实例的一个代理类。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
这个是Proxy.newProxyInstance方法的签名,不明白的同志自己去了解下。
我们已经得到了目标对象的代理类,可是我们只想对目标对象的某几个方法进行拦截, 是怎么做到的呢?
这次我们把目光转向我们的“插头”吧。
@Intercepts({
@Signature(type = Map.class, method = "get", args = {Object.class})})
public static class AlwaysMapPlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return "Always";
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
这里通过注解告诉我们,会对Map.get(Object obj)的方法进行拦截。而且在拦截之后,直接返回了Always, 这就是最终的原因了。
罗里吧嗦一大堆,我们直接看MyBatis里面抽象的几个相关类吧:
1. Plugin 类:也就是我们说的“插板”类, 继承了InvocationHandler 接口,主要的职责是将目标对象、拦截器组装成一个新的代理类。 简单的代码如下:
public class Plugin implements InvocationHandler {
private Object target; // 目标对象
private Interceptor interceptor; //拦截器对象
private Map<Class<?>, Set<Method>> signatureMap; //目标对象的方法签名
private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance( // 返回一个目标对象的代理类
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));//对目标对象中的某个方法进行拦截,将相关的实现给到了拦截器的实现类
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
2. Interceptor 拦截器的接口:
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable; // 对方法进行拦截的抽象方法
Object plugin(Object target); //把拦截器插入到目标对象的方法
void setProperties(Properties properties);
}
3. Invocation 真正对目标类方法的拦截的实现, 这里没有什么说的。 值得一提的是, 如果我们想实现类似spring的拦截器,比如说前置通知、后置通知、环绕通知等,应该是可以在这里做文章的。
public class Invocation {
private Object target;
private Method method;
private Object[] args;
public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
public Object getTarget() {
return target;
}
public Method getMethod() {
return method;
}
public Object[] getArgs() {
return args;
}
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}
4. 搞定。
总结:其实拦截器还是很简单的, 只需要熟识了Proxy类、InvocationHandler接口, 基本都能写出来,只是写的好与坏,是不是低耦合、高内聚的代码。类的命名是不是让别人一看就懂,这些都决定着一个人水平的高低;
拦截器使用的场景: 比如说 权限、日志记录、缓存等等;
先分享到这里.....
- 大小: 48.3 KB
分享到:
相关推荐
代码包含: EscapeUtil.java:特殊字符(\,_,%)转义工具类 MyQueryInterceptor.java: Mybatis自定义拦截器 注意:该拦截器只支持QueryWrapper的like方法,serviceImpl层传全角模糊查询(%%) mapper或xml层的全角模糊查询(%...
特殊字符(\,_,%)转义工具类 MyQueryInterceptor.java: Mybatis自定义拦截器 注意:该拦截器只支持QueryWrapper的like方法,serviceImpl层传全角模糊查询(%%) mapper或xml层的全角模糊查询(%*%)和半角模糊查询(%*或*%)
mybatis分页拦截器(自动封装版)剖析.pdfmybatis分页拦截器(自动封装版)剖析.pdfmybatis分页拦截器(自动封装版)剖析.pdfmybatis分页拦截器(自动封装版)剖析.pdfmybatis分页拦截器(自动封装版)剖析.pdfmybatis分页拦截...
Mybatis分页拦截器,mybatis-3.1.1和mybatis-3.0.4版本下测试通过。
使用mybatis的拦截器功能实现分页操作,使分页代码在整个项目中通用,减少代码冗余。
主要给大家介绍了关于mybatis拦截器实现通用权限字段添加的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用mybatis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
下面小编就为大家带来一篇MyBatis拦截器:给参数对象属性赋值的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
通过Mybatis拦截器自动定位慢SQL并记录日志
通过mybatis的拦截器,实现为所有sql(或指定sql) 统一添加查询条件,譬如通过线程变量传递某参数(日期),来实现对指定参数的数据筛选,而不需要在每个查询前,手动将该条件注入到查询中。因该资料网络较少,故特此...
mybatis mysql 分页拦截器,可以针对方法名称,或指定的参数进行拦截
通过mybatis拦截器将查询语句、更新语句、删除语句、插入语句中指定表明替换为另一个表名
MyBatis拦截器分页与动态修改SQL及其参数值 提取SQL Like 字段
Mybatis拦截器记录数据更新历史记录到MongoDB的源码,另外需要配置拦截器到mybatis配置文件中。
mybatis拦截器的完整实现,test.sql是数据库测试脚本,主要目的是生成mybatis最终执行的sql语句,并打印出来,方便调试。 基于此,可以实现自动化分页。
MyBatis拦截器分页代码,网上看到的,供大家一起共同分享学习。
Mybatis 如何防止 sql 注入?mybatis 拦截器了解过吗,应用场景是什么答案 Mybatis 如何防止 sql 注入?mybatis 拦截器了解过吗,应用场景是什么答案