Java笔记 - 反射 动态代理

20131227-java001

反射:运行时的类信息

如果不知道某个对象的确切类型,RTTI可以告诉你,但是前提是在编译时必须已知,这样才能使用RTTI识别它,并利用这些信息做一些有用的事情。也就是在编译时,编译器必须知道所有要通过RTTI来处理的类。

考虑这种情况:假设你获取了一个指向某个并不在你程序空间中的对象的引用,这个时候,在编译时,程序就无法获取到这个对象所属的类。

这个时候反射就登场啦。反射提供了一种机制,用来检查可用的方法,并返回方法名。另外,在这种情况下我们也会需要在运行时获取类的信息:希望提供在跨网络的远程平台上创建和运行对象的能力。

反射机制的支持类:Class类与java.lang.reflect类库一起对反射的概念进行了支持,这个类库包含了Field,Method以及Constructor类(每个类都实现了Member接口)。这些类都是JVM运行时创建的,用于表示未知类里对应的成员。

使用Constructor创建新对象,用get()和set()方法读取和修改与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFields(),getMethods()和getConstructors()等方法,返回表示字段,方法以及构造器的对象数组。

这里推荐几篇反射的文章:

反射机制的介绍和基本的API的使用

通过反射机制动态创建和访问数组

通过反射机制修改类中的私有属性的值

通过反射机制动态获取属性的值模拟Struts的自动赋值

RTTI与反射的区别:

RTTI:编译器在编译时打开和检查.class文件,也就是说我们可以用普通的方式调用对象的所有方法。

反射机制:.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件的。

反射机制的更多用途:通常你是不需要使用反射的,但是它们在你需要创建更加动态的代码时会很有用。反射机制用来支持其他的特性如对象序列化,JavaBean。

动态代理:

代理是基本的设计模式之一。关于代理模式,这里有一篇文章:

设计模式笔记 – Proxy 代理模式 (Design Pattern)

Java的动态代理比代理的思想更向前迈进了一步,因为它可以动态的创建代理并动态的处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的策略。

下面来看一个Java中实现动态代理的例子:

首先创建抽象角色:

public interface Subject {
void doSomething();
void somethingElse(String arg);
}

接下来创建具体角色:

public class RealSubject implements Subject{

@Override
public void doSomething() {
    System.out.println("doSomething");
}

@Override
public void somethingElse(String arg) {
    System.out.println("somethingElse: " + arg);
}

}

接下来是创建一个InvocationHandler接口的实现:

public class DynamicProxyHandler implements InvocationHandler{

private Object proxied;

public DynamicProxyHandler(Object proxied){
    this.proxied = proxied;
}

/\*\*
 \* invoke()方法中传递进来了代理对象,以防你需要区分请求的来源
 */
@Override
public Object invoke(Object proxy, Method method, Object\[\] args)
        throws Throwable {
    System.out.println("*** proxy ***" + proxy.getClass());
    System.out.println("*** method ***" + method);
    System.out.println("*** args ***" + args);
    // 可以在这里做一些处理,如查看方法名,查看方法签名的其他方面,甚至可以搜索特定的参数值
    if(method.getName().equals("interesting"))
        System.out.println("Proxy detected the interesting method.");
    // 将请求转发给被代理对象,并传入必须的参数
    return method.invoke(proxied, args);
}

}

最后在main方法中创建动态代理对象,并通过动态代理调用真实角色的方法:

public class SimpleDynamicProxy {

public static void consumer(Subject subject){
    subject.doSomething();
    subject.somethingElse("haha~~");
}

public static void main(String\[\] args){
    // 创建真实的对象
    RealSubject real = new RealSubject();
    consumer(real);
    // 通过调用该方法创建动态代理
    Subject proxy = (Subject)Proxy.newProxyInstance(
            Subject.class.getClassLoader(), // 类加载器
            new Class\[\]{Subject.class}, // 你希望该代理实现的接口列表
            new DynamicProxyHandler(real));  // InvocationHandler接口的一个实现
    consumer(proxy);
}

}

第一个consumer(real)输出为:

doSomething
somethingElse: haha~~

第二个consumer(proxy)输出为:

*** proxy class $Proxy0
\
** method *
public abstract void com.itzhai.javanote.entity.Subject.doSomething()
*** args null
doSomething
\
** proxy *
class $Proxy0
*** method public abstract void com.itzhai.javanote.entity.Subject.somethingElse(java.lang.String)
\
** args *
[Ljava.lang.Object;@1a1c887
somethingElse: haha~~

动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器传递一个真实对象的引用,从而使得调用处理器在执行其中介任务时,可以将请求转发。如上面的创建DynamicProxyHandler对象时就传入了真实的角色。

下面是一篇关于Java动态代理的文章:

Java基础笔记 – 动态代理 Java中动态代理类的介绍和使用

arthinking wechat
欢迎关注itzhai公众号