一、前言
1.1、什么是代理?
大道理上讲代理是一种软件设计模式,目的地希望能做到代码重用。具体上讲,代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法。这个就好比 A---->B—>C 这种模式。A可以不通过直接与C对话的情况下,而通过B与其产生间接对话。
Java动态代理之所以叫做动态,因为它能避免传统代理模式实现中人工一个一个的将java函数转发过去,而是能够让代码自动做到这一点,这样代理类的代码是和业务无关的,不会因为业务类的方法增多而逐渐庞大。使代码更易维护更易修改,实现自动化搬砖。
1.2、什么情况下使用动态代理?
1、需要对较难修改的类方法进行功能增加。
2、RPC即远程过程调用,通过动态代理的建立一个中间人进行通信。
3、实现切面编程(AOP)可以采用动态代理的机制来实现。
二、静态代理和动态代理
根据加载被代理类的时机不同,将代理分为静态代理和动态代理。编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;运行时才确定被代理的类是哪个,那么可以使用类动态代理。
2.1、静态代理
我们先创建一个接口,Java API代理机制求被代理类必须要实现某个接口,对于静态代理方式 代理类 也要实现和 被代理类 相同的接口;
定义一个接口,这个接口定义了被代理类需要实现的功能:
public interface Subject {
public void sayGoodBye();
public void sayHello(String str);
}
定义被代理类(原来功能类)并实现被代理类的功能逻辑:
public class RealSubject implements Subject {
@Override
public void sayGoodBye() {
System.out.println("RealSubject sayGoodBye");
}
@Override
public void sayHello(String str) {
System.out.println("RealSubject sayHello " + str);
}
}
定义静态代理类(功能增加类),这个代理类也必须要实现和被代理类相同的Subject接口,便于对原有功能的增强:
public class ProxySubject implements Subject {
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public void sayGoodBye() {
//代理类,功能的增强
System.out.println("ProxySubject sayGoodBye begin");
//在代理类的方法中 间接访问被代理对象的方法
subject.sayGoodBye();
System.out.println("ProxySubject sayGoodBye end");
}
@Override
public void sayHello(String str) {
//代理类,功能的增强
System.out.println("ProxySubject sayHello begin");
//在代理类的方法中 间接访问被代理对象的方法
subject.sayHello(str);
System.out.println("ProxySubject sayHello end");
}
}
客户端代码:
public static void main(String[] args) {
//被代理的对象,某些情况下 我们不希望修改已有的代码,我们采用代理来间接访问
RealSubject realSubject = new RealSubject();
//代理类对象
ProxySubject proxySubject = new ProxySubject(realSubject);
//调用代理类对象的方法
proxySubject.sayGoodBye();
System.out.println("******");
proxySubject.sayHello("Test");
}
测试代码输出:
ProxySubject sayGoodBye begin
RealSubject sayGoodBye
ProxySubject sayGoodBye end
******
ProxySubject sayHello begin
RealSubject sayHello Test
ProxySubject sayHello end
静态代理看起来是比较简单的,它是运用原理跟装饰设计模式类似,Subject接口相当于一个抽象构建Component ,被代理类RealSubject相当于一个具体构建ConcreteComponent,而代理类ProxySubject则相当于装饰角色(Decorator)和具体装饰角色(ConcreteDecorator)的结合。不管是静态代理和装饰设计模式其实都是为了对原有功能的增强 ,屏蔽或改变。
静态代理(传统代理模)的实现方式比较暴力直接,需要将所有被代理类的所有方法都写一遍,并且一个个的手动转发过去。在维护被代理类的同时,作为java码工还需要同时维护代理类的相关代码,实在是累心。因此就需要我们的动态代理登场了,通过使用动态代理,动态代理能够自动将代理类的相关方法转发到被代理类。
2.2、动态代理
在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。
InvocationHandler:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
每一个代理实类例的invocation handler都要实现InvocationHandler这个接口。并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
- proxy:指代生成的代理对象;
- method:指代的是我们所要调用真实对象的某个方法的Method对象;
- args:指代的是调用真实对象某个方法时接受的参数;
我们来看看Proxy这个类,这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但用的最多的就是 newProxyInstance 这个方法:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
- loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
- interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
- h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。
通过上面的需要传入接口的参数可以看出,JDK动态代理需要借助接口来实现,如果我们要代理的对象功能没有抽成任何接口,那么我们就无法通过JDK动态代理的方式来实现。
好了,在介绍完这两个接口(类)以后,我们来通过一个实例来看看我们的动态代理模式是什么样的。首先我们定义了一个Subject类型的接口,为其声明了两个方法,这两个方法表示被代理类需要实现的功能:
public interface Subject {
public void sayGoodBye();
public void sayHello(String str);
}
接着,定义了一个类来实现这个接口,这个类就是我们的真实对象(被代理类),RealSubject类:
public class RealSubject implements Subject {
@Override
public void sayGoodBye() {
System.out.println("RealSubject sayGoodBye");
}
@Override
public void sayHello(String str) {
System.out.println("RealSubject sayHello " + str);
}
}
下一步,我们就要定义一个InvocationHandler了,相当于一个代理处理器。前面说个,每一个动态代理类实例的invocation handler 都必须要实现 InvocationHandler 这个接口:
public class SubjectInvocationHandler implements InvocationHandler {
//这个就是我们要代理的真实对象
private Object subject;
//构造方法,给我们要代理的真实对象赋初值
public SubjectInvocationHandler(Object subject) {
this.subject = subject;
}
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
//在代理真实对象前我们可以添加一些自己的操作
System.out.println("before Method invoke");
System.out.println("Method:" + method);
//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
method.invoke(subject, args);
//在代理真实对象后我们也可以添加一些自己的操作
System.out.println("after Method invoke");
return null;
}
}
SubjectInvocationHandler并不是真正的代理类,而是用于定义代理类需要扩展、增强那些方法功能的类。在invoke函数中,对代理对象的所有方法的调用都被转发至该函数处理。在这里可以灵活的自定义各种你能想到的逻辑。
最后,来看看我们的Client类:
public static void main(String[] args) {
//被代理类
Subject realSubject = new RealSubject();
//我们要代理哪个类,就将该对象传进去,最后是通过该被代理对象来调用其方法的
SubjectInvocationHandler handler = new SubjectInvocationHandler(realSubject);
//生成代理类
Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(), handler);
//输出代理类对象
System.out.println("Proxy : "+ subject.getClass().getName());
System.out.println("Proxy super : "+ subject.getClass().getSuperclass().getName());
System.out.println("Proxy interfaces : "+ subject.getClass().getInterfaces()[0].getName());
//调用代理类sayGoodBye方法
subject.sayGoodBye();
System.out.println("--------");
//调用代理类sayHello方法
subject.sayHello("Test");
}
输出结果:
Proxy : com.sun.proxy.$Proxy0
Proxy super : java.lang.reflect.Proxy
Proxy interfaces : com.company.ha.Subject
before Method invoke
Method:public abstract void com.company.ha.Subject.sayGoodBye()
RealSubject sayGoodBye
after Method invoke
--------
before Method invoke
Method:public abstract void com.company.ha.Subject.sayHello(java.lang.String)
RealSubject sayHello Test
after Method invoke
与静态代理相比,动态代理具有如下的优点:
1、代理转发的过程自动化了,实现自动化搬砖;
2、代理类的代码逻辑和具体业务逻辑解耦,与业务无关;
我们首先来看看 $Proxy0 这东西,这个东西就是真正的代理类对象,我们定义SubjectInvocationHandler类则是用于添加对代理类的功能扩展!而 $Proxy0类继承java.lang.reflect.Proxy类 并实现Subject接口 ,因此它的类声明方式如下:
public class $Proxy0 extends Proxy implements Subject
同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
如果我们定义的方法有返回值,那么可以通过invoke中把该方法的返回值进行返回,因为返回值的对象是Object,所以支持返回值为空(void)的返回。
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
//直接返回调用对象的返回值
return method.invoke(subject,args);
}
那么在调用代理类该方法的时候就能获取其返回值了。
当然我们可以针对某些方法的返回值进行加工再进行返回,比如我们假设public int sayGoodBye();方法是具有返回值的,那么我们希望在调用代理类的 sayGoodBye()方法的时候,返回值总能在原有的基础上+10,那么invoke中可以这么写:
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
if (method.getName().equals("sayGoodBye")) {//在调用sayGoodBye方法的时候 对返回值进行处理
int result = (int) method.invoke(subject, args);
return result + 10;
} else {//其他方法一律不处理
return method.invoke(subject, args);
}
}
客户端调用:
Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
int result = subject.sayGoodBye();
System.out.println("reslut "+result);
客户端获得result的值就总是对原有返回值基础上+10的返回值了。
三、动态代理源码分析
动态类Proxy的是通过newProxyInstance方法来生成的:
Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
因此我们从newProxyInstance来入手,为了更好的理解其原理,以下是精简后的代码:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){
//所有被实现的业务接口
final Class<?>[] intfs = interfaces.clone();
//1、寻找或生成指定的代理类:com.sun.proxy.$ProxyX
Class<?> cl = getProxyClass0(loader, intfs);
//通过反射类中的Constructor获取其所有构造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
//2、用构造方法创建代理类com.sun.proxy.$ProxyX的实例,并传入InvocationHandler参数
return cons.newInstance(new Object[]{h});
}
上面的代码主要实现两个操作:1、生成ProxyX 的Class类对象 ,2、通过该Class对象创建对应的实例,并传入InvocationHandler参与构造。
生成ProxyX的Class类对象的操作在getProxyClass0方法中:
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//...
return proxyClassCache.get(loader, interfaces);
}
proxyClassCache会缓存所有的代理类,如果缓存中有这个业务代理类,则会从缓存中取出,否则从ProxyClassFactory中生成。ProxyClassFactorys是Proxy的静态内部类,而ProxyClassFactory中的apply方法就是主要生成Class对象的地方:
Proxy$ProxyClassFactory:
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
...
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = "$Proxy" + ".";
}
// nextUniqueNumber是一个静态类每次生成一个代理类都会自增
long num = nextUniqueNumber.getAndIncrement();
//生成后的类名为com.sun.proxy.$ProxyX
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//传入接口、类名,生成了我们要的字节码形式的代理类
//Generate the specified proxy class.
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
//defineClass0是个native方法,生成二进制数据对应的Class类对象
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
}
真正核心生成字节码的方法是ProxyGenerator.generateProxyClass方法,这里就不深究内部是如何生成的,由于ProxyX是以字节码形式存在于内存中,我们无法看到其全貌,因此我们可以仿ProxyGenerator.generateProxyClass方法的方式生成其class字节码,查看其全貌:
//真实对象
Subject realSubject = new RealSubject();
//生成代理类
byte[] proxyClassFile = ProxyGenerator.generateProxyClass("MyName", realSubject.getClass().getInterfaces());
//保存在本地文件中
try (FileOutputStream fis = new FileOutputStream(new File("./myname.class"))){
fis.write(proxyClassFile);
} catch (Exception e) {
e.printStackTrace();
}
打开myname.class文件,最终代理类$ProxyX类是这样的:
public final class MyName extends Proxy implements Subject {
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m0;
public MyName(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void sayHello(String var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void sayGoodBye() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("com.company.ha.Subject").getMethod("sayHello", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.company.ha.Subject").getMethod("sayGoodBye");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
这个流程终于变得清晰了。当我们将接口Subject和SubjectInvocationHandler传入Proxy中后,Proxy会为我们生成一个实现了Subject接口并继承了Proxy的业务代理类$Proxy0 。在我们具体调用方法 subject.sayGoodBye();时它其实是调用了 $Proxy0中的sayGoodBye();方法,然后再调用Proxy类的invoke方法,所以SubjectInvocationHandler中的invoke方法才是最终执行的方法,这个方法给了我们扩展的可能并且最终我们实现了代理对象访问原对象的目的,也就是 $Proxy0代理了RealSubject。
四、应用场景
4.1、Retrofit
Android中的网络注解框架retrofit内部实现其实就是应用了动态代理技术,通常我们定义网络接口是这样的:
public interface ApiStore {
// 员工登录
@FormUrlEncoded
@POST("/resource/d/member/login")
Observable<BaseResponse<LoginResult>> login(@FieldMap Map<String, String> params);
// 退出登录
@FormUrlEncoded
@POST("/resource/d/member/signOut")
Observable<BaseResponse<LogOutResult>> logout(@FieldMap Map<String, String> params);
//....
}
创建Retrofit:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://ww.xxx.com/")
.build();
ApiStore service = retrofit.create(ApiStore.class);
而retrofit.create(ApiStore.class);这句代码其实就是创建一个代理类ProxyX,这个生成的代理类的类声明是这样的:
public $Proxy3 extends Proxy implements ApiStore
我们可以看retrofit.create内部具体的实现方式:
public <T> T create(final Class<T> service) {
//...
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
看到没,retrofit就是通过Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },new InvocationHandler() )的方式创建的一个代理类,invoke中的代码就是当网络接口被调用的时候需要做的处理。在这个方法里,首先根据接口定义的方法,生成一个ServiceMethod对象,在ServiceMethod对象中会反射接口中定义的注解,解析出具体的网络请求方式,然后拿到封装好的ServiceMethod对象后,构造一个OkHttpCall对象,以便与进行真正的网络请求(OkHttp)。
五、总结
JDK的动态代理机制是利用动态在内存中生成类字节码的方式实现的,将分散的不同对象的不同方法的调用转发到该动态类中进行处理,有了这个关键点就可以对不同对象中的不同方法中进行动态的功能增强,是实现切片编程的中最重要的方式之一。