JDK动态代理原理

静态代理

生活中代理的例子不胜枚举:
张三由于工作很忙,迟迟没有结婚,家里人很着急。他爸爸拿着他的照片帮他张罗着相亲,他爸爸就是他的一个代理。
- 定义接口

public interface People {
    /**
     * 找对象
     */
    public void Zhaoduixiang();
}
  • 张三
public class ZhangSan implements People{
    public void Zhaoduixiang() {
        System.out.println(this.getClass().getName()+":要求不多,人美胸大活好!");
    }
}
  • 张三他爸
public class ZhangSanFather implements People{
    /**
     * 被代理对象的引用,相当于他爸拿着张三的照片
     */
    private ZhangSan zhangSan;

    public ZhangSanFather(){

    }
    public ZhangSanFather(ZhangSan zhangSan){
        this.zhangSan = zhangSan;
    }

    /**
     * 代理
     */
    public void Zhaoduixiang() {
        before();
        this.zhangSan.Zhaoduixiang();
        after();
    }

    /**
     *  想跟我儿子好,我先要审核下
     */
    private void before(){
        System.out.println(this.getClass().getName()+":要求人品好,家境好!");
    }

    /**
     * 评价这个菇凉
     */
    private void after(){
        System.out.println(this.getClass().getName()+":这个菇凉阔以!");
    }
}
  • 测试
    @Test
    public void testStaticProxy(){
        People zhangSanFather = new ZhangSanFather(new ZhangSan());
        zhangSanFather.Zhaoduixiang();
    }
proxy.staticProxy.ZhangSanFather:要求人品好,家境好!
proxy.staticProxy.ZhangSan:要求不多,人美胸大活好!
proxy.staticProxy.ZhangSanFather:这个菇凉阔以!

重要特点:
- 代理类持有被代理类的引用

动态代理

张三没结婚,请个保姆做饭,洗碗,张三自己只用张嘴吃饭就行。保姆就是张三的代理。
- 接口

public interface People {
    /**
     * 吃东西
     */
    void eat(String food) throws Throwable;
}
  • 张三
public class ZhangSan implements  People{
    /**
     * 吃
     * @param food 食物
     */
    public void eat(String food) {
        System.out.println(this.getClass().getName()+":吃"+food+"!");
    }
}
  • 代理
public class ProxyHandler implements InvocationHandler{
    /**
     * 被代理对象的引用
     */
    private People people;

    public ProxyHandler(People people){
       this.people = people;
    }

    /**
     * 代理方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        method.invoke(people,args);
        after();
        return null;
    }

    /**
     * 执行在代理方法之前
     */
    private void before(){
        System.out.println(this.getClass().getName()+":做饭!");
    }

    /**
     * 执行在代理方法之后
     */
    private void after(){
        System.out.println(this.getClass().getName()+":洗碗!");
    }
}
  • 测试
    @Test
    public void testJDKDynamicProxy() throws Throwable {
        //创建代理
        People people = (People) Proxy.newProxyInstance(
                People.class.getClassLoader(),
                new Class[]{People.class},
                new ProxyHandler(new ZhangSan()));
        System.out.println(people.getClass().getName());
        //执行操作
        people.eat("炸鸡,喝啤酒!");
    }
com.sun.proxy.$Proxy0
proxy.dynamicProxy.ProxyHandler:做饭!
proxy.dynamicProxy.ZhangSan:吃炸鸡,喝啤酒!!
proxy.dynamicProxy.ProxyHandler:洗碗!

到这里,效果虽然看到了,但是为什么要使用动态代理呢?

保姆给张三做饭、洗碗,大多时候不会像张三他爸那样长期固定,可能会因各种原因辞职,而重新帮另外的人做饭、洗碗。她代理的具体事情没有变化,变化的只是被代理的对象,如果使用静态代理,那么每次都要重新定义代理(不符合开闭原则),或者新增代理(代码冗余,不宜维护)。

那么jdk动态代理又是如何工作的呢?

前面代码中

System.out.println(people.getClass().getName());

打印的结果是

com.sun.proxy.$Proxy0
  • 这就是jdk动态生成的,处于内存中的代理。还是一头雾水,必须要看看他具体的内容是什么
/**
     * 生成代理的Class文件
     */
    public static void createProxyClassFile(){
        //生成代理类的字节流
        byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{People.class});
        //生成文件
        try {
            FileOutputStream fout = new FileOutputStream("src/main/java/proxy/dynamicProxy/$Proxy0.class");
            fout.write($Proxy0s);
            fout.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  • 重新运行程序,生成代理文件
@Test
    public void testJDKDynamicProxy() throws Throwable {
        //创建代理
        People people = (People) Proxy.newProxyInstance(
                People.class.getClassLoader(),
                new Class[]{People.class},
                new ProxyHandler(new ZhangSan()));
        System.out.println(people.getClass().getName());
        //执行操作
        people.eat("炸鸡,喝啤酒!");
        createProxyClassFile();
    }

image
如上图,这个$Proxy0.class就是jdk动态生成的,反编译打开该文件,真相就在里面!

public final class $Proxy0 extends Proxy implements People {
    private static Method m1;
    private static Method m0;
    private static Method m3;
    private static Method m2;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
       ...
    }

    public final int hashCode() throws  {
       ...
    }

    public final void eat(String var1) throws Throwable {
        super.h.invoke(this, m3, new Object[]{var1});
    }

    public final String toString() throws  {
        ...
    }

    static {
        try {
            ...
            m3 = Class.forName("proxy.dynamicProxy.People").getMethod("eat", new Class[]{Class.forName("java.lang.String")});
            ...
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

从源码可以看出$Proxy0的真实身份:
1、它实际上实现了People接口,所以可以具体实现eat(String var1)方法。
2、而且它继承了Proxy类,在jdk源码中可以看到它有一个成员变量InvocationHandler,是不是很眼熟,这就是我们自定义代码ProxyHandler实现的接口。

public class Proxy implements java.io.Serializable{
    ...
    protected InvocationHandler h;
    ...
}

3、这里逻辑就很清晰了,eat(String var1)的具体实现就是调用InvocationHandler接口的invoke方法,并把真实的eat方法反射生成通过参数传入invoke方法,所以我们才能在ProxyHandler中调用method.invoke(people,args),从而实现动态代理。

动态代理完美的诠释的开闭原则,即对扩展开放,对修改关闭。非常适用于程序扩展,非常非常重要,在各种框架中广泛应用,如大名鼎鼎的SpirngAOP。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值