码迷,mamicode.com
首页 > 编程语言 > 详细

JAVA反射机制与动态代理

时间:2015-03-09 17:33:52      阅读:160      评论:0      收藏:0      [点我收藏+]

标签:java   invoke   classloader   class   

在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。

Java 反射机制主要提供了以下功能

1、在运行时判断任意一个对象所属的类。
2、在运行时构造任意一个类的对象。
3、在运行时判断任意一个类所具有的成员变量和方法。
4、在运行时调用任意一个对象的方法

Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods

一般而言,开发者社群说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言

尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语

在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
1、Class类:代表一个类。
2、Field 类:代表类的成员变量(成员变量也称为类的属性)。
3、Method类:代表类的方法。
4、Constructor 类:代表类的构造方法。
5、Array类:提供了动态创建数组,以及访问数组的元素的静态方法

如例程10-1所示DumpMethods类演示了Reflection API的基本作用,它读取命令行参数指定的类名,然后打印这个类所具有的方法信息:

例程10-1 DumpMethods.java

import java.lang.reflect.*;

public class DumpMethods {

  public static void main(String args[]) throws Exception{

    //加载并初始化命令行参数指定的类

    Class classType = Class.forName(args[0]);

    //获得类的所有方法

    Method methods[] = classType.getDeclaredMethods();

    for(int i = 0; i < methods.length; i++)

      System.out.println(methods[i].toString());

  }

}
<p class="ad">运行命令“java DumpMethods java.util.Stack”,就会显示java.util.Stack类所具有的方法,程序的打印结果如下:</p><p class="a8">public synchronized java.lang.Object java.util.Stack.pop()</p><p class="a8">public java.lang.Object java.util.Stack.push(java.lang.Object)</p><p class="a8">public boolean java.util.Stack.empty()</p><p class="a8">public synchronized java.lang.Object java.util.Stack.peek()</p><p class="a8">public synchronized int java.util.Stack.search(java.lang.Object)</p>

如例程10-2所示ReflectTester类进一步演示了Reflection API的基本使用方法。ReflectTester类有一个copy(Object object)方法,这个方法能够创建一个和参数object同样类型的对象,然后把object对象中的所有属性复制到新建的对象中,并将它返回。

这个例子只能复制简单的JavaBean,假定JavaBean的每个属性都有public类型的getXXX()和setXXX()方法。

例程10-2 ReflectTester.java

import java.lang.reflect.*;

public class ReflectTester {

 public Object copy(Object object) throws Exception{

//获得对象的类型

  Class classType=object.getClass();

  System.out.println("Class:"+classType.getName());

  //通过默认构造方法创建一个新的对象
  //new Class[]{} 构造函数参数类型
  Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{}); 
  //获得对象的所有属性 Field fields[]=classType.getDeclaredFields(); 
  for(int i=0; i<fields.length;i++){ 
    Field field=fields[i]; 
    String fieldName=field.getName(); 
    String firstLetter=fieldName.substring(0,1).toUpperCase(); 
    //获得和属性对应的getXXX()方法的名字 
    String getMethodName="get"+firstLetter+fieldName.substring(1); 
    //获得和属性对应的setXXX()方法的名字 
    String setMethodName="set"+firstLetter+fieldName.substring(1); 
    //获得和属性对应的getXXX()方法 
    Method getMethod=classType.getMethod(getMethodName,new Class[]{}); 
   //获得和属性对应的setXXX()方法 
   Method setMethod=classType.getMethod(setMethodName,new Class[]{field.getType()}); 
   //调用原对象的getXXX()方法 Object value=getMethod.invoke(object,new Object[]{}); 
   System.out.println(fieldName+":"+value); 
   //调用复制对象的setXXX()方法 
   setMethod.invoke(objectCopy,new Object[]{value}); } 
   return objectCopy; 
 } 
  public static void main(String[] args) throws Exception{ 
    Customer customer=new Customer("Tom",21); 
    customer.setId(new Long(1)); 
    Customer customerCopy=(Customer)new ReflectTester().copy(customer); 
    System.out.println("Copy information:"+customerCopy.getName()+""+customerCopy.getAge()); 
 }
}
class Customer{ 
  //Customer类是一个JavaBean 
  private Long id; 
  private String name; 
  private int age; 
  public Customer(){} 
  public Customer(String name,int age){ this.name=name; this.age=age; } 
  public Long getId(){return id;} 
  public void setId(Long id){this.id=id;} 
  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;}

}
 

以上代码假定每个属性都有相应的getXXX()和setXXX()方法,并且在方法名中,“get”和“set”的后面一个字母为大写。例如,Customer类的name属性对应getName()和setName()方法。Method类的invoke(Object obj,Object args[])方法用于动态执行一个对象的特定方法,它的第一个obj参数指定具有该方法的对象,第二个args参数指定向该方法传递的参数。

在java.lang.Object 类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。Class类是Reflection API 中的核心类,它有以下方法getName():获得类的完整名字。getFields():获得类的public类型的属性。getDeclaredFields():获得类的所有属性。getMethods():获得类的public类型的方法。getDeclaredMethods():获得类的所有方法。

getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。getConstructors():获得类的public类型的构造方法。getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

通过默认构造方法创建一个新对象:Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});以上代码先调用Class类的getConstructor()方法获得一个Constructor 对象,它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。

获得对象的所有属性:Field fields[]=classType.getDeclaredFields();Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性

如例程10-3所示的InvokeTester类的main()方法中,运用反射机制调用一个InvokeTester对象的add()和echo()方法

例程10-3 InvokeTester.java

import java.lang.reflect.*;

public class InvokeTester {

public int add(int param1,int param2){

  return param1+param2;

}

public String echo(String msg){

  return "echo:"+msg;

}

 public static void main(String[] args) throws Exception{

  Class classType=InvokeTester.class;

  Object invokeTester=classType.newInstance();

  //调用InvokeTester对象的add()方法

  Method addMethod=classType.getMethod("add",new Class[]{<strong>int.class,int.class</strong>});

  Object result=addMethod.invoke(invokeTester,new Object[]{new Integer(100),new Integer(200)});

  System.out.println((Integer)result);

  //调用InvokeTester对象的echo()方法

  Method echoMethod=classType.getMethod("echo",new Class[]{String.class});

  result=echoMethod.invoke(invokeTester,new Object[]{"Hello"});

  System.out.println((String)result);

 }

}

add()方法的两个参数为int类型,获得表示add()方法的Method对象的代码如下:

Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});

Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回。

本例中,尽管InvokeTester类的add()方法的两个参数及返回值都是int类型,调用addMethod对象的invoke()方法时,只能传递Integer类型的参数,并且invoke()方法的返回类型也是Integer类型,Integer类是int基本类型的包装类:

Object result=addMethod.invoke(invokeTester,new Object[]{new Integer(100),new Integer(200)});

System.out.println((Integer)result); //result为Integer类型


java.lang.Array类提供了动态创建和访问数组元素的各种静态方法。

如例程10-4所示的ArrayTester1类的main()方法创建了一个长度为10的字符串数组,接着把索引位置为5的元素设为“hello”,然后再读取索引位置为5的元素的值。

例程10-4 ArrayTester1.java

import java.lang.reflect.*;

public class ArrayTester1 {

 public static void main(String args[])throws Exception {

  Class classType = Class.forName("java.lang.String");

  //创建一个长度为10的字符串数组
 
  Object array = Array.newInstance(classType, 10);

  //把索引位置为5的元素设为"hello"

  Array.set(array, 5, "hello");

  //读取索引位置为5的元素的值

  String s = (String) Array.get(array, 5);

  System.out.println(s);

 }

}
如例程10-5所示的ArrayTester2类的main()方法创建了一个5×10×15的整型数组,并把索引位置为[3][5][10]的元素的值为设37。
例程10-5 ArrayTester2.java

import java.lang.reflect.*;

public class ArrayTester2{

public static void main(String args[]) {

  int dims[] = new int[]{5, 10, 15};
  //生成三维数组
  Object array = Array.newInstance(Integer.TYPE, dims);

  //使arrayObj引用array[3]

  Object arrayObj = Array.get(array, 3);//第一维下标为3的
  <pre name="code" class="java">  //arrayObj为两维数组 
  Class cls = arrayObj.getClass().getComponentType();

  System.out.println(cls);

  //使arrayObj引用array[3][5]

  arrayObj = Array.get(arrayObj, 5);//一维数组

  //把元素array[3][5][10]设为37

  Array.setInt(arrayObj, 10, 37);//第十个元素设置为37

  int arrayCast[][][] = (int[][][]) array;

  System.out.println(arrayCast[3][5][10]);

 }

}

扩展:从Object层次来说,没有重写euqals的情况下,“==”和equals是相同的,“==”不管什么情况,都是比较的内存地址,在object中,equals方法调用的“==”,this==obj

Class是Reflection起源。针对任何您想探勘的class,唯有先为它产生一个Class object,接下来才能经由后者唤起为数十多个的Reflection APIs

Java允许我们从多种途径为一个class生成对应的Class object

技术分享技术分享

技术分享

欲生成对象实体,在Reflection 动态机制中有两种作法,一个针对“无自变量构造方法”,一个针对“带参数构造方法”。如果欲调用的是“带参数ctor“就比较麻烦些,不再调用Class的newInstance(),而是调用Constructor 的newInstance()。首先准备一个Class[]做为ctor的参数类型(本例指定为一个double和一个int),然后以此为自变量调用getConstructor(),获得一个专属ctor。接下来再准备一个Object[] 做为ctor实参值(本例指定3.14159和125),调用上述专属ctor的newInstance()。技术分享

动态生成“Class object 所对应之class”的对象实体;无自变量。

技术分享

运行时调用methods

这个动作和上述调用“带参数之ctor”相当类似。首先准备一个Class[]做为参数类型(本例指定其中一个是String,另一个是Hashtable),然后以此为自变量调用getMethod(),获得特定的Method object。接下来准备一个Object[]放置自变量,然后调用上述所得之特定Method object的invoke()。为什么获得Method object时不需指定回返类型?

因为method overloading机制要求signature必须唯一,而回返类型并非signature的一个成份。换句话说,只要指定了method名称和参数列,就一定指出了一个独一无二的method。

技术分享

运行时变更fields内容

与先前两个动作相比,“变更field内容”轻松多了,因为它不需要参数和自变量。首先调用Class的getField()并指定field名称。获得特定的Field object之后便可直接调用Field的get()和set(),

技术分享

代理模式

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

代理模式一般涉及到的角色有 抽象角色:声明真实对象和代理对象的共同接口代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

1、Subject.java

abstract public class Subject{
    abstract public void request();
}

2、RealSubject.java
public class RealSubject extends Subject{

    public RealSubject()
    {
    }

    public void request()
    {
        System.out.println("From real subject.");
    }

}
3、ProxySubject

public class ProxySubject extends Subject

{
    private RealSubject realSubject; // 以真实角色作为代理角色的属性

    public ProxySubject()
    {
    }

    public void request() // 该方法封装了真实对象的request方法

    {
        preRequest();

        if (realSubject == null)
        {
            realSubject = new RealSubject();
        }

        realSubject.request(); // 此处执行真实对象的request方法

        postRequest();
    }

    private void preRequest()
    {

        // something you want to do before requesting

    }

    private void postRequest()
    {

        // something you want to do after requesting

    }

}

<pre name="code" class="java">Client.java

public class Client
{
    public static void main(String[] args)
    {
        Subject sub = new ProxySubject();

        sub.request();
    }




由以上代码可以看出,客户实际需要调用的是RealSubject类的request()方法,现在用ProxySubject来代理 RealSubject类,同样达到目的,同时还封装了其他方法(preRequest(),postRequest()),可以处理一些其他问题。 另外,如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个 代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决

动态代理类

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类: (1)Interface InvocationHandler:该接口中仅定义了一个方法public object invoke(Object obj,Method method, Object[] args)在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。 这个抽象方法在代理类中动态实现。(2)Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容

protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。 static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)

所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作

在使用动态代理类时,我们必须实现InvocationHandler接口

public interface Subject
{
    abstract public void request();
}

//具体角色

public class RealSubject implements Subject
{

    public RealSubject()
    {
    }

    public void request()
    {
        System.out.println("From real subject.");
    }

}

//代理处理器 

/**
 * 该代理类的内部属性为Object类,实际使用时通过该类的构造函数DynamicSubject(Object obj)对其赋值;
 * 此外,在该类还实现了invoke方法,该方法中的 method.invoke(sub,args);
 * 其实就是调用被代理对象的将要被执行的方法,方法参数sub是实际的被代理对象,
 * args为执行被代理对象相应操作所需的参数。
 * 通过动态代理类,我们可以在调用之前或之后执行一些相关操作
 */

public class DynamicSubject implements InvocationHandler
{
    private Object sub;

    public DynamicSubject()
    {
    }

    public DynamicSubject(Object obj)
    {
        sub = obj;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        System.out.println("before calling " + method);

        method.invoke(sub, args);

        System.out.println("after calling " + method);

        return null;
    }

}

public class Client
{
    static public void main(String[] args) throws Throwable
    {

        RealSubject rs = new RealSubject(); // 在这里指定被代理类
        InvocationHandler ds = new DynamicSubject(rs);
        Class<?> cls = rs.getClass();

        // 以下是一次性生成代理

        Subject subject = (Subject) Proxy.newProxyInstance(
                cls.getClassLoader(), cls.getInterfaces(), ds);

        subject.request();
    }
}
getClassLoader,类加载器,把类加载到内存中


通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理
4.通过代理调用方法


例子

public interface Foo
{
    void doAction();
}

public class FooImpl implements Foo
{
    public FooImpl()
    {
    }

    public void doAction()
    {
        System.out.println("in FooImp1.doAction()");
    }
}

public class FooImpl2 implements Foo
{
    public FooImpl2()
    {
    }

    public void doAction()
    {
        System.out.println("in FooImp2.doAction()");
    }

} 

public class CommonInvocationHandler implements InvocationHandler
{

    // 动态执行对象,需要回调的对象
    private Object target;

    // 支持构造子注射
    public CommonInvocationHandler()
    {

    }

    // 支持构造子注射
    public CommonInvocationHandler(Object target)
    {
        setTarget(target);
    }

    /**
     * 
     * 采用setter方法注射
     * 
     * @param target
     * 
     */
    public void setTarget(Object target)
    {
        this.target = target;
    }

    /**
     * 
     * 调用proxy中指定的方法method,并传入参数列表args
     * 
     * @param proxy
     *            代理类的类型,例如定义对应method的代理接口
     * 
     * @param method
     *            被代理的方法
     * 
     * @param args
     *            调用被代理方法的参数
     * 
     * @return
     * 
     * @throws java.lang.Throwable
     * 
     */

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        return method.invoke(target, args);
    }

}

public class Demo
{
    public static void main(String[] args)
    {

        // 1.通用的动态代理实现

        CommonInvocationHandler handler = new CommonInvocationHandler();

        Foo f;

        // 2.接口实现1

        handler.setTarget(new FooImpl());

        // 方法参数说明:代理类、代理类实现的接口列表、代理类的处理器

        // 关联代理类、代理类中接口方法、处理器,当代理类中接口方法被调用时,会自动分发到处理器的invoke方法

        // 如果代理类没有实现指定接口列表,会抛出非法参数异常

        f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),

        new Class[] { Foo.class },

        handler);

        f.doAction();

        // 3.接口实现2

        handler.setTarget(new FooImpl2());

        f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),

        new Class[] { Foo.class },

        handler);

        f.doAction();
    }
}












JAVA反射机制与动态代理

标签:java   invoke   classloader   class   

原文地址:http://blog.csdn.net/liuganggao/article/details/44133433

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!