原文:http://www.cnblogs.com/kakaxisir/p/4579110.html?utm_source=tuicool&utm_medium=referral
以前一直觉得写博客是给别人看的,所以很少分享自己写的东西。这段时间突然意识到博客是给自己看的。
欢迎各位喜欢java的朋友骚扰。
最近在学习mybatis,看了下源代码。翻到了Interceptor的实现,恰好前不久看过JDK的动态代理和责任链,因此来记录一下。
一:JDK的动态代理
概念性质的东西就不谈了,毕竟网上很多。JDK的动态代理要求接口和接口的实现类
1
2
3
|
public
interface
Target {
public
void
execute();
}
|
1
2
3
4
5
6
7
8
9
10
11
|
/**
* Target的实现类
* @author wpr
*
*/
public
class
TargetImpl
implements
Target {
@Override
public
void
execute() {
System.out.println(
"execute"
);
}
}
|
a.JDK原生的动态代理写法
要求实现InvocationHandler接口,在invoke方法内实现拦截的逻辑(不懂得去看JDK的动态代理)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public
class
TargetProxy
implements
InvocationHandler{
Target target;
public
TargetProxy(Target target) {
this
.target = target;
}
@Override
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
System.out.println(
"拦截前"
);
Object o= method.invoke(target, args);
System.out.println(
"拦截后"
);
return
o;
}
}
|
测试的类:
1
2
3
4
5
6
7
|
@Test
public
void
test3(){
Target target =
new
TargetImpl();
target = (Target) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new
TargetProxy(target));
target.execute();
}
|
以上就是JDK动态代理的实现,但是存在问题,Proxy.newProxyInstance(..)完全可以交给TargetProxy来处理,于是第二版出现
1
2
3
4
5
6
7
|
public
class
TargetProxy
implements
InvocationHandler{
//...........上面的代码省略了...............
public
static
Object bind(Target target){
return
Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new
TargetProxy(target));
}
}
|
测试类:
1
2
3
4
5
6
|
@Test
public
void
test2(){
Target target =
new
TargetImpl();
target = (Target) TargetProxy.bind(target);
target.execute();
}
|
但还是存在问题,业务代码如果是execute()的话,所有的逻辑都写死在invoke()方法里面了,不符合设计模式的要求。结合面向切面的编程,做如下说明,target.execute()视为业务代码,在invoke()方法前进行插入切面(例如记录日志、开启事务等),设计Interceptor接口
1
2
3
|
public
interface
Interceptor {
public
void
intercept();
}
|
intercept()方法负责处理各种前期准备,下面是Interceptor的两个实现类
1
2
3
4
5
6
|
public
class
LogInterceptor
implements
Interceptor{
@Override
public
void
intercept(){
System.out.println(
"日志记录开始"
);
}
}
|
1
2
3
4
5
6
|
public
class
TransactionInterceptor
implements
Interceptor {
@Override
public
void
intercept() {
System.out.println(
"事务开启"
);
}
}
|
代理对象进一步改变,为了形象的说明是拦截器栈,所以我用了Stack,但是感觉使用List(ArrayList更合理一点)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
class
TargetProxy
implements
InvocationHandler{
private
Target target;
private
Stack
public
TargetProxy(Target target, Stack
this
.target = target;
this
.interceptorStack = interceptorStack;
}
@Override
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
for
(Interceptor interceptor:interceptorStack){
interceptor.intercept();
}
return
method.invoke(target, args);
}
}
|
在每次执行业务代码execute(...)之前都会拦截,测试代码如下:
1
2
3
4
5
6
7
8
9
10
11
|
@org
.junit.Test
public
void
test() {
Stack
new
Stack<>();
interceptorStack.add(
new
LogInterceptor());
interceptorStack.add(
new
TransactionInterceptor());
Target target =
new
TargetImpl();
target = (Target) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new
TargetProxy(target, interceptorStack));
target.execute();
}
|
接下来更近一步,根据代码的设计准则,将不变的和变化的分离开。我们设计一个Invocation的类,先看下它的实现:
(其实这个地方还可以这样理解:为了在Interceptor中得到被拦截对象的信息,需要定义一种数据结构来表示被拦截的方法,就是Invocation。这样就实现了拦截器Interceptor和具体的对象之间的解耦)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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;
}
/**
* 调用代理类的方法
* @return
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws InvocationTargetException
*/
public
Object process()
throws
IllegalAccessException, IllegalArgumentException, InvocationTargetException{
return
method.invoke(target, args);
}
//省略了getter和setter
}
|
Invocation类就是将被代理的目标类对立出出来,target表示目标类,method是拦截的方法,args是方法参数,于是新的TargetProxy变成了下面的样子。仅仅是invoke
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
class
TargetProxy
implements
InvocationHandler{
private
Target target;
private
Interceptor interceptor;
public
TargetProxy(Target target,Interceptor interceptor) {
this
.target = target;
this
.interceptor= interceptor;
}
@Override
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
Invocation invocation =
new
Invocation(target, method, args);
return
interceptor.intercpt(invocation);
}
}
|
同时,要改变Interceptor的行为:
1
2
3
4
|
public
interface
Interceptor {
public
Object intercpt(Invocation invocation)
throws
IllegalAccessException, IllegalArgumentException, InvocationTargetException;
}
|
具体的实现如下,一定返回invocation.process();要不然拦截就会断掉
1
2
3
4
5
6
7
8
9
|
public
class
LogInterceptor
implements
Interceptor{
@Override
public
Object intercpt(Invocation invocation)
throws
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
System.out.println(
"打印日志"
);
return
invocation.process();
}
}
|
但是问题又出现了,我们希望目标类只需要了解拦截它的类就可以,并不需要知道它的代理类,于是把target的拦截过程放在Interceptor接口中完成(实际操作交个TargetProxy)。最终我们的Interceptor接口变成了
1
2
3
4
|
public
interface
Interceptor {
public
Object intercept(Invocation invocation)
throws
IllegalAccessException, IllegalArgumentException, InvocationTargetException;
public
Object register(Object object);
}
|
1
2
3
4
5
6
7
8
9
10
11
|
public
class
LogInterceptor
implements
Interceptor{
@Override
public
Object intercept(Invocation invocation)
throws
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
System.out.println(
"日志拦截前:"
);
return
invocation.process();
}
@Override
public
Object register(Object target) {
return
TargetProxy.bind(target,
this
);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
class
TargetProxy
implements
InvocationHandler{
private
Object target;
private
Interceptor interceptor;
public
TargetProxy(Object target, Interceptor interceptor) {
this
.target = target;
this
.interceptor = interceptor;
}
@Override
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
Invocation invocation =
new
Invocation(target, method, args);
return
interceptor.intercept(invocation);
}
public
static
Object bind(Object target,Interceptor interceptor){
return
Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new
TargetProxy(target,interceptor));
}
}
|
到此为止,目标类仅需要知道在执行前应该由谁去拦截它就可以了,测试代码如下:
1
2
3
4
5
6
7
|
@org
.junit.Test
public
void
test() {
Target target =
new
TargetImpl();
Interceptor interceptor =
new
LogInterceptor();
target =(Target) interceptor.register(target);
target.execute();
}
|
好处显而易见,在使用时根本不必知道代理的存在,只要定义业务逻辑,和对业务逻辑的拦截(切面),然后把他们绑定在一起就可以了。
二:责任链
以上代码实现了对一个业务的一次拦截,但如果对其进行多次拦截的话就需要用到责任链了(依然略过概念,自己google吧)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public
class
InterceptorChain {
private
Stack
public
InterceptorChain(Stack
this
.interceptors = interceptors;
}
public
Object registerAll(Object target){
for
(Interceptor interceptor:interceptors){
target = TargetProxy.bind(target, interceptor);
}
return
target;
}
public
void
addInterceptor(Interceptor interceptor){
interceptors.add(interceptor);
}
public
Stack
return
(Stack
}
}
|
registerAll(...)方法来完成对目标的全部代理,一层一层的包裹,测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Test
public
void
interceptorChainTest(){
Stack
new
Stack<>();
LogInterceptor logInterceptor =
new
LogInterceptor();
TransactionInterceptor transactionInterceptor =
new
TransactionInterceptor();
interceptors.add(logInterceptor);
interceptors.add(transactionInterceptor);
InterceptorChain interceptorChain =
new
InterceptorChain(interceptors);
Target target =
new
TargetImpl();
target= (Target)interceptorChain.registerAll(target);
target.execute();
}
|
以上内容都比较基础和理论,但mybatis的Interceptor完全是我们这样实现的
三:mybatis的拦截分析
其中大部分和之前的分析一致,Plugin就是TargetProxy,内部实现的代码逻辑也完全相同,Signature是实现对特定方法拦截的,不在今天的记录范围内。之前的工作相当于完成了这个部分的工作。