java-动态代理
代理模式
代理模式是Java中常用的设计模式,主要由公共接口、被代理类和代理类等三部分组成,代理类持有被代理类的实类,代为执行具体的类方法。其中代理类与被代理类有同样的接口。
代理类与被代理类之间通常会存在关联关系,一个代理类的对象与一个被代理类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用被代理类对象的方法来提供特定的服务。
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起中介的作用。
代理模式UML
- Subject:抽象主题角色,抽象主题类可以是抽象类,也可以是接口,是一个最普通的业务类型定义,无特殊要求。
- RealSubject:具体主题角色,也叫被委托对象,被代理角色。是业务逻辑的具体执行者
- Proxy:代理主题角色,也叫委托类、代理类。它把所有抽象主题类定义的方法给具体主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后工作。
代理模式
根据加载被代理类的时机不同,将代理分为静态代理和动态代理。
静态代理,程序运行之前就已经存在的编译好的代理类
动态代理,程序运行时动态生成,根据java的反射机制动态生成
静态代理
静态代理:在代码编译时就确定了具体的被代理类。由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
代码案例
//IMacSeller.java
public interface IMacSeller{
void buy()
}
//USAMacSeller.java
public class USAMacSeller implements IMacSeller{
@Override
public void buy(){
System.out.println("buy a macbook from USA")
}
}
//HongkongMacSeller.java
public class HongkongMacSeller implements IMacSeller{
IMacSeller seller =new USAMacSeller();
@Override
public void buy(){
seller.buy();
System.out.println("buy a macbook from Hongkong");
}
}
//Client.java
public class Client{
public static void main(String[] args){
System.out.println("Proxy main");
IMacSeller seller = new HongkongMacSeller();
seller.buy();
}
}
JAVA动态代理
在java中,通常使用Proxy(动态代理)来实现对代理模式的支持
在代码编译时未确定具体的被代理类,而在代码运行时动态加载被代理的类。在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。在程序运行过程中产生的这个对象,其实就是通过反射机制来生成的一个代理。
java中要创建一个代理对象,必须调用Proxy类的静态方法newProInstance,该方法原型如下:
Object Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler) throws IllegalArgumentException
//loader,表示类加载器,对于不同来源(系统库或网络等)的类需要不同的类加载器来加载,这是java安全模型的一部分。
//interfaces,它就是被代理类对象的共有的接口
//handler,表示调用处理器,它必须是实现了InvocationHandler接口的对象,其作用是定义代理对象中需要执行的具体操作
InvocationHandler接口只有一个方法invoke,定义了代理对象在执行真实对象的方法时所希望执行的动作
Object invoke(Object proxy,Method method,Object[] args) throws Throwable
// proxy,表示执行这个方法的代理对象,相当于代理类的实例
// method,表示真实对象实际需要执行的方法
// args,表示实际对象实际执行方法时所需的参数
动态代理代码案例
1.定义接口类 ImiSeller
package com.tyut.proxy;
public interface ImiSeller {
void buy();
}
2.定义接口实现类 MiStore,并重写了buy()方法
package com.tyut.proxy;
public class MiStore implements ImiSeller {
@Override
public void buy() {
System.out.println("我们买了一部小米手机");
}
}
3.实现InvocationHandler接口,里面invoke定义了代理类需要实现的方法
package com.tyut.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyInvocationHandler implements InvocationHandler {
Object proxyTarget;
public ProxyInvocationHandler(Object proxyTarget){
this.proxyTarget = proxyTarget;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("瞧一瞧,看一看!!!");
if(method.getName().equals("buy")){
try {
method.invoke(this.proxyTarget, args);
}catch (Exception e){
return null;
}
}
System.out.println("要不继续看看???");
return null;
}
}
4.创建并运行动态代理
package com.tyut.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
ImiSeller obj = new MiStore(); //创建了MiStore类的实例
InvocationHandler invocationHandler = new ProxyInvocationHandler(obj);//实现了InvocationHandler方法,并将被代理的实例对象传入
// 动态生成代理类
ImiSeller proxy = (ImiSeller) Proxy.newProxyInstance(
MiStore.class.getClassLoader(),// 指定动态代理类的类加载器
new Class[]{ImiSeller.class},// 定义动态代理类 实现的接口
invocationHandler);// 动态代理处理类
// 使用生成的代理类,调用invoke中定义的方法
proxy.buy();
}
}
动态代理主要方法说明
创建动态代理类会使用到java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
java.lang.reflect.Proxy
java.lang.reflect.Proxy主要用于生成动态代理类Class、创建代理类实例,该类实现了java.io.Serializable接口。
java.lang.reflect.Proxy类主要方法如下:
package java.lang.reflect;
import java.lang.reflect.InvocationHandler;
/**
* Creator: yz
* Date: 2020/1/15
*/
public class Proxy implements java.io.Serializable {
// 省去成员变量和部分类方法...
/**
* 获取动态代理处理类对象
*
* @param proxy 返回调用处理程序的代理实例
* @return 代理实例的调用处理程序
* @throws IllegalArgumentException 如果参数不是一个代理实例
*/
public static InvocationHandler getInvocationHandler(Object proxy)
throws IllegalArgumentException {
...
}
/**
* 创建动态代理类实例
*
* @param loader 指定动态代理类的类加载器
* @param interfaces 指定动态代理类的类需要实现的接口数组
* @param h 动态代理处理类
* @return 返回动态代理生成的代理类实例
* @throws IllegalArgumentException 不正确的参数异常
*/
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException {
...
}
/**
* 创建动态代理类
*
* @param loader 定义代理类的类加载器
* @param interfaces 代理类要实现的接口列表
* @return 用指定的类加载器定义的代理类,它可以实现指定的接口
*/
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) {
...
}
/**
* 检测某个类是否是动态代理类
*
* @param cl 要测试的类
* @return 如该类为代理类,则为 true,否则为 false
*/
public static boolean isProxyClass(Class<?> cl) {
return java.lang.reflect.Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
}
/**
* 向指定的类加载器中定义一个类对象
*
* @param loader 类加载器
* @param name 类名
* @param b 类字节码
* @param off 截取开始位置
* @param len 截取长度
* @return JVM创建的类Class对象
*/
private static native Class defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
}
java.lang.reflect.InvocationHandler
java.lang.reflect.InvocationHandler接口用于调用Proxy类生成的代理类方法,该类只有一个invoke方法。
java.lang.reflect.InvocationHandler接口代码(注释直接搬的JDK6中文版文档):
package java.lang.reflect;
import java.lang.reflect.Method;
/**
* 每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并
* 将其指派到它的调用处理程序的 invoke 方法。
*/
public interface InvocationHandler {
/**
* 在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
*
* @param proxy 在其上调用方法的代理实例
* @param method 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明
* 方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
* @param args 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,
* 则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer
* 或 java.lang.Boolean)的实例中。
* @return 从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,
* 则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。
* 如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出
* NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,
* 则代理实例上的方法调用将抛出 ClassCastException。
* @throws Throwable 从代理实例上的方法调用抛出的异常。该异常的类型必须可以分配到在接口方法的
* throws 子句中声明的任一异常类型或未经检查的异常类型 java.lang.RuntimeException 或
* java.lang.Error。如果此方法抛出经过检查的异常,该异常不可分配到在接口方法的 throws 子句中
* 声明的任一异常类型,代理实例的方法调用将抛出包含此方法曾抛出的异常的
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
使用java.lang.reflect.Proxy动态创建类对象
ClassLoader和Unsafe都有一个叫做defineClassXXX的native方法,我们可以通过调用这个native方法动态的向JVM创建一个类对象,而java.lang.reflect.Proxy类恰好也有这么一个native方法,所以我们也将可以通过调用java.lang.reflect.Proxy类defineClass0方法实现动态创建类对象。
package com.anbai.sec.proxy;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import static com.anbai.sec.classloader.TestClassLoader.TEST_CLASS_BYTES;
import static com.anbai.sec.classloader.TestClassLoader.TEST_CLASS_NAME;
/**
* Creator: yz
* Date: 2020/1/15
*/
public class ProxyDefineClassTest {
public static void main(String[] args) {
// 获取系统的类加载器,可以根据具体情况换成一个存在的类加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
try {
// 反射java.lang.reflect.Proxy类获取其中的defineClass0方法
Method method = Proxy.class.getDeclaredMethod("defineClass0", new Class[]{
ClassLoader.class, String.class, byte[].class, int.class, int.class
});
// 修改方法的访问权限
method.setAccessible(true);
// 反射调用java.lang.reflect.Proxy.defineClass0()方法,动态向JVM注册
// com.anbai.sec.classloader.TestHelloWorld类对象
Class helloWorldClass = (Class) method.invoke(null, new Object[]{
classLoader, TEST_CLASS_NAME, TEST_CLASS_BYTES, 0, TEST_CLASS_BYTES.length
});
// 输出TestHelloWorld类对象
System.out.println(helloWorldClass);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//运行接口
class com.anbai.sec.classloader.TestHelloWorld
创建代理类实例
我们可以使用Proxy.newProxyInstance来创建动态代理类实例,或者使用Proxy.getProxyClass()获取代理类对象再反射的方式来创建,下面我们以com.anbai.sec.proxy.FileSystem接口为例,演示如何创建其动态代理类实例。
Proxy.newProxyInstance示例代码
// 创建UnixFileSystem类实例
FileSystem fileSystem = new UnixFileSystem();
// 使用JDK动态代理生成FileSystem动态代理类实例
FileSystem proxyInstance = (FileSystem) Proxy.newProxyInstance(
FileSystem.class.getClassLoader(),// 指定动态代理类的类加载器
new Class[]{FileSystem.class}, // 定义动态代理生成的类实现的接口
new JDKInvocationHandler(fileSystem)// 动态代理处理类
);
我们可以看下 Proxy.newProxyInstance具体流程
1、进入Proxy类,调用其静态方法Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h),该方法回调用getProxyClass0(loader,intfs)方法,该方法为获取代理类对象,跟进该方法
2、getProxyClass0 方法返回了proxyClassCache.get()方法
proxyClassCache中定义了 new ProxyClassFactory()对象
继续跟进ProxyClassFactory 方法,最后发现使用了defineClass0 向JVM注册了该代理方法

Proxy.getProxyClass反射示例代码
// 创建UnixFileSystem类实例
FileSystem fileSystem = new UnixFileSystem();
// 创建动态代理处理类
InvocationHandler handler = new JDKInvocationHandler(fileSystem);
// 通过指定类加载器、类实现的接口数组生成一个动态代理类
Class proxyClass = Proxy.getProxyClass(
FileSystem.class.getClassLoader(),// 指定动态代理类的类加载器
new Class[]{FileSystem.class}// 定义动态代理生成的类实现的接口
);
// 使用反射获取Proxy类构造器并创建动态代理类实例
FileSystem proxyInstance = (FileSystem) proxyClass.getConstructor(
new Class[]{InvocationHandler.class}).newInstance(new Object[]{handler}
);
通过 Proxy.getProxyClass返回一个代理类
使用反射获取Proxy类构造器并创建动态代理类实例,通过反射先获取构造方法,在利用该方法创建代理类实例