注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

淡忘邻人

二十七,始发愤

 
 
 

日志

 
 
 
 

反射模式  

2011-09-21 21:58:29|  分类: JAVA |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

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

 

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

 

Java代码 复制代码 收藏代码
package com.board.proxy;

import java.lang.annotation.Annotation;
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; // 参数数组

    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();
        }
    }
   
    /** 
     * 获取方法上的注解 
     * @param anClazz 注解类 
     * @return 
     */ 
    public Annotation getAnnotation(Class anClazz) {  
        return this.method.getAnnotation(anClazz);  
    } 


}

反射模式 - 在水一方 - 在水一方的博客
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()方法,测试代码如下:

Java代码 复制代码 收藏代码反射模式 - 在水一方 - 在水一方的博客
  Manager man = new Manager(); //目标对象   MethodProxy proxy = new MethodProxy(man, "say"); //方法代理对象   proxy.doMethod(); //调用被代理的方法   proxy.rebindMethod("love", "Tom", "Marry"); //重新绑定方法   proxy.doMethod(); //调用被代理的方法

 package com.board.proxy;

/**
 * package com.board.proxy;<br>
 * 类的说明部分
 *
 * @author ljm luojm@e-board.com.cn
 * @date 2011-9-21 下午09:47:20 zhuhai fortune digital technology Co.Ltd
 */
public class ProxyTest
{
    public static void main(String[] args)
    {
        Manager man = new Manager(); // 目标对象
        MethodProxy proxy = new MethodProxy(man, "say"); // 方法代理对象
        proxy.doMethod(); // 调用被代理的方法
        proxy.rebindMethod("love", "Tom", "Marry"); // 重新绑定方法
        proxy.doMethod(); // 调用被代理的方法
        
        
//        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();   
//        }  

    }
}

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


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

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

 

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

Java代码 复制代码 收藏代码反射模式 - 在水一方 - 在水一方的博客
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface Low {  int boyAge(); //男孩法定的谈恋爱年龄  int girlAge(); //女孩法定的谈恋爱年龄 } 

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;

 

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

Java代码 复制代码 收藏代码反射模式 - 在水一方 - 在水一方的博客
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方法略 }

  package com.board.proxy;

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());
    }

}

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

Java代码 复制代码 收藏代码反射模式 - 在水一方 - 在水一方的博客
  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();   }

 package com.board.proxy;

public class Person
{
    private String name;
    private int age;

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

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public int getAge()
    {
        return age;
    }

    public void setAge(int age)
    {
        this.age = age;
    }
    
}


Tom还不到法定年龄,不能谈恋爱!  Marry还不到法定年龄,不能谈恋爱!  Tom is able to love Marry

 

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

  评论这张
 
阅读(212)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017