论坛首页 Java企业应用论坛

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

浏览 9836 次
精华帖 (0) :: 良好帖 (5) :: 新手帖 (2) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-08-30   最后修改:2010-11-02
OO
AOP技术是spring框架的一个重要特征。通过该特性能够在函数运行之前,之后,或者异常处理的时候执行我们需要的一些操作。

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

动态代理DynamicProxy

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

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

这个类有个非常重要的函数用来实现某个类的代理:
Object java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, 
             Class<?>[] interfaces, 
             InvocationHandler h) throws IllegalArgumentException


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

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

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

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

步骤1: 定义接口IUser
package com.cyh.proxy.sample;

public interface IUser {
    public String getName();

    public void setName(String name);
}


步骤2: 写IUser接口的实现类User
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: 写TraceHandler实现调用处理器InvocationHandler,即在invoke()方法里我们要打印被代理的类的类名+“.”+方法名+”(“+参数+”,”+参数+...+”)”

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: 最后,让我们写测试类ProxyTest
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();
    }

}



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

讲一下运行原理:
首先我们初始化了user对象,user.name = = “LaraCroft”;
然后创建了user对象的代理proxy。
注意这里:Proxy.newProxyInstance()函数的返回值使用接口IUser转型的,你或许会想到
用User来做强制类型转换,但是会抛出下面的异常

Exception in thread "main" java.lang.ClassCastException: $Proxy0 cannot be cast to com.cyh.proxy.impl.User
因为:代理类是实现了User类的所有接口,但是它的类型是$Proxy0,不是User。

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

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



可重用拦截器机制的实现


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


设计思路


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

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

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

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



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

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


代码实现

步骤1: 定义接口DynamicProxyFactory
package com.cyh.proxy.interceptor;

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


步骤2: 定义接口Interceptor
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: 实现接口DynamicProxyFactory
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;
    }

}


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

完成测试

步骤1: 首先,给需要代理的类定义一个接口Service
package com.cyh.proxy.interceptor.test;

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

步骤2: 实现这个接口,编写类ServiceImpl
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,编写类InterceptorImpl
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:编写测试类TestDynamicProxy
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();
    }
}


好了,整个测试包完成了,让我们运行下看看运行结果:

before invoking method: greet
Hello, iwindyforest
after invoking method: greet
afterFinally invoking method: greet



完善设计

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


参考书籍:


Core java Volume I
深入浅出JDK6.0



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

  • 大小: 6.4 KB
   发表时间:2008-08-30  
楼主,抛开新旧轮子的考虑,有没有想过反射的效率太低了?
0 请登录后投票
   发表时间:2008-08-31  
这个文章对于我的意义在于阐述了AOP框架的存在价值
详情见这里 http://www.iteye.com/post/646283
0 请登录后投票
   发表时间:2008-10-30  
写得不错,挺适合java反射、代理以及AOP入门的,代码有点小问题:InterceptorImpl.java每个方法前不能加@Override,道理楼主应该知道,因为InterceptorImpl类是实现Interceptor接口而不是继承,文章有些错别字,不过这些都是小问题,不过楼主精神挺好,赞一个!也不知楼主现在找到中意的东家没,如果还没有,也不用太着急,是金子总会发光的,是人才总不会被埋没的,我看好你,希望你找个好工作!
0 请登录后投票
   发表时间:2008-11-03  
给你增强一下,实现aop链。
public class DynamicProxyInvocationHandler implements InvocationHandler {
	private Object target;
	private Interceptor[] interceptors;

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

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

		try {
			// 在执行method之前调用interceptor去做什么事
			for(Interceptor interceptor :interceptors){
				interceptor.before(method, args);
			}
			
			// 在这里我们调用原始实例的method
			result = method.invoke(this.target, args);

			// 在执行method之后调用interceptor去做什么事
			for(Interceptor interceptor :interceptors){
				interceptor.after(method, args);
			}		
		} catch (Throwable throwable) {
			for(Interceptor interceptor :interceptors){
				interceptor.afterThrowing(method, args, throwable);
			}			
			throw throwable;
		} finally {
			// 在finally之后调用interceptor去做什么事
			for(Interceptor interceptor :interceptors){
				interceptor.afterFinally(method, args);
			}
		}

		return result;
	}
}


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

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

0 请登录后投票
   发表时间:2008-11-16  
to jander:
引用
给你增强一下,实现aop链。

:D 不错,好想法,谢谢你!
0 请登录后投票
   发表时间:2009-03-03  
最近刚好在看反射方面的东西.不错的入门级资料。
0 请登录后投票
   发表时间:2009-03-03  
很基本,很低效

动态代理的使用 讲的还是很清楚的~~
0 请登录后投票
   发表时间:2009-03-03  
很支持楼主!不断的思考,不断的实践。
造自己的轮子,让别人说去吧。
0 请登录后投票
   发表时间:2009-03-03  
云中苍月 写道
很支持楼主!不断的思考,不断的实践。
造自己的轮子,让别人说去吧。

说aop没啥,
写个类就是框架,框架就漫天飞了

-------------------

5楼增强了下,
楼下的哪位可以再加个序列化的类,rpc框架就出来了~

好像这年头,什么都是框架~ NB
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics