动态代理概述
动态代理是指在运行时动态生成代理类。不需要像静态代理那个去手动写一个个的代理类。生成动态代理类有很多方式:Java动态代理,CGLIB,Javassist,ASM库等。这里主要说一下Java动态代理和CGLIB的实现。
一 JDK动态代理—-通过接口
在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现动态代理所必须用到的。
-
InvocationHandler:
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
/** * * proxy: 指代所代理的那个真实对象 * method: 指代的是所要调用真实对象的某个方法的Method对象 * args: 指代的是调用真实对象某个方法时接受的参数 * */ Object invoke(Object proxy, Method method, Object[] args) throws Throwable -
Proxy:
这个代类有很多方法常用的方法就是newProxyInstance
/** * * loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载 * interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口, * 如果提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了 * h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上 * */ public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
一个例子:
-
定义一个Subject类型的接口:
package com.kyle.DynamicProxy; public interface Subject { void hello(String str); } -
定义了一个类来实现这个接口,这个类就是真实对象,RealSubject类:
package com.kyle.DynamicProxy; public class RealSubject implements Subject { @Override public void hello(String str) { System.out.println("hello: " + str); } } -
定义一个动态代理类了,每一个动态代理类都必须要实现 InvocationHandler 这个接口,因此这个动态代理类也不例外:
package com.kyle.DynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class DynamicProxy implements InvocationHandler { // 要代理的真实对象 private Object subject; // 构造方法,要代理的真实对象赋初值 public DynamicProxy(Object subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在代理真实对象前我们可以添加一些自己的操作 System.out.println("before Hello"); System.out.println("Method:" + method); // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用 method.invoke(subject, args); // 在代理真实对象后我们也可以添加一些自己的操作 System.out.println("after Hello"); return null; } } -
最后,定义Client类:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { // 要代理的真实对象 Subject realSubject = new RealSubject(); // 要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的 InvocationHandler handler = new DynamicProxy(realSubject); /* * 通过Proxy的newProxyInstance方法来创建我们的代理对象,三个参数 第一个参数 * handler.getClass().getClassLoader() * ,这里使用handler这个类的ClassLoader对象来加载代理对象 * 第二个参数realSubject.getClass().getInterfaces(),这里为代理对象提供的接口是真实对象所实行的接口 * ,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了 第三个参数handler, 我们这里将这个代理对象关联到了上方的 * InvocationHandler 这个对象上 */ Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); System.out.println(subject.getClass().getName()); subject.hello("world"); } }
控制台的输出:
com.sun.proxy.$Proxy0
before Hello
Method:public abstract void com.kyle.DynamicProxy.Subject.hello(java.lang.String)
hello: world realSubject
after Hello
注:通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
对于JDK动态代理的疑问 为什么java动态代理为什么一定要代理接口而不能代理类? 在动态代理经过编译之后的实现类似于以下结构:
public class $Proxy1 extends Proxy implements 传入的接口{
}
到这里应该很容易理解为什么这里需要代理接口而不是一个类,因为这个动态代理已经继承了Proxy这个类.
延迟加载代理举例:
-
接口类:
package com.kyle.LazyLoad; public interface IDBExcute { String query(); int update(); } -
实现类:
package com.kyle.LazyLoad; public class DBExcute implements IDBExcute { String queryContent = "Something from DB."; @Override public String query() { System.out.println("query is running..."); // 模拟查询操作的返回结果 return queryContent; } public DBExcute() { // 真实对象被创建时候打印信息 System.out.println("Real object is created..."); } @Override public int update() { System.out.println("update is running..."); // 模拟更新操作影响的件数 return 1; } } -
代理类:
package com.kyle.LazyLoad; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class DBExcuteHandler implements InvocationHandler { // 定义主题接口 IDBExcute dbQuery = null; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 如果第一次调用,生成真实主题,创建真正要代理的类 if (dbQuery == null) { dbQuery = new DBExcute(); } // 返回真实主题完成实际的操作 return method.invoke(dbQuery, args); } } -
测试类:
package com.kyle.LazyLoad; import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { System.out.println("开始获取代理对象"); // 获取代理对象的时候真正的对象并没有被创建 IDBExcute excute = (IDBExcute) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] { IDBExcute.class }, new DBExcuteHandler()); System.out.println("开始调用实际方法"); // 当第一次调用实际对象方法的时候真正的对象才被创建 String result = excute.query(); System.out.println("查询方法结果: " + result); int count = excute.update(); System.out.println("更新结果: " + count); } }
控制台的输出:
开始获取代理对象
开始调用实际方法
Real object is created...
query is running...
查询方法结果: Something from DB.
update is running...
更新结果: 1
二 CGLIB动态代理—-通过类继承
未完待续。。。