目录
一、简介
这篇文章总结redis缓存链路跟踪器的实现
二、思路
redis的客户端本身是没有提供拦截器的。此外,缓存操作一般也不是一个独立的方法,而是嵌入在某业务方法内部,因此也不可能用AOP来拦截。这里的思路是通过动态代理,提供一个代理类,由它来完成redis缓存操作的拦截。
给redis操作提供定义拦截器的功能
我们准备通过动态代理来给redis操作提供定义拦截器的功能,类似于mybatis。
我们一步一步来,先看看需要哪些储备知识。
-
静态代理
我们可以看到,有3个角色,Action为操作的接口,Operator执行真正的操作逻辑,Proxy引用了Operator的对象,它也实现了Action接口,但是它在doSomething方法中调用Operator对象的该方法,并在调用前后附加自己的逻辑。
public class Proxy implements Action {
private Operator operator;
public Proxy(Operator operator) {
this.operator = operator;
}
public void doSomething() {
preHandle();
operator.doSomething();
postHandle();
}
private void preHandle() {
System.out.println("前置操作");
}
private void postHandle() {
System.out.println("后置操作");
}
}
调用的时候如下
Proxy proxy = new Proxy(new Operator());
proxy.doSomething();
静态代理有一个很大的缺点,就是需要新代理一个接口,就必须修改Proxy类,或者新增一个Proxy类。
比如上面的例子中,我需要新代理一个Handle接口,第一种方式,我们的uml图变为如下所示
很明显Proxy类需要实现Handle接口,并且增加成员变量handler,增加方法doHandle
还有一种方式是新建一个Proxy类
我们希望不用修改Proxy类的代码,也不新增一个Proxy类。这时候就需要动态代理了。
-
动态代理
动态代理解决了新增一个需要代理的接口,也不用修改Proxy类或者新增一个Proxy类。
Proxy类通过反射自动得分析出了需要代理的接口,方法,实际实现类,并把这些对象作为p参数传递给了InvocationHandler接口的实现类的invoke方法。
示例代码如下
public class ActionProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
preHandle();
Object result = method.invoke(target, args);
postHandle();
return result;
}
private void preHandle() {
System.out.println("前置操作");
}
private void postHandle() {
System.out.println("前置操作");
}
}
Action action =
Proxy.newProxyInstance(
this.getClass().getClassLoader(),
Action,
new Operator());
action.doSomething();
这样的话,新增一个需要被代理的接口,只需要在调用的时候修改Proxy.newProxyInstance传递的参数即可,不需要修改Proxy类的代码,或者新增一个Proxy类。
-
mybatis的interceptor实现
mybatis的interceptor的实现就是通过动态代理实现的,我们来看看它是怎么实现的吧
1、启动的时候通过解析配置文件,实例化interceptor,并保存下来
其中紫色代表类,绿色代表方法,蓝色代表方法中的关键代码
XMLConfigBuilder从xml中解析出ingerceptor的类名,并通过反射将其实例化,最后保存到Configuration的interceptorChain成员变量中。
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
2、执行sql操作的时候使用动态代理,代理如下接口,Executor,ParameterHandler,ResultHandler,StatementHandler
其中紫色代表类,绿色代表方法,蓝色代表方法中的关键代码
在执行sql操作之前,会调用newE