动态代理

本文详细介绍了代理模式的概念及其在Java中的应用。代理模式分为静态代理和动态代理两种形式,静态代理适用于对象数量有限的情况,而动态代理则能在运行时动态生成代理类。文中还通过实例演示了如何使用JDK动态代理和CGLib动态代理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代理模式

  • 定义:为其他对象提供一种代理机制,以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。-《设计模式》

  • 在代理模式下,有一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理对象实现同一个接口,先访问代理类再访问真正要访问的对象。

  • 代理模式分为静态代理、动态代理

静态代理

  • 静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。
  • 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
//主题接口,后续类需要实现此接口
public interface Subject {
    public void doSomething();
}
//实现上述接口的具体类,表示目标对象
public class RealSubject implements Subject {
    public void doSomething() {
        System.out.println("这是真正的对象");
    }
}
//代理类,如上文所述,需要与目标类实现相同的接口
public class ProxySubject implements Subject {

    //包含真正目标对象作为成员
    private Subject subject;

    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    //通过访问代理类实现对目标对象的访问
    public void doSomething() {
        System.out.println("这是一个代理对象");

        this.subject.doSomething();
    }
}
public class ProxyTest {
    public static void main(String[] args){

        //根据真实对象创建一个代理对象
        ProxySubject proxy = new ProxySubject(new RealSubject());

        //通过访问代理对象实现对真实对象的访问
        proxy.doSomething();
    }
}

运行结果:
这里写图片描述

  • 优点:代理类接受一个Subject接口的对象,任何实现该接口的对象,都可以通过代理类进行代理,增加了通用性。

  • 缺点:代理对象和真实对象高度耦合,每一个代理类都必须实现一遍委托类的接口,如果接口增加方法,则代理类也必须跟着修改;其次,代理类每一个接口对象对应一个委托对象,如果委托对象非常多,则静态代理类就非常臃肿,难以胜任。

动态代理

  • 顾名思义,动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。Java的动态代理有两种方式,传统的jdk动态代理和cglib动态代理,主要区别在于前者通过实现接口,后者通过继承方式。

jdk动态代理

1.通过实现InvocationHandler接口来自定义自己的InvocationHandler;
2.通过Proxy.getProxyClass获得动态代理类
3.通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)
4.通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入
5.通过代理对象调用目标方法

//实现主题接口的目标类
public class ActionImpl_01 implements ActionInf{
    public void doSomething(String word) {
        System.out.println(this.getClass().getName()+"::"+"Hello "+word);
    }
}
//实现InvocationHandler接口
public class InvocationHandlerImpl implements InvocationHandler{
    private ActionInf object;

    public InvocationHandlerImpl(ActionInf object) {
        this.object = object;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("======before invoke======");

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

        Object res = method.invoke(this.object, args);

        System.out.println("======after invoke======");

        return res;
    }

    public Object getProxyObject(){

        return Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(), 
                this.object.getClass().getInterfaces(), 
                this
        );
    }
}
public class DynamicProxyTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        ActionImpl_01 a1 = new ActionImpl_01();

        //通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入 
        InvocationHandler handler1 = new InvocationHandlerImpl(a1);

        //通过Proxy.getProxyClass获得动态代理类
        Class proxyClass = Proxy.getProxyClass(ActionInf.class.getClassLoader(), ActionInf.class);

        //通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)
        Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);

        //通过代理对象调用目标方法
        ActionInf inf = (ActionInf)constructor.newInstance(handler1);

        inf.doSomething("world");

    }
}

运行结果

当然,上述步骤中的2、3、4步也可通过调用Proxy.newProxyInstance()一步完成,读者可以自行查看此方法的实现过程。

上述过程中,我们并没有静态构造实现Subject主题接口的代理类,而是在运行过程中生成了一个名为com.sun.proxy.$Proxy0的代理类,此类运行时生成,结束后销毁,达到了动态的标准。

cglib动态代理

  • CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
//实现cglib动态代理的关键,就是要实现MethodInterceptor(方法拦截器接口)
public class CglibProxyImpl implements MethodInterceptor {
    private ActionInf object;

    public CglibProxyImpl(ActionInf object) {
        this.object = object;
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("======before invoke======");

        ActionInf res = (ActionInf) methodProxy.invokeSuper(o, objects);

        System.out.println("======after invoke======");

        return res;
    }

    public ActionInf getProxyObject(){
        Enhancer enhancer = new Enhancer();

        //设置enhancer对象的父类
        enhancer.setSuperclass(this.object.getClass());

        //设置enhancer的回调对象
        enhancer.setCallback(this);

        //创建代理对象
        return (ActionInf)enhancer.create();
    }
}
public class DynamicProxyTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        ActionImpl_01 a1 = new ActionImpl_01();

        CglibProxyImpl cg = new CglibProxyImpl(a1);

        ActionInf inf = cg.getProxyObject();

        inf.doSomething("World");
    }
}

运行结果

可以看出,cglib通过继承方式基于目标类动态生成一个子类,那很明显一点,cglib不能动态代理final修饰的方法,这点与jdk动态代理有较大差别。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值