动态代理(proxy)类和类加载器
作者:zhrb
学习该技术之前,需要对反射Method对象的使用有一定的了解。
可以在运行时创建一个实现了一组给定接口的新类(代理类)。不过该代理类和Hibernate中使用的代理类使用的是不一样的技术。
该代理类具有下列方法:
指定接口所需要的全部方法
Object类中的全部方法
但是该代理类不能定义其所实现接口的方法,即,不能编写实现该方法的代码。所以需要invocation handler,该invocation handler实现了InvocationHandler接口的类对象,该接口中方法的定义:
Object
invoke(Object proxy, Method method, Object[] args)
在代理实例上处理方法调用并返回结果。
在代理实例上处理方法调用并返回结果。
那么当我们调用新生成的代理对象的方法时,invocation handler的invoke方法就会被调用。从而间接达到了为代理类增加自己代码的目的。
以下面这个例子为例,我们希望实现Comparable接口的对象的compareTo方法被调用时执行一些额外的代码,如打印被调用的方法(compareTo)名称、调用该方法传递进来的参数。我们可以使用代理类实现。具体代码详见《java核心技术 卷1:基础知识》接口与内部类这章的代理小节中的例6-7, ProxyTest.java代码。
为了做实验我们编写自己的代码进行操作,有如下接口与类:
public interface ShapeInterface {
public double getArea();
}
Cricle、Square、Triangle类实现了ShapeInterface接口。
我们希望实现如下功能,当ShapeInterface的实现类的getArea()方法被调用的时候,打印出被代理对象的类型、该代理对象中被调用的方法名、参数个数、参数类型、参数值,如果没有参数传递进来则打印“该方法没有参数”,最后执行getArea()方法本身。这些功能可以算是为代理类新增的代码,但我们不能直接在代理类中编写代码,我们需要为ShapeInterface接口定义一个invocation handler,即下面的ShapeProxyHandler,见如下代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ShapeProxyHandler implements InvocationHandler {
public ShapeProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.print("你所要代理的对象类型是"+target.getClass().getName());
System.out.println(",要执行"+method.getName()+"方法");
if (args!=null){
System.out.print("该方法有"+args.length+"个参数,分别是");
for(Object e:args){
System.out.print(e.getClass().getName());
System.out.print(","+e);
}
}
else{
System.out.println("该方法没有参数");
}
return method.invoke(target, args);
}
private Object target;//存放要代理的对象
}
该invocation handler编写好以后,编写如下测试代码进行测试:
import java.lang.reflect.Proxy;
import proxy.ShapeInterface;
public class ProxyTestShape {
public static void main(String[] args) {
Object[] elements = new Object[3];//用于存放代理对象的数组
ShapeProxyHandler proxyHandler1 = new ShapeProxyHandler(new Circle());
Object proxy1 = Proxy.newProxyInstance(ShapeInterface.class.getClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler1);
elements[0] = proxy1;
ShapeProxyHandler proxyHandler2 = new ShapeProxyHandler(new Square(10));
Object proxy2 = Proxy.newProxyInstance(ShapeInterface.class.getClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler2);
elements[1] = proxy2;
ShapeProxyHandler proxyHandler3 = new ShapeProxyHandler(new Triangle(10, 20, 30));
Object proxy3 = Proxy.newProxyInstance(ShapeInterface.class.getClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler3);
elements[2] = proxy3;
invokeGetArea(elements);
}
public static void invokeGetArea(Object[] elements){
for(Object e:elements){
System.out.println("返回结果是="+((ShapeInterface)e).getArea());
System.out.println("===============END=============");
}
}
}
其中下面这句代码:
Object proxy1 = Proxy.newProxyInstance(ShapeInterface.class.getClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler1);
使用了Proxy类的newProxyInstance方法。
我们先来看一下该方法第一个参数:
类加载器(class loader),ShapeInterface.class.getClassLoader()就是ShapeInterface这个类型的类加载器。该处代码和java核心技术的参考代码又不一样,核心技术里面该处的参数为null,主要是因为核心技术里面实现的是Comparable接口,而该接口是被Extension类加载器。Extension类加载器用于从jre/lib/ext目录加载,而Comparable作为java.lang包中的接口正好位于jre/lib/ext目录。但我们自定义的接口ShapeInterface却不能被Extension类加载器加载而是被System类加载器加载,所以此处的代码不能像java核心技术中的那样设置为null。每个线程都有一个对类加载器的引用,称为上下文类加载器。主线程的上下文类加载器是System类加载器。在这里main方法所在的主线程的就使用了System类加载器。也就是说主线程的上下文类加载器和自定义接口的ShapeInterface类加载器刚好一样,都是System类加载器。那么上面这段代码可以改成如下这段代码:
Object proxy1 = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] { ShapeInterface.class } , proxyHandler1);
其中Thread.currentThread().getContextClassLoader()这句代码就是获得当前线程(主线程)的上下文类加载器。
我们再来看一下该方法第二个参数:
这个参数new Class[] { ShapeInterface.class }是一个数组,意味着我们实际上生成的代理类可以实现多个接口。
该方法的第三个参数:
proxyHandler1就是我们实现的invocation handler,也就是在这个invocation handler中我们编写了想要为代理类增加的代码。
使用代理类还应注意,我们只能生成接口的代理类而不能生成某个类的代理类。
代理类的其他案例:
实际开发中可能会遇到新旧版本的匹配问题,如旧版本接口的和新版本的接口不一样,但是希望新旧版本都可使用,也可以使用动态代理类实现。具体请参考附录的参考资料2.
代理技术为Java提供了极大的动态性可以使我们的编程更加灵活,所以有兴趣的话大家可以了解一下动态代理类。
参考代码见附件
参考资料:
1. JAVA核心技术 卷1:基础知识--接口与内部类--代理小节
2. JAVA核心技术 卷2:高级特性--安全--类加载器小节
3.深入理解Java 7:核心技术与最佳实践 成富 著
分享到:
相关推荐
- `ClassLoader loader`:用于加载代理类的类加载器,通常传入目标接口的类加载器。 - `Class[] interfaces`:代理对象需要实现的接口列表。 - `InvocationHandler handler`:处理代理对象方法调用的`...
这个过程涉及到反射API和类加载器的工作原理,对于深入理解Java虚拟机的运行机制非常有帮助。 6. **工具应用**: 在实际开发中,Spring框架的AOP模块就大量使用了Java的动态代理技术。通过配置或注解,Spring能够...
### Java 动态代理Proxy应用和底层源码分析 #### 一、Java动态代理简介 Java动态代理是一种在运行时动态生成代理类的技术,通过该技术可以为一个或多个接口生成一个实现类,该实现类可以拦截接口方法的调用,并...
- `ClassLoader`:用于加载生成的代理类的类加载器,通常是目标接口的类加载器。 - `Interface[]`:表示代理类需要实现的接口列表。 - `InvocationHandler`:实现了`java.lang.reflect.InvocationHandler`接口的...
### JAVA类加载机制与动态代理 #### 一、类加载机制 ...类加载机制确保了类能够正确地被加载到虚拟机中并准备好供程序使用,而动态代理则提供了灵活的方式来增强类的功能,提高代码的可维护性和扩展性。
`bind()`方法用于创建代理对象,`Proxy.newProxyInstance()`是关键,它接收三个参数:目标对象的类加载器、目标对象实现的接口列表以及当前的`InvocationHandler`实例。 最后,我们通过测试类`TestProxy`来演示动态...
使用`Proxy.newProxyInstance`方法,传入类加载器、接口列表和自定义的`InvocationHandler`。 - **步骤5**:通过代理对象调用方法。代理对象将调用转发给`InvocationHandler`的`invoke`方法。 下面是一个简单的...
在Java编程语言中,反射(Reflection)和动态代理(Dynamic Proxy)是两个强大的特性,它们为程序员提供了在运行时检查和操作类、接口、对象的能力。这篇内容将深入讲解这两个概念,帮助初学者理解并掌握它们的应用...
- 生成代理对象:使用`Proxy.newProxyInstance()`方法,传入接口类型、InvocationHandler实例以及目标对象的类加载器,生成代理对象。 - 调用代理方法:通过代理对象调用接口方法,实际会触发`InvocationHandler`...
ClassLoader表示类加载器,Class[] interfaces表示要代理的接口,InvocationHandler是 InvocationHandler 对象。 InvocationHandler是一个接口,它是代理对象的核心组件。每个代理对象都有一个与之关联的 ...
- 使用`Proxy.newProxyInstance()`创建代理对象,传入`MyService`的类加载器、接口数组和`MyInvocationHandler`实例。 - 最后,可以通过代理对象调用`MyService`接口的方法,实际执行的是`MyInvocationHandler`中...
FoxyProxy 是一款高级代理服务器管理工具,是 Firefox 火狐浏览器的代理插件,相比比 SwitchProxy、ProxyButton、QuickProxy、xyzproxy、ProxyTex 等扩展提供更多的功能。 FoxyProxy 通过使用通配符、正则表达式和...
- 创建代理对象需要三个参数:类加载器、目标接口数组和InvocationHandler实例。 二、CGLIB代理 CGLIB(Code Generation Library)是另一个常用的Java代理库,它不需要目标类实现任何接口,而是通过继承的方式创建...
- 最后,使用Proxy.newProxyInstance()创建代理对象,传入接口类型、类加载器和你的InvocationHandler实现。 例如,我们可以定义一个名为`Service`的接口,包含一些业务方法,然后创建一个实现`InvocationHandler`...
使用`java.lang.reflect.Proxy`类动态生成代理类二进制字节流的技术称为**动态代理**(B)。这种技术常用于实现AOP(面向切面编程)等功能。 #### 二、判断题知识点详解 1. **Java虚拟机提供的类加载器必须遵循...
Proxy 类的 newProxyInstance 方法是生成代理对象的入口,该方法需要三个参数:类加载器、被代理对象的接口和 InvocationHandler 接口的实现类。 动态代理的应用场景非常广泛,例如在 AOP(Aspect-Oriented ...
3. 创建代理类:使用`Proxy.newProxyInstance()`生成代理类的实例,传入类加载器、接口数组和自定义的`InvocationHandler`实例。 4. 使用代理对象:通过代理对象调用方法,实际上会触发`InvocationHandler`的`invoke...
该方法接受三个参数:类加载器、代理类需要实现的接口数组以及`InvocationHandler`实例。 ```java ClassLoader loader = TargetClass.class.getClassLoader(); Class[] interfaces = {IDAO.class}; ...
2. **Proxy类**:用于创建动态代理对象,通过`newProxyInstance()`静态方法,传入类加载器、接口数组和InvocationHandler实例来生成代理对象。 3. **实现过程**:首先,你需要定义一个接口,然后创建实现该接口的...
这个方法需要三个参数:代理类的类加载器、代理类需要实现的接口列表以及我们的`InvocationHandler`实例: ```java MyService realService = new RealService(); // 实现MyService接口的类 MyInvocationHandler ...