0%
这是一片思考的空间 -- arthinking
Spring 重构&代码整洁之道 软件设计 JVM 并发编程 数据结构与算法 分布式 存储 网络 微服务 设计模式
Java技术栈 - 涉及Java技术体系

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中动态代理类的介绍和使用

欢迎关注我的其它发布渠道

订阅IT宅
内功修炼
Java技术栈
Java架构杂谈是IT宅精品文章公众号,欢迎订阅:
📄 网络基础知识:两万字长文50+张趣图带你领悟网络编程的内功心法 📄 HTTP发展史:三万长文50+趣图带你领悟web编程的内功心法 📄 HTTP/1.1:可扩展,可靠性,请求应答,无状态,明文传输 📄 HTTP/1.1报文详解:Method,URI,URL,消息头,消息体,状态行 📄 HTTP常用请求头大揭秘 📄 HTTPS:网络安全攻坚战 📄 HTTP/2:网络安全传输的快车道 📄 HTTP/3:让传输效率再一次起飞 📄 高性能网络编程:图解Socket核心内幕以及五大IO模型 📄 高性能网络编程:三分钟短文快速了解信号驱动式IO 📄 高性能网络编程:彻底弄懂IO复用 - IO处理杀手锏,带您深入了解select,poll,epoll 📄 高性能网络编程:异步IO:新时代的IO处理利器 📄 高性能网络编程:网络编程范式 - 高性能服务器就这么回事 📄 高性能网络编程:性能追击 - 万字长文30+图揭秘8大主流服务器程序线程模型
📄 Java内存模型:如果有人给你撕逼Java内存模型,就把这些问题甩给他 📄 一文带你彻底理解同步和锁的本质(干货) 📄 AQS与并发包中锁的通用实现 📄 ReentrantLock介绍与使用 📄 ReentrantReadWriteLock介绍与使用 📄 ReentrantLock的Condition原理解析 📄 如何优雅的中断线程 📄 如何优雅的挂起线程 📄 图解几个好玩的并发辅助工具类 📄 图解BlockingQueue阻塞队列
📄 消息队列那么多,为什么建议深入了解下RabbitMQ? 📄 高并发异步解耦利器:RocketMQ究竟强在哪里? 📄 Kafka必知必会18问:30+图带您看透Kafka
📄 洞悉MySQL底层架构:游走在缓冲与磁盘之间 📄 SQL运行内幕:从执行原理看调优的本质 📄 洞悉Redis技术内幕:缓存,数据结构,并发,集群与算法