`
iwindyforest
  • 浏览: 230050 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

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

阅读更多
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
分享到:
评论
10 楼 jacky68147527 2009-08-25  
写的不错,支持一下!
9 楼 kimmking 2009-03-03  
云中苍月 写道
很支持楼主!不断的思考,不断的实践。
造自己的轮子,让别人说去吧。

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

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

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

好像这年头,什么都是框架~ NB
8 楼 云中苍月 2009-03-03  
很支持楼主!不断的思考,不断的实践。
造自己的轮子,让别人说去吧。
7 楼 kimmking 2009-03-03  
很基本,很低效

动态代理的使用 讲的还是很清楚的~~
6 楼 kekeemx 2009-03-03  
最近刚好在看反射方面的东西.不错的入门级资料。
5 楼 iwindyforest 2008-11-16  
to jander:
引用
给你增强一下,实现aop链。

:D 不错,好想法,谢谢你!
4 楼 jander 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);
	}
}

3 楼 cun2001 2008-10-30  
写得不错,挺适合java反射、代理以及AOP入门的,代码有点小问题:InterceptorImpl.java每个方法前不能加@Override,道理楼主应该知道,因为InterceptorImpl类是实现Interceptor接口而不是继承,文章有些错别字,不过这些都是小问题,不过楼主精神挺好,赞一个!也不知楼主现在找到中意的东家没,如果还没有,也不用太着急,是金子总会发光的,是人才总不会被埋没的,我看好你,希望你找个好工作!
2 楼 Joo 2008-08-31  
这个文章对于我的意义在于阐述了AOP框架的存在价值
详情见这里 http://www.iteye.com/post/646283
1 楼 taupo 2008-08-30  
楼主,抛开新旧轮子的考虑,有没有想过反射的效率太低了?

相关推荐

    理解拦截器用于在某个方法或字段被访

    同时也是提供了一种可以提取action中可重用的部分的方式。 谈到拦截器,还有一个词大家应该知道——拦截器链(Interceptor Chain,在Struts 2中称为拦截器栈Interceptor Stack)。拦截器链就是将拦截器按一定的顺序...

    java拦截器

    拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。 在Webwork的中文文档的解释为——拦截器是动态拦截Action调用...

    计算机科学系毕业设计论文范文

    Struts 2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的设计也使得业务逻辑控制器能够与Servlet API完全脱离开,所以Struts 2可以理解为WebWork的更新产品。因为Struts 2和Struts 1有着太大的变化,...

    asp.net知识库

    采用XHTML和CSS设计可重用可换肤的WEB站点 asp.net的网址重定向方法的比较:面向搜索引擎友好 也谈 ASP.NET 1.1 中 QueryString 的安全获取写法 ASP.NET运行模式:PageHandlerFactory 利用搜索引擎引用来高亮页面...

    Spring中文帮助文档

    13.4.3. 拦截器(HandlerInterceptor) 13.5. 视图与视图解析 13.5.1. 视图解析器(ViewResolver) 13.5.2. 视图解析链 13.5.3. 重定向(Rediret)到另一个视图 13.6. 本地化解析器 13.6.1. ...

    Spring API

    13.4.3. 拦截器(HandlerInterceptor) 13.5. 视图与视图解析 13.5.1. 视图解析器(ViewResolver) 13.5.2. 视图解析链 13.5.3. 重定向(Rediret)到另一个视图 13.6. 本地化解析器 13.6.1. ...

    Spring面试题

    4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能 5.容器提供了众多的辅助类,能加快应用的开发 6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等 7.spring属于低侵入...

    精通AngularJS part1

    HTTP响应拦截器204 创建securityInterceptor服务205 创建securityRetryQueue服务207 通知安全服务208 76防止导航到安全受限路由208 使用路由resolve函数209 创建授权服务210 77总结212 第8章创建自定义指令...

    基于jbpm与activiti的工作流平台技术架构介绍

    系统提供在线流程设计器,在线表单设计器,代码生成器,结合BPMX3的基础组件,以实现复杂的流程业务应用。基础组件包括: Spring基础组件库,报表引擎,数据库访问模块,短信模块,后台定时任务调用组件,短信访问...

    【分布式事务----LCN】LCN原理及使用方式.docx

    TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行业务...

    经典JAVA.EE企业应用实战.基于WEBLOGIC_JBOSS的JSF_EJB3_JPA整合开发.pdf

    8.8 拦截器 332 8.9 依赖注入 335 8.9.1 EJB注入 336 8.9.2 资源注入 339 8.10 配置EJB引用 340 8.11 使用计时器进行任务调度 342 8.12 本章小结 345 第9章 消息驱动EJB 346 9.1 JMS和EJB 347 9.1.1 为什么使用MDB ...

    linux网路编程 中文 23M 版

    1.7 Linux软件开发的可借鉴之处........................:.................. 12 1-8 .................................................................13 第2 章Linux编程环境...................................

Global site tag (gtag.js) - Google Analytics