打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

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

 

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

 

Java代码
  1. import java.lang.reflect.InvocationTargetException;   
  2. import java.lang.reflect.Method;   
  3. /**  
  4.  * 方法代理类  
  5.  * @author rongxinhua  
  6.  *  
  7.  */  
  8. public class MethodProxy {   
  9.        
  10.     private Class clazz;    //对象所属的类   
  11.     private Object target;  //目标对象   
  12.     private Method method;  //目标方法   
  13.     private Object[] params;    //参数数组   
  14.        
  15.     @SuppressWarnings("unchecked")   
  16.     public MethodProxy(Object target, String methodName, Object ... params) {   
  17.         rebindTarget(target, methodName, params);   //设置目标对象与方法   
  18.     }   
  19.        
  20.     /**  
  21.      * 重新设置目标对象与方法  
  22.      * @param target  
  23.      * @param methodName  
  24.      * @param params  
  25.      */  
  26.     public void rebindTarget(Object target, String methodName, Object ... params) {   
  27.         this.target = target;   
  28.         this.clazz = target.getClass();   
  29.         rebindMethod(methodName, params);   //设置目标方法   
  30.     }   
  31.        
  32.     /**  
  33.      * 重新设置目标方法  
  34.      * @param methodName  
  35.      * @param params  
  36.      */  
  37.     public void rebindMethod(String methodName, Object ...params) {   
  38.         this.params = params;   
  39.         int paramLength = params.length;   
  40.         Class[] paramTypes = new Class[paramLength];   
  41.         for(int i = 0 ; i < paramLength ; i ++ ) {   
  42.             paramTypes[i] = params[i].getClass();   
  43.         }   
  44.         try {   
  45.             this.method = clazz.getMethod(methodName, paramTypes);   
  46.         } catch (SecurityException e) {   
  47.             e.printStackTrace();   
  48.         } catch (NoSuchMethodException e) {   
  49.             e.printStackTrace();   
  50.         }   
  51.     }   
  52.        
  53.     /**  
  54.      * 动态调用已绑定的方法  
  55.      */  
  56.     public void doMethod() {   
  57.         try {   
  58.             this.method.invoke(target, params);   
  59.         } catch (IllegalArgumentException e) {   
  60.             e.printStackTrace();   
  61.         } catch (IllegalAccessException e) {   
  62.             e.printStackTrace();   
  63.         } catch (InvocationTargetException e) {   
  64.             e.printStackTrace();   
  65.         }   
  66.     }   
  67.   
  68. }  

 

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

Java代码
  1. public class Manager {   
  2.        
  3.     public void say() {   
  4.         System.out.println("Nobody say nothing");   
  5.     }   
  6.        
  7.     public void love(String boy, String girl) {   
  8.         System.out.println(boy + " love " + girl);   
  9.     }   
  10.        
  11. }  

 

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

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

 

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


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

Java代码
  1. /**  
  2.  * 获取方法上的注解  
  3.  * @param anClazz 注解类  
  4.  * @return  
  5.  */  
  6. public Annotation getAnnotation(Class anClazz) {   
  7.     return this.method.getAnnotation(anClazz);   
  8. }  

 

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

Java代码
  1. @Retention(RetentionPolicy.RUNTIME)   
  2. @Target(ElementType.METHOD)   
  3. @interface Low {   
  4.     int boyAge();   //男孩法定的谈恋爱年龄   
  5.     int girlAge();  //女孩法定的谈恋爱年龄   
  6. }  

我们要引进Annotation相关的类:

Java代码
  1. import java.lang.annotation.Annotation;   
  2. import java.lang.annotation.ElementType;   
  3. import java.lang.annotation.Retention;   
  4. import java.lang.annotation.RetentionPolicy;   
  5. import java.lang.annotation.Target;  

 

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

Java代码
  1. public class LoveManager {   
  2.        
  3.     @Low(boyAge=12, girlAge=10)   
  4.     public void beAbleToLove(Person boy, Person girl) {   
  5.         System.out.println(boy.getName() + " is able to love " + girl.getName());   
  6.     }   
  7.        
  8. }   
  9.   
  10. public class Person {   
  11.     private String name;   
  12.     private int age;   
  13.     public Person(String name, int age) {   
  14.         this.name = name;   
  15.         this.age = age;   
  16.     }   
  17.     //getter方法略   
  18. }  

 

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

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

 

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

 

Text代码
  1. Tom还不到法定年龄,不能谈恋爱!   
  2.   
  3. Marry还不到法定年龄,不能谈恋爱!   
  4.   
  5. Tom is able to love Marry  

 

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

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
利用java反射调用类的的私有方法
浅谈———Java注解详解
ResultSet结果集转换为实体对象实现方案
Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM) (清晰,浅显)
Java注解(Annotation)原理详解
[Java] 通过反射,动态修改注解的某个属性值
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服