论坛首页 Java企业应用论坛

从代理模式到Java反射机制

浏览 17628 次
精华帖 (2) :: 良好帖 (8) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-03-31   最后修改:2010-04-08

        代理模式,相信大多数人都非常熟悉,常见的实现方式是通过公共接口的方式,让我们的目标类和代理类实现同一接口,在代理类中调用目标类对象的方法。具体请看我另一个博客中的文章:Java的代理模式(通过公共接口实现) 。通过接口的方式,有个不好的地方,就是对每个目标类都要写一对与之相对应的接口和代理类,如果业务类很多,就是非常繁锁的工作了。

 

而加入反射机制的代理模式,可实现一个公共的代理类,省去我们不少功夫。Java的java.lang.reflect包及其子包中提供了Class、Method、Annotation等有用的类。下面,写个方法代理的类MethodProxy,实现动态地调用对象的方法。

 

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
 * 方法代理类
 * @author rongxinhua
 *
 */
public class MethodProxy {
	
	private Class clazz;	//对象所属的类
	private Object target;	//目标对象
	private Method method;	//目标方法
	private Object[] params;	//参数数组
	
	@SuppressWarnings("unchecked")
	public MethodProxy(Object target, String methodName, Object ... params) {
		rebindTarget(target, methodName, params);	//设置目标对象与方法
	}
	
	/**
	 * 重新设置目标对象与方法
	 * @param target
	 * @param methodName
	 * @param params
	 */
	public void rebindTarget(Object target, String methodName, Object ... params) {
		this.target = target;
		this.clazz = target.getClass();
		rebindMethod(methodName, params);	//设置目标方法
	}
	
	/**
	 * 重新设置目标方法
	 * @param methodName
	 * @param params
	 */
	public void rebindMethod(String methodName, Object ...params) {
		this.params = params;
		int paramLength = params.length;
		Class[] paramTypes = new Class[paramLength];
		for(int i = 0 ; i < paramLength ; i ++ ) {
			paramTypes[i] = params[i].getClass();
		}
		try {
			this.method = clazz.getMethod(methodName, paramTypes);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 动态调用已绑定的方法
	 */
	public void doMethod() {
		try {
			this.method.invoke(target, params);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

}

 

这样就可以实现动态地调用某个对象的某个方法了,写个测试代码如下:

public class Manager {
	
	public void say() {
		System.out.println("Nobody say nothing");
	}
	
	public void love(String boy, String girl) {
		System.out.println(boy + " love " + girl);
	}
	
}

 

 我们通过代理类来调用Manager类中的say()和love()方法,测试代码如下:

		Manager man = new Manager();	//目标对象
		MethodProxy proxy = new MethodProxy(man, "say");	//方法代理对象
		proxy.doMethod();	//调用被代理的方法
		proxy.rebindMethod("love", "Tom", "Marry");	//重新绑定方法
		proxy.doMethod();	//调用被代理的方法

 

这样就实现了动态代理调用对象的方法,上面代码输出结果就不贴出来了。如果要设置前置通知和后置通知等功能,也很容易实现,只需在“proxy.doMethod()”代码处的前面和后面设置即行。


扩展应用:我们在上面的MethodProxy类中加入以下方法:

	/**
	 * 获取方法上的注解
	 * @param anClazz 注解类
	 * @return
	 */
	public Annotation getAnnotation(Class anClazz) {
		return this.method.getAnnotation(anClazz);
	}

 

这个方法用来读取方法上的注解(Annotation),有什么用呢?我们写一个注解来测试下。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Low {
	int boyAge();	//男孩法定的谈恋爱年龄
	int girlAge();	//女孩法定的谈恋爱年龄
}

我们要引进Annotation相关的类:

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

 

我们另外写一个测试用的业务类:

public class LoveManager {
	
	@Low(boyAge=12, girlAge=10)
	public void beAbleToLove(Person boy, Person girl) {
		System.out.println(boy.getName() + " is able to love " + girl.getName());
	}
	
}

public class Person {
	private String name;
	private int age;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	//getter方法略
}

 

接写上例中的proxy对象测试代码:

		LoveManager loveManager = new LoveManager();
		Person boy = new Person("Tom", 13);
		Person girl = new Person("Marry", 10);
		proxy.rebindTarget(loveManager, "beAbleToLove", boy, girl);	//重新绑定对象和方法
		Low low = (Low)proxy.getAnnotation(Low.class);
		if(boy.getAge() < low.boyAge()) {
			System.out.println(boy.getName() + "还不到法定年龄,不能谈恋爱!");
		} else if(girl.getAge() < low.girlAge()) {
			System.out.println(girl.getName() + "还不到法定年龄,不能谈恋爱!");
		} else {
			proxy.doMethod();
		}

 

根据boy和girl的年龄大小,会相应地输出下列之一:

 

Tom还不到法定年龄,不能谈恋爱!

Marry还不到法定年龄,不能谈恋爱!

Tom is able to love Marry

 

这就实现了,通过Java的反射来读取Annotation的值,并根据Annotation的值,来处理业务数据有效性的判断,或者面向切面动态地注入对象,或者作日志、拦截器等等。这种用法在所多框架中都常常看到, 我们在开发自己的Java组件时,不妨也采用一下吧!

   发表时间:2010-03-31  
对反射机制不了解,刚好看这楼主这篇文章来学习。
0 请登录后投票
   发表时间:2010-04-01  
不实现API中的InvocationHandler动态代理  不错

看来有必要去弄弄注解了
0 请登录后投票
   发表时间:2010-04-01  
一直在用JDK1.4的规范,通过这篇文章看来,真需要对新的规范加紧学习,深入了解了
0 请登录后投票
   发表时间:2010-04-01  
有个问题,所谓代理应该是操作类和被操作类之间的一层,代理类操作被操作类所提供的方法,加入其他行为,可以让操作类能把代理类当成原操作类无差别的使用。而这里,方法的形式不是都改变了吗?可以理解层利用反射机制的通用封装,但不是代理吧
0 请登录后投票
   发表时间:2010-04-01  
If abstract doMethod(), and let concrete class to customize doMethod(), that would be better. Otherwise, there is no way to demonstrate the concept of 'Proxy'.
0 请登录后投票
   发表时间:2010-04-01  
rongxh2010 写道

 /**
* 重新设置目标方法
* @param methodName
* @param params
*/
public void rebindMethod(String methodName, Object ...params) {
this.params = params;
int paramLength = params.length;
Class[] paramTypes = new Class[paramLength];
for(int i = 0 ; i < paramLength ; i ++ ) {
paramTypes[i] = params[i].getClass();
}
try {
this.method = clazz.getMethod(methodName, paramTypes);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}


通过params 一定能得到正确的 method的 paramTypes吗?  子类可以代替父类,实现类可以代理接口作为参数.

这样 params[i].getClass 已经不是method meta里面的 class了.

只能说在通常情况下 ,能找到method

1 请登录后投票
   发表时间:2010-04-01  
比较经典的文章
0 请登录后投票
   发表时间:2010-04-01  
正在学习java的反射机制 谢谢分享
0 请登录后投票
   发表时间:2010-04-01  
wkbulletin 写道
rongxh2010 写道

 /**
* 重新设置目标方法
* @param methodName
* @param params
*/
public void rebindMethod(String methodName, Object ...params) {
this.params = params;
int paramLength = params.length;
Class[] paramTypes = new Class[paramLength];
for(int i = 0 ; i < paramLength ; i ++ ) {
paramTypes[i] = params[i].getClass();
}
try {
this.method = clazz.getMethod(methodName, paramTypes);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}


通过params 一定能得到正确的 method的 paramTypes吗?  子类可以代替父类,实现类可以代理接口作为参数.

这样 params[i].getClass 已经不是method meta里面的 class了.

只能说在通常情况下 ,能找到method


    是啊,如果class类中的方法的参数是个类,这个类存在子类,在直接调用class的时候,可以将子类作为参数出入,但通过类的反射,class.getMethod是找不到

    以子类为参数定义的方法的,谁能告诉我这种情况下该如何解决?

0 请登录后投票
论坛首页 Java企业应用版

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