Skip to content

Latest commit

 

History

History
138 lines (94 loc) · 3.96 KB

4.2.3描述动态代理的几种实现方式,分别说出相应的优缺点.md

File metadata and controls

138 lines (94 loc) · 3.96 KB

问题描述:描述动态代理的几种实现方式,分别说出相应的优缺点

参考答案:

首先我们要区分两个含义上的静态代理和动态代理。

从设计模式上来讲:静态代理和动态代理都属于代理模式。

静态代理模式下,代码本身的适用性受到局限,我们可以说是一种"硬编码"的方式:代理类通过构造函数引用**特定的被代理的类**,并在执行被代理类的方法时执行一些代理逻辑,达到扩展的目的,因此静态代理的扩展成本较高,不够具有通用性,而且一定程度的通用必须依赖接口。


为了解决静态代理的通用性问题,产生了动态代理模式,动态代理的实现底层我们不会自己去写,通常是使用已有的动态代理技术,在Java生态中,目前普遍使用的是JDK自带的代理和CGLib提供的类库。

JDK代理

代码示例

public class JDKProxy implements InvocationHandler {
    private IPerson target;
    public IPerson getInstance(IPerson person) {
        this.target = person;
        Class<? extends IPerson> targetClass = target.getClass();
        
        return (IPerson) Proxy.newProxyInstance(targetClass.getClassLoader(),targetClass.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        this.before();
        Object invoke = method.invoke(this.target, args);
        this.after();
        return invoke;
    }

    private void before() {
        log.info("洗手");
    }

    private void after() {
        log.info("收拾");
    }
}
public class LiSiBaby implements IPerson{

    @Override
    public void eat() {
        log.info("大口吃饭");
    }
}

测试

JDKProxy jdkProxy = new JDKProxy();
IPerson instance = jdkProxy.getInstance(new LiSiBaby());
instance.eat();

/**
 * design_model.proxy_model.JDKProxy - 洗手
 * design_model.proxy_model.LiSiBaby - 大口吃饭
 * design_model.proxy_model.JDKProxy - 收拾
 */

可见JDK动态代理的强大之处在于只要规范了接口定义,就可以动态扩展所有实现类,而不需要每个去单独编码。

CGlib代理

代码示例

public class CGlibProxy implements MethodInterceptor {

    public Object getInstance(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        this.before();
        Object invokeSuper = methodProxy.invokeSuper(o, objects);
        this.after();
        return invokeSuper;
    }


    private void before() {
        log.info("洗手");
    }

    private void after() {
        log.info("收拾");
    }
}

测试

CGlibProxy cGlibProxy = new CGlibProxy();
LiSiBaby instance = (LiSiBaby) cGlibProxy.getInstance(LiSiBaby.class);
instance.eat();

/**
 * design_model.proxy_model.CGlibProxy.CGlibProxy - 洗手
 * design_model.proxy_model.LiSiBaby - 大口吃饭
 * design_model.proxy_model.CGlibProxy.CGlibProxy - 收拾
 */

当然了,CGlib并不依赖接口,你可以传入任意对象,并对其进行扩展。

对比:

(1)JDK动态代理实现了被代理对象的接口,CGLib动态代理继承了被代理对象。

(2)JDK动态代理和CGLib动态代理都在运行期生成字节码,JDK动态代理直接写Class字节码,CGLib动态代理使用ASM框架写Class字节码。CGLib动态代理实现更复杂,生成代理类比JDK动态代理效率低。

(3)JDK动态代理调用代理方法是通过反射机制调用的,CGLib动态代理是通过FastClass机制(索引分配直接调用)直接调用方法的,因此CGLib动态代理的执行效率更高。

开发中,我们不必太过纠结性能问题,可以根据自己的实际需求灵活选择。