还分不清代理之间区别?一文带你掌握代理

代理

代理模式(Proxy):为其他对象提供一种代理来控制对这个对象的访问。

代理又分为静态代理动态代理

静态代理指的是代理类由我们程序员自己编写代理类。常见实现方式有组合实现,继承等。

动态代理指的是程序运行时代理类自动生成,我们只需要提供核心代码即可。常见动态代理有JDK原生动态代理,CGLIB动态代理

静态代理

有一个接口:

public interface Action {
    public abstract void action();
}

它的一个实现类:

public class Target implements Action{
    @Override
    public void action() {
        System.out.println("This is targetMethod.");
    }
}

现在我有一个需求,要对 Targetaction()方法进行一个增强。

那么这时候就有人要说了,这简单,直接修改action()方法内部逻辑,做一个增强不就好了吗…

这其实也可以,但这违背了 OCP 原则(对扩展开放,对修改关闭),这条原则建议我们最好不要修改原有的代码,而是用其他的方法扩展原有的代码。

这就引入这期的主角 - 代理

设想一下我可不可以在一个对象中有 Target的引用,在另一个对象中调用Target的方法。这就是静态代理。

public class MyProxy implements Action{
    private Action target;

    public MyProxy(Action target){
        this.target = target;
    }

    @Override
    public void action() {
        // 做一些增强
        System.out.println("目标方法 前 的增强");
        target.action();
        System.out.println("目标方法 后 的增强");
    }

}

使用方式如下:

public static void main(String[] args) {
    Action proxy = new MyProxy(new Target());
    proxy.action();
}
// out:
// 目标方法 前 的增强
// This is targetMethod.
// 目标方法 后 的增强

这是通过组合实现,还可以通过继承实现, CGLIB 就是通过继承实现动态代理的。

继承实现:

public class MyProxy extends Target implements Action{
    @Override
    public void action() {
        // 做一些增强
        System.out.println("目标方法 前 的增强");
        super.action();
        System.out.println("目标方法 后 的增强");
    }
}

使用方式:

public static void main(String[] args) {
    Action proxy = new MyProxy();
    proxy.action();
}
// out:
// 目标方法 前 的增强
// This is targetMethod.
// 目标方法 后 的增强

试想一下我们每次要代理一个类,就要多出来一个代理类,容易造成类爆炸(类的数量急剧增多),于是人们就想有没有一种能够帮助我自动生成代理类的技术呢?

有的兄弟,有的,这样的技术有两种:

    1. JDK原生动态代理
    2. CGLIB动态代理

动态代理

JDK原生动态代理

jdk代理是java自带的代理方法,主要采用了多态和反射的技术,让代理对象和目标类一起去实现一个共同的接口,这样就可以保证两者有相同的方法。

jdk代理通过 Proxy.newProxyInstance() 获取到代理对象

public static void main(String[] args) {
    Action target = new Target();
    Action proxyInstance = (Action)Proxy.newProxyInstance(Action.class.getClassLoader(),
            new Class[]{Action.class},
            new MyProxy(target));
    proxyInstance.action();
}
Proxy.newProxyInstance(类加载器,
                       接口集合,代理对象要实现的接口,
                       一个函数式接口 -> (代理对象,原方法,参数));
public class MyProxy implements InvocationHandler {

    // 原对象
    Action target;

    public MyProxy(Action target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // invoke(代理对象自己,原方法,方法参数)

        // 做一些增强
        System.out.println("目标方法 前 的增强");
        Object result = method.invoke(target,args);
        System.out.println("目标方法 后 的增强");
        return result;
    }
}

代理类反编译后:

public final class $Proxy0 extends Proxy implements Action {
    // hashCode()
    private static final Method m0;
    // equals()
    private static final Method m1;
    // toString()
    private static final Method m2;
    // 真正代理的方法 action()
    private static final Method m3;

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

    public final int hashCode() {
         return (Integer)super.h.invoke(this, m0, (Object[])null);
    }

    public final boolean equals(Object var1) {
       return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
    }

    public final String toString() {
       return (String)super.h.invoke(this, m2, (Object[])null);
    }

    public final void action() {
        super.h.invoke(this, m3, (Object[])null);
    }

    static {
        ClassLoader var0 = $Proxy0.class.getClassLoader();

            m0 = Class.forName("java.lang.Object", false, var0).getMethod("hashCode");
            m1 = Class.forName("java.lang.Object", false, var0).getMethod("equals", Class.forName("java.lang.Object", false, var0));
            m2 = Class.forName("java.lang.Object", false, var0).getMethod("toString");
            m3 = Class.forName("proxy.Action", false, var0).getMethod("action");
        
    }

    private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup var0) throws IllegalAccessException {
        if (var0.lookupClass() == Proxy.class && var0.hasFullPrivilegeAccess()) {
            return MethodHandles.lookup();
        } else {
            throw new IllegalAccessException(var0.toString());
        }
    }
}

可以看到,自动生成的代理类,不仅增强了 action(), 还增强了 hashCode() , equels() , toString()三个方法。

CGLIB 动态代理

cglib代理不是jdk自带的代理,需要导外部依赖。

底层是让代理类继承目标类,通过Enhancer.create获取代理对象。

	Enhancer.create(目标类,
		一个函数式接口 -> (代理对象自己,需要被增强的方法,方法参数,MethodProxy 代理不走反射的关键));

interface MethodInterceptor extends Callback

public static void main(String[] params) {
    Action target = new Target();
    Action proxyObj = (Action)Enhancer.create(Action.class, (MethodInterceptor) (proxy, meth, arg, methodProxy) -> {
        System.out.println("enhance before");

        // 反射调用目标方法
        // Object result = meth.invoke(target, arg);

        // methodProxy 可以避免反射调用

        // 内部没有用反射,需要原对象
        Object result = methodProxy.invoke(target, arg);

        // 内部没有用反射,需要代理对象
        //Object result = methodProxy.invokeSuper(proxy, arg);

        System.out.println("enhance after");

        return result;
    });
    proxyObj.action();
}
// out:
// enhance before
// This is targetMethod.
// enhance after

生成如下三个文件:

在这里插入图片描述

区别:

  • 使用JDK动态代理,被代理类必须要实现接口;使用CGLIB动态代理,被代理类可以不实现接口
    • JDK动态代理生成的代理类继承了java.lang.reflect.Proxy,因为Java是单继承的,如果不通过实现接口的形式,无法对类进行扩展。
    • CGLIB动态代理生成的代理类实际上是被代理类的子类,所以被代理类可以不实现接口。
  • 自动生成类的数量不同
    • JDK动态代理只会生成1个代理类,一般情况下名称为:com.sun.proxy.$Proxy0。

    • CGLIB动态代理会生成好几个类,核心的3个分别是:

      • 1)代理类:被代理类的子类,名称格式为Action$$EnhancerByCGLIB$$62adcfbf,包名和被代理类包名一致。

      • 2)代理类的索引类:名称格式为Action$$EnhancerByCGLIB$$62adcfbf$$FastClassByCGLIB$$2fbc00a6,包名和被代理类包名一致。

      • 3)被代理类的索引类:名称格式为Action$$FastClassByCGLIB$$7219d5a6,包名和被代理类包名一致。

  • 调用方式不同
    • JDK动态代理:代理类—>InvocationHandler.invoke()—>被代理类方法(用到了反射)。
    • CGLIB动态代理:代理类—>MethodInterceptor.intercept()—>代理类索引类 getIndex()—> 代理类索引类invoke()—>代理类—>被代理类。(直接调用)

总结

  • JDK原生动态代理是Java原生支持的,不需要任何外部依赖,但是它只能基于接口进行代理(需要代理的对象必须实现于某个接口)
  • CGLIB通过继承的方式进行代理(让需要代理的类成为Enhancer的父类),无论目标对象有没有实现接口都可以代理,但是无法处理final的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值