实现自己的可重用拦截器机制

AOP技术是spring框架的一个重要特征。通过该特性能够在[b]函数运行之前,之后,或者异常处理的时候[/b]执行我们需要的一些操作。

下面我们就是需要抛开AOP,Spring这样成型的框架不用,而仅仅使用java反射机制中的Proxy,InvocationHandler来实现类似Spring框架的拦截器的效果。

[b][size=xx-large]动态代理DynamicProxy[/size][/b]

首先,在设计这个拦截器框架之前,我们需要明白java中动态代理是什么?我想如果早就清楚请直接跳过,如果需要了解,那我想你手边最好有一个javadoc的电子书。

Java.lang.reflect.Proxy是反射包的成员之一。具体说明请查javadoc。
用法就是比如有一个对象,我们需要在调用[b]它提供的方法[/b]之前,干点别的什么,就不能直接调用它,而是生成一个它的代理,这个代理有[b]这个对象所提供的所有接口方法[/b],我们通过直接调用代理的这些方法,来实现:[b]函数既能像原来对象的那样工作,又能在函数运行过程前后加入我们自己的处理。[/b]

这个类有个非常重要的函数用来实现某个类的代理:

Object java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException


参数有点迷惑人,解释下:
[b]ClassLoader[/b] 是类加载器,这个参数用来定义代理类,一般使用原对象的即可,也可以为null用上下文解决。
[b]Class<?>[][/b] 接口数组,就是我们需要这个代理能够提供原来的类的什么函数。如果全部则直接[b]class.getInterfaces()[/b]来解决.
[b]InvocationHandler[/b] 调用处理器,这个就是如果你调用代理的方法,那么这个处理器就会被关联过来,处理调用这个函数的整个过程。这个接口只定义了一个方法:

public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable;

参数中[b]proxy[/b]就是你调用的代理,[b]method[/b]指的是你调用的代理的那个方法,[b]args[/b]是传给该方法的参数。

我们生成某个类的代理步骤,一般需要先考虑我们在调用这个类的函数的时候(之前,或者之后)如何处理某些事情,因此我们首先考虑的就是如何实现[b]InvocationHandler[/b]这个接口。

让我们做一个实践,做这么一个调用处理器:任何使用此处理器的代理在调用它的任何方法的时候,都打印[b]被代理的类的类名+“.”+方法名+”(“+参数+”,”+参数+...+”)”[/b]。

步骤1: 定义接口[b]IUser[/b]

package com.cyh.proxy.sample;

public interface IUser {
public String getName();

public void setName(String name);
}


步骤2: 写IUser接口的实现类[b]User[/b]

package com.cyh.proxy.sample.impl;

import com.cyh.proxy.sample.IUser;

public class User implements IUser {
String name;

public User(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}


步骤3: 写[b]TraceHandler[/b]实现调用处理器[b]InvocationHandler[/b],即在[b]invoke()[/b]方法里我们要打印被[b]代理的类的类名+“.”+方法名+”(“+参数+”,”+参数+...+”)”[/b]。


package com.cyh.proxy.sample.impl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TraceHandler implements InvocationHandler {
private Object target;

public TraceHandler(Object target) {
this.target = target;
}

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {

// print implicit argument
System.out.print(target.getClass().getName());
// print method name
System.out.print("." + method.getName() + "(");
// print explicit arguments
if (args != null) {
for (int i = 0; i < args.length; i++) {
System.out.print(args[i]);
if (i < args.length - 1) {
System.out.print(",");
}
}
}
System.out.println(")");

return method.invoke(this.target, args);
}
}


步骤4: 最后,让我们写测试类[b]ProxyTest[/b]

package com.cyh.proxy.sample.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.cyh.proxy.sample.IUser;
import com.cyh.proxy.sample.impl.TraceHandler;
import com.cyh.proxy.sample.impl.User;

public class ProxyTest {
User user;

public ProxyTest() {
user = new User("LaraCroft");

ClassLoader classLoader = user.getClass().getClassLoader();
Class[] interfaces = user.getClass().getInterfaces();
InvocationHandler handler = new TraceHandler(user);
IUser proxy = (IUser) Proxy.newProxyInstance(classLoader, interfaces,
handler);

proxy.setName("David Beckham");
}

public static void main(String[] args) {
new ProxyTest();
}

}



好了,所有代码写好了,运行一下,测试结果是:
[b]com.cyh.proxy.impl.User.setName(David Beckham)[/b]

讲一下运行原理:
首先我们初始化了user对象,user.name = = “LaraCroft”;
然后创建了user对象的代理proxy。
注意这里:[b]Proxy.newProxyInstance()函数的返回值使用接口IUser转型的,你或许会想到
用User来做强制类型转换,但是会抛出下面的异常[/b]
[color=red]Exception in thread "main" java.lang.ClassCastException: $Proxy0 cannot be cast to com.cyh.proxy.impl.User[/color]
[b]因为:代理类是实现了User类的所有接口,但是它的类型是$Proxy0,不是User。[/b]

最后,我们调用代理的setName()方法:
[b]proxy.setName("David Beckham");[/b]

代理在执行此方法的时候,就好触发调用处理器 TraceHandler,并执行 TraceHandler的invoke()方法,然后就会打印:
[b]com.cyh.proxy.impl.User.setName(David Beckham)[/b]


[size=xx-large][b]可重用拦截器机制的实现[/b][/size]


好了,关于代理的知识我们讲完了,我们可以考虑如何实现这个拦截器的框架,所谓[b]拦截器就是在函数的运行前后定制自己的处理行为[/b],也就是通过实现[b]InvocationHandler[/b]达到的。


[size=x-large][b]设计思路[/b][/size]


我们来理清一下思路,在使用一个拦截器的时候?[b]什么是不变的,什么是变化的?[/b]

[b]不变的:[/b]
每次都要创建代理
拦截的时间:函数执行之前,之后,异常处理的时候

[b]变化的:[/b]
每次代理的对象不同
拦截器每次拦截到执行时的操作不同

好了,废话少说,看类图:

[img]http://www.iteye.com/upload/attachment/36855/1ff800fd-12d0-36b8-b606-6f6b6255fcb4.png[/img]

图中:
[b]DynamicProxyFactory[/b] 和它的实现类,是一个工厂,用来创建代理

[b]Interceptor[/b] 这个接口用来定义拦截器的拦截处理行为配合DynamicProxyInvocationHandler达到拦截效果
[b]DynamicProxyInvocationHandler[/b] 调用处理器的实现,它有两个成员,一个是Object target指的是被代理的类,另一个是Interceptor interceptor就是在invoke()方法执行target的函数之前后,异常处理时,调用interceptor的实现来达到拦截,并处理的效果。


[size=x-large][b]代码实现[/b][/size]

步骤1: 定义接口[b]DynamicProxyFactory[/b]

package com.cyh.proxy.interceptor;

public interface DynamicProxyFactory {
/**
* 生成动态代理,并且在调用代理执行函数的时候使用拦截器
*
* @param clazz
* 需要实现的接口
* @param target
* 实现此接口的类
* @param interceptor
* 拦截器
* @return
*/
public <T> T createProxy(T target, Interceptor interceptor);
}


步骤2: 定义接口[b]Interceptor[/b]

package com.cyh.proxy.interceptor;

import java.lang.reflect.Method;

public interface Interceptor {
public void before(Method method, Object[] args);

public void after(Method method, Object[] args);

public void afterThrowing(Method method, Object[] args, Throwable throwable);

public void afterFinally(Method method, Object[] args);
}


步骤3: 实现接口[b]DynamicProxyFactory[/b]

package com.cyh.proxy.interceptor.impl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.cyh.proxy.interceptor.DynamicProxyFactory;
import com.cyh.proxy.interceptor.Interceptor;

public class DynamicProxyFactoryImpl implements DynamicProxyFactory {
/**
* 生成动态代理,并且在调用代理执行函数的时候使用拦截器
*
* @param target
* 需要代理的实例
* @param interceptor
* 拦截器实现,就是我们希望代理类执行函数的前后,
* 抛出异常,finally的时候去做写什么
*/
@Override
@SuppressWarnings("unchecked")
public <T> T createProxy(T target, Interceptor interceptor) {
// 当前对象的类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
// 获取此对象实现的所有接口
Class<?>[] interfaces = target.getClass().getInterfaces();
// 利用DynamicProxyInvocationHandler类来实现InvocationHandler
InvocationHandler handler = new DynamicProxyInvocationHandler(target,
interceptor);

return (T) Proxy.newProxyInstance(classLoader, interfaces, handler);
}
}


步骤4: 实现调用处理器

package com.cyh.proxy.interceptor.impl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import com.cyh.proxy.interceptor.Interceptor;

/**
* 动态代理的调用处理器
*
* @author chen.yinghua
*/
public class DynamicProxyInvocationHandler implements InvocationHandler {
private Object target;
private Interceptor interceptor;

/**
* @param target
* 需要代理的实例
* @param interceptor
* 拦截器
*/
public DynamicProxyInvocationHandler(Object target,
Interceptor interceptor) {
this.target = target;
this.interceptor = interceptor;
}

/**
* @param proxy
* 所生成的代理对象
* @param method
* 调用的方法示例
* @args args 参数数组
* @Override
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;

try {
// 在执行method之前调用interceptor去做什么事
this.interceptor.before(method, args);
// 在这里我们调用原始实例的method
result = method.invoke(this.target, args);
// 在执行method之后调用interceptor去做什么事
this.interceptor.after(method, args);
} catch (Throwable throwable) {
// 在发生异常之后调用interceptor去做什么事
this.interceptor.afterThrowing(method, args, throwable);
throw throwable;
} finally {
// 在finally之后调用interceptor去做什么事
interceptor.afterFinally(method, args);
}

return result;
}

}


好了,目前为止,这个框架算完成了,怎么用呢?
接下来我们完成测试包。

[size=x-large][b]完成测试[/b][/size]

步骤1: 首先,给需要代理的类定义一个接口[b]Service[/b]

package com.cyh.proxy.interceptor.test;

public interface Service {
public String greet(String name);
}

步骤2: 实现这个接口,编写类[b]ServiceImpl[/b]

package com.cyh.proxy.interceptor.test;

public class ServiceImpl implements Service {
@Override
public String greet(String name) {
String result = "Hello, " + name;
System.out.println(result);
return result;
}
}

步骤3: 实现拦截器接口Interceptor,编写类[b]InterceptorImpl[/b]

package com.cyh.proxy.interceptor.test;

import java.lang.reflect.Method;

import com.cyh.proxy.interceptor.Interceptor;

public class InterceptorImpl implements Interceptor {
@Override
public void after(Method method, Object[] args) {
System.out.println("after invoking method: " + method.getName());
}

@Override
public void afterFinally(Method method, Object[] args) {
System.out.println("afterFinally invoking method: " + method.getName());
}

@Override
public void afterThrowing(Method method, Object[] args,
Throwable throwable) {
System.out.println("afterThrowing invoking method: "
+ method.getName());
}

@Override
public void before(Method method, Object[] args) {
System.out.println("before invoking method: " + method.getName());
}
}

步骤4:编写测试类[b]TestDynamicProxy[/b]

package com.cyh.proxy.interceptor.test;

import com.cyh.proxy.interceptor.DynamicProxyFactory;
import com.cyh.proxy.interceptor.Interceptor;
import com.cyh.proxy.interceptor.impl.DynamicProxyFactoryImpl;

public class TestDynamicProxy {
public TestDynamicProxy() {
DynamicProxyFactory dynamicProxyFactory = new DynamicProxyFactoryImpl();
Interceptor interceptor = new InterceptorImpl();
Service service = new ServiceImpl();

Service proxy = dynamicProxyFactory.createProxy(service, interceptor);
// Service proxy = DefaultProxyFactory.createProxy(service,
// interceptor);
proxy.greet("iwindyforest");
}

public static void main(String[] args) {
new TestDynamicProxy();
}
}


好了,整个测试包完成了,让我们运行下看看运行结果:
[b]
before invoking method: greet
Hello, iwindyforest
after invoking method: greet
afterFinally invoking method: greet
[/b]


[size=x-large][b]完善设计[/b][/size]

现在,让我们回顾一下:接口[b]DynamicProxyFactory[/b],真的需要么?
它只是一个工厂,负责生产代理的,但是我们并没有过多的要求,因此可以说它的实现基本上是不变的。鉴于此,我们在使用[b]createProxy()[/b]函数的时候,只需要一个[b]静态方法[/b]就可以了,没有必要再初始化整个类,这样才比较方便么。
因此,我在[b]com.cyh.proxy.interceptor.impl[/b]包里加了一个默认的工厂[b]DefaultProxyFactory[/b]:

package com.cyh.proxy.interceptor.impl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.cyh.proxy.interceptor.Interceptor;

public class DefaultProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target, Interceptor interceptor) {
// 当前对象的类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
// 获取此对象实现的所有接口
Class<?>[] interfaces = target.getClass().getInterfaces();
// 利用DynamicProxyInvocationHandler类来实现InvocationHandler
InvocationHandler handler = new DynamicProxyInvocationHandler(target,
interceptor);

return (T) Proxy.newProxyInstance(classLoader, interfaces, handler);
}
}


[size=x-large][b]参考书籍:[/b][/size]

[color=red]
Core java Volume I
深入浅出JDK6.0
[/color]

[color=blue]
P.S:本来想系统的写一下反射的学习笔记来,刚刚写了个开头,回来在看,发现写的东西真是很基本很基本,心想这样的东西放到java区没准就会被打到“新手区”了,因此还是没有写,不过临发这篇文章,我心里还是比较忐忑:又是一篇写轮子的文章,这次会不会又进新手区了?
[/color]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值