一文吃透动态代理!

本文深入探讨Java动态代理,从JDK动态代理的示例到源码分析,揭示其生成代理类的机制。CGLib代理则通过子类化被代理类进行增强,解决了JDK代理对接口的依赖。同时,文章指出CGLib无法代理static、private、final方法。
摘要由CSDN通过智能技术生成

前言 

        动态代理几乎是面试必问题,因为Spring AOP的实现原理之一就是动态代理,此外,动态代理相较其他设计模式而言,它还比较特殊,动态代理需要动态生成一个类,而且它还有两种动态生成类的实现方式;

 示例

        先写一个JDK动态代理,一个演员需要有经纪人,我们把演员当成被代理类,经纪人当成代理类;

演员类Actor.java:

package com.lw.designpattern.proxy.dynamic;

public class Actor {
    public void act() {
        System.out.println("某毯星戛纳走红毯");
    }
}

调用处理类ActorInvocation.java,为什么要设计这么一个类,后面讲

package com.lw.designpattern.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ActorInvocation<T> implements InvocationHandler {

    T target;

    public ActorInvocation(T target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("经纪人准备");
        method.invoke(this.target, args);
        System.out.println("经纪人善后");
        return null;
    }
}

测试类:

package com.lw.designpattern.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class DynamicProxyTest {

    public static void main(String[] args) {
        Actor actor = new Actor();
        InvocationHandler actorHandler = new ActorInvocation<Actor>(actor);
        Actor actorProxy = (Actor) Proxy.newProxyInstance(Actor.class.getClassLoader(), new Class<?>[] { Actor.class }, actorHandler);

        actorProxy.act();
    }
}

测试结果: 

Exception in thread "main" java.lang.IllegalArgumentException: com.lw.designpattern.proxy.dynamic.Actor is not an interface
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590)
	at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
	at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
	at java.lang.reflect.WeakCache.get(WeakCache.java:127)
	at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
	at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
	at com.lw.designpattern.proxy.dynamic.DynamicProxyTest.main(DynamicProxyTest.java:12)

咦,报错了。。。。提示Actor类不是一个接口,原因是啥,暂且按下不表,先改成接口再说

package com.lw.designpattern.proxy.dynamic;

public interface Actor {
    public void act();
}

增加实现类:

package com.lw.designpattern.proxy.dynamic;

public class BBFan implements Actor {

    @Override
    public void act() {
        System.out.println("某毯星戛纳走红毯");
    }
}

测试结果:

经纪人准备
某毯星戛纳走红毯
经纪人善后

OK,搞定,下面讲讲实现原理

源码刨析

        在说原理之前,先搞清楚概念,所谓的动态,其实就是动态生成一个代理类Class,相比较静态代理模式的手写代理类,动态代理在这方面可谓是更加灵活。下面先看看生成的代理类是个什么样子。

        代理类我是直接通过ProxyGenerator类生成的,这也是JDK源码使用的类,所以生成的代理类中没有包含自定义的代理功能。

        当然也可以在测试方法中加上这句:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

byte[] proxyClassFile = ProxyGenerator.generateProxyClass("ActorProxy", new Class[]{Actor.class});
try {
    FileOutputStream f = new FileOutputStream("ActorProxy.class");
    f.write(proxyClassFile);
    f.flush();
    f.close();
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

生成代理类class的反编译源码: 

import com.lw.pattern.proxy.dynamic.Actor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class ActorProxy extends Proxy implements Actor {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public ActorProxy(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 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 act() throws  {
        try {
            // 这边通过父类的h属性调用实际业务方法
            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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.lw.pattern.proxy.dynamic.Actor").getMethod("act");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

从这个源码我们来解释一下上面遗留的两个问题:

1,代理类继承了JDK的Proxy Class,因为Java规范设计一个类只能继承一个父类,且新代理类与被代理类属于兄弟关系,所以只能通过接口来标识两者的关系,这也就解释了为什么JDK的动态代理要求被代理类一定要实现接口。

2,Invocation的设计是为了动态代理的调用,我们可以在Proxy类中找到关于Invocation的属性定义:

    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

        有了这个实例变量的定义,我们就可以把这个Invocation看作是动态代理的模板方法,这样的话,每new一个新的被代理类,就不需要像静态动态代理那样修改代理类。

        看到这里,就会发现,动态代理真的没有什么高深的东西,本质上同静态代理的实现思路也一致。

CGLib动态代理

        CGlib 动态代理模式的实现原理是对代理目标类生成一个子类,并覆盖其中方法实现增强,因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理类必须要有接口的缺陷。同时,由于是继承方式,如果是 static方法,private方法,final方法等描述的方法是不能被代理的,cglib动态代理底层是借助asm字节码技术;

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值