锁定老帖子 主题:从代理模式到Java反射机制
精华帖 (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()”代码处的前面和后面设置即行。
/** * 获取方法上的注解 * @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组件时,不妨也采用一下吧! 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-03-31
对反射机制不了解,刚好看这楼主这篇文章来学习。
|
|
返回顶楼 | |
发表时间:2010-04-01
不实现API中的InvocationHandler动态代理 不错
看来有必要去弄弄注解了 |
|
返回顶楼 | |
发表时间:2010-04-01
一直在用JDK1.4的规范,通过这篇文章看来,真需要对新的规范加紧学习,深入了解了
|
|
返回顶楼 | |
发表时间:2010-04-01
有个问题,所谓代理应该是操作类和被操作类之间的一层,代理类操作被操作类所提供的方法,加入其他行为,可以让操作类能把代理类当成原操作类无差别的使用。而这里,方法的形式不是都改变了吗?可以理解层利用反射机制的通用封装,但不是代理吧
|
|
返回顶楼 | |
发表时间: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'.
|
|
返回顶楼 | |
发表时间: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[i].getClass 已经不是method meta里面的 class了. 只能说在通常情况下 ,能找到method |
|
返回顶楼 | |
发表时间:2010-04-01
比较经典的文章
|
|
返回顶楼 | |
发表时间:2010-04-01
正在学习java的反射机制 谢谢分享
|
|
返回顶楼 | |
发表时间: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[i].getClass 已经不是method meta里面的 class了. 只能说在通常情况下 ,能找到method
以子类为参数定义的方法的,谁能告诉我这种情况下该如何解决? |
|
返回顶楼 | |