Java JDK动态代理 CGLib动态代理

代理(proxy):就是一个“中介”。

现在对象A可以直接调用对象B。

需求来了:在调用B的前后打印日志。

静态代理

创建一个新类(发生在编译时),来作为旧类的代理,从而增加功能。增加新功能的代码无法复用。

public class Hello{
    public void sayHello(){
        System.out.println("Hello");
    }
}

// 代理类
public class ProxyStatic extends Hello{
    private Hello hello = new Hello();

    public void sayHello(){
        System.out.println("START log");
        hello.sayHello();
        System.out.println("END log");
    }
}

public class ProxyStaticTest {

    public static void main(String[] args){
        // 调用原来的类
        Hello hello = new Hello();
        hello.sayHello();
        // 调用代理类
        ProxyStatic helloNew = new ProxyStatic();
        helloNew.sayHello();
    }
}

输出:
Hello
START log
Hello
END log

 

动态代理 by JDK

需求增加:有一批被调用对象,给这些调用的前后都加上日志。所以,增加新功能的代码可复用,不可能给每一个类都写一个静态代理类;可在运行时修改,也就是说原来的类已经编译加载到内存了。所以,不能修改原来的类,只能创建一个新类,拦截原来的方法。

// 接口
public interface IHello {
    void sayHello();
}

// 实现了接口的类
public class Hello implements IHello{  
    public void sayHello(){
        System.out.println("Hello");
    }
}

import java.lang.reflect.*;
// 调用处理器
public class LoggerHandler implements InvocationHandler {

    private Object target;

    public LoggerHandler(Object target){
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) 
    throws Throwable{
        System.out.println("START log");  // 增加新功能
        Object result = method.invoke(target, args);  // 类中的方法调用
        System.out.println("END log");  // 增加新功能
        return result;
    }
}

import java.lang.reflect.Proxy;

public class ProxyJDKTest {

    public static void main(String[] args){

        IHello hello = new Hello();

        // 直接调用,不使用代理
        hello.sayHello();

        // 使用代理 by JDK
        LoggerHandler handler = new LoggerHandler(hello);  // 创建调用处理器
        IHello helloNew = (IHello) Proxy.newProxyInstance(  // 创建代理类
                Thread.currentThread().getContextClassLoader(),
                hw.getClass().getInterfaces(),
                handler
        );
        helloNew.sayHello();  // 使用代理类调用

    }
}

查看代理类(helloNew.getClass()):

import sun.misc.ProxyGenerator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;

public class ProxyJDKTest {

    public static void main(String[] args) throws IOException {
        IHello hello = new Hello();
        // use proxy by JDK
        LoggerHandler handler = new LoggerHandler(hello);
        IHello helloNew = (IHello) Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                hello.getClass().getInterfaces(),
                handler
        );
        helloNew.sayHello();
        ProxyJDKTest.saveClass(helloNew.getClass());  // 保存代理类的class文件
    }

    public static void saveClass(Class cls) throws IOException {
        byte[] classFile = ProxyGenerator.generateProxyClass(cls.getName(), cls.getInterfaces());
        File file =new File(cls.getName() + ".class");
        FileOutputStream fos =new FileOutputStream(file);
        fos.write(classFile);
        fos.flush();
        fos.close();
    }
}

IntelliJ IDEA下查看生成的 $Proxy0.class,自动翻译为源码(去掉try/catch):

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

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

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

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

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

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

    static {
        ……
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("proxy.proxyJDK.IHello").getMethod("sayHello");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        ……
    }
}

super.h 是 Proxy类的 protected InvocationHandler h;

可见,代理类

无关于:被代理类、handler(InvocationHandler的具体实现)

有关于:接口数组(动态地实现了接口,实现就是转发给 handler,即调动super.h)

对被代理类调用,由 handler 来管理。

JDK动态代理 总结:

实现机制:代理类主要是实现了接口,在接口的方法实现中,将调用转发给 handler;handler调用被代理类,且在调用前后增加新功能。

局限:只能为接口创建代理,返回的代理对象只能转换为某个接口类型。

不适用的情况:如果一个类没有接口,或希望代理代理的方法不是接口中定义的方法,那就不能使用 JDK动态代理了。

 

动态代理 by CGLib

通过 JDK 实现动态代理有个限制:原来的类的方法是实现的接口中的方法。也就是需要一个接口。

如果类没有实现接口呢?使用 CGLib。

先导入 com.springsource.net.sf.cglib-2.2.0.jar 命名为 net.sf.cglib。

public class Hello {
    public void sayHello(){
        System.out.println("Hello");
    }
}

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class LogInterceptor implements MethodInterceptor {

    public Object intercept(Object obj,
                            Method method,
                            Object[] args,
                            MethodProxy proxy) throws Throwable{
        System.out.println("START log");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("END log");
        return result;
    }
}

import net.sf.cglib.proxy.Enhancer;

public class proxyCGLibTest {
    public static void main(String[] args){
        // 不使用代理
        Hello hello = new Hello();
        hello.sayHello();

        // 使用代理 by CGLib
        Enhancer enhancer = new Enhancer(); // 创建一个增强器,用来在运行时生成类
        enhancer.setSuperclass(Hello.class); // 设置要继承的目标类
        enhancer.setCallback(new LogInterceptor()); // 设置 logInterceptor
        Hello helloNew = (Hello) enhancer.create(); // 生成新的类
        helloNew.sayHello(); // 调用代理类的方法
    }
}

CGLib动态代理 总结:

实现机制:代理类是被代理类的一个子类,重写了父类的所有 public非final 方法,改为调用 Callback中的相关方法(示例中的 intercept)

 

JDK动态代理与CGLib动态代理对比

JDK动态代理

面向的是一组接口。

动态地创建一个新类,实现这些接口。接口的具体实现是通过 自定义的InvocationHandler 实现的。

代理的是对象。

先有一个被代理的对象;自定义的InvocationHandler引用该对象,然后创建一个代理类和代理对象;客户端访问的是代理对象,代理对象再调用实际对象的方法。

CGLib动态代理

面向的是一个具体的类。

动态地创建一个新类,继承了被代理类。重写方法。

代理的是类。创建的对象只有一个。

 

在Python这种动态语言中,实现“动态代理”太容易了。

使用装饰器:

def proxy(func):
    def warp():
        print('START log')
        result = func()
        print('END log')
        return result
    return warp

@proxy
def say_hello():
    print('hello')

if __name__ == '__main__':
    say_hello()

 

在函数 say_hello上@proxy,相当于是 say_hello = proxy(say_hello)

等号右边:原来的函数say_hello作为参数传递给函数proxy,proxy的返回值是函数wrap。

等号以及左边:最后让say_hello变量引用wrap。也就是说函数say_hello名称不变,但函数体是wrap

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值