我理解和整理的动态代理
动态代理这章学的时候感觉有点复杂,看完狂神的视频还是没完全搞清楚,再结合廖雪峰、bravo1988等大神的笔记,整理一下我所理解的动态代理,如有不对的地方,请各位大神帮忙指出。
什么是动态代理
要搞清楚动态代理之前,需要先了解静态代理,静态代理的机制是,在目标对象和抽象对象(接口)之间构建一个实现类实现接口,来实现不动源代码而修改功能达到需求的目的。比如现在有房东(目标对象)要出租房屋,中介说帮他出租房租,共同目的都是出租房屋所以他俩是出租房屋接口的实现类,不同的是房东目的很单纯,就出租房,中介就不同了,他能通过引入这个房东的姓名来看出租什么房,在此基础上,可以再加其他要求,比如端茶倒水捶背,最后租客创建实例,调用中介提供的房东’s出租房方法完成租赁,用图来表示是这样的:
这种架构就很简洁明了,但弊端就是每个目标对象都有一个代理,很浪费代码,而且如果在不更改源代码(接口和目标对象)的情况下,有需求要加,那就麻烦了,每个代理都要改,比如说,中介老大规定所有中介给租客租房前先要给租客鞠个躬,有100个中介,修改代理的代码,加上鞠躬这个动作。
100个也只是假如,如果上万个,哭吧就。所以这时候就需要动态代理了,动态代理就是直接得到代理class对象,不用单独写个代理对象,而是通过某种特殊方法来创建代理对象,等于用独门秘方给你clone出来100个中介,不用一个个去建,而你想要管理这100个中介,管理这个独门秘方就行,这里我看到个有趣的比喻:
接口Class对象是大内太监,里面的方法和字段比做他的一身武艺,但是他没有小DD(构造器),所以不能new实例。一身武艺后继无人。那怎么办呢?
正常途径(implements):写一个类,实现该接口。这个就相当于大街上拉了一个人,认他做干爹。一身武艺传给他,只是比他干爹多了小DD,可以new实例。
非正常途径(动态代理):通过妙手圣医Proxy的克隆大法(Proxy.getProxyClass()),克隆一个Class,但是有小DD。所以这个克隆人Class可以创建实例,也就是代理对象。
来自于知乎 https://www.zhihu.com/question/20794107/answer/658139129
动态代理创建代理对象读取内存过程
一般静态代理从创建代理类,目标类到内存的过程是这样的
动态代理创建代理对象的存取内存的过程是这样的(个人理解所画,如有不对的地方请帮忙指正)
写个代码来实现下动态代理
情景:booking系统,bookingAdmin出租书,有人想租书,用动态代理怎么实现?
我主要分接口booking,接口实现类(目标对象)bookingAdmin,动态代理统一类getProxy(其他booking也能用),实例。
booking 接口:
package com.jj.demo02;
//booking接口,有无参和有参两个booking函数
public interface Booking {
void booking();
void booking(String bookName);
}
实现类bookAdmin:
package com.jj.demo02;
//booking的实现类,目标类
public class BookAdmin implements Booking{
@Override
public void booking() {
}
//重写了booking的有参函数
public void booking(String bookName) {
System.out.println("预定了"+bookName);
}
}
通用生成动态代理的类getProxy
package com.jj.demo02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class getProxy {
//无参构造函数
public getProxy() {
}
//有参构造函数,target来适用不同的接口和目标对象
public static Object getProxy(Object target) throws Exception{
//生成代理类
Object proxy=Proxy.newProxyInstance(
target.getClass().getClassLoader(),//加载类
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
//处理代理实例
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这里调用了目标类的方法
Object result=method.invoke(target,args);
return null;
}
}
);
return proxy;
}
}
最后实例
import com.jj.demo02.*;
public class MyTest {
public static void main(String[] args) throws Exception {
BookAdmin target=new BookAdmin();
String bookName="红楼梦";
//实例代理类
Booking bookingProxy=(Booking) getProxy.getProxy(target);
bookingProxy.booking(bookName);
}
}
结果
新需求:执行前要先打印下说执行了什么方法
如果是静态代理,在代理对象上增加一个方法打印出来,那动态代理呢?
如之前所说,直接修改独门秘诀就行,修改getProxy,加打印的方法,代码实现如下
getProxy 类
package com.jj.demo02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class getProxy {
//无参构造函数
public getProxy() {
}
//有参构造函数,target来适用不同的接口和目标对象
public static Object getProxy(Object target) throws Exception{
//生成代理类
Object proxy=Proxy.newProxyInstance(
target.getClass().getClassLoader(),//加载类
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
//处理代理实例
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//新增打印执行了什么方法
doLog(method.getName());
//这里调用了目标类的方法
Object result=method.invoke(target,args);
return null;
}
}
);
return proxy;
}
//打印执行了什么方法的方法
public static void doLog(String msg){
System.out.println("执行了"+msg);
}
}
加了一个doLog的方法,在invoke中调用了一下。所以其他100个1万个代理都可以适用了,方便!
总结
动态代理是通过newProxyInstance生成代理类,invoke进行实例的方法,没有静态代理那么直接单纯,但是对后期修改代码很友好。到这,我才算理解了些动态代理的皮毛,希望我所整理的对同样有困惑的朋友也有帮助,有不对的请大家指教。
最后,bravo1988大神的太监比喻真的很有趣,推荐大家也去看看,链接文中有。