JDK与CGLIB动态代理复习笔记

 

目录

JDK动态代理(基于Proxy和InvocationHandler)

代理接口代码

代理接口实现类的代码

究其原理

Cglib动态代理(基于Enhancer和MethodInterceptor)

代理接口的代码

代理类的代码

究其原理

注意事项

动态代理无法重写final方法

cglib被代理类的构造方法会执行两次


JDK动态代理(基于Proxy和InvocationHandler)

Jdk动态代理可以代理接口和类,基于Proxy类提供的一些静态方法如Proxy.newInstance,同时Proxy也是所有其生成的代理类的父类。

jdk动态代理需要被代理者是个接口,或者是个接口的实现类,如果是一个纯类没实现任何接口,应考虑cglib动态代理,包括Spring官方文档AOP的代理机制中也如是说。

代理接口:被代理的接口,没有任何实现,jdk动态代理帮助其实现

代理接口实现类:被代理的接口,已有实现,但是jdk动态代理可以为实现的方法切面编程

代理接口代码

可以看到Engine没有实现类,jdk动态代理相当于帮它产生一个实现类,并实现了Engine接口中的方法,invoke方法相当于帮你实现接口中的方法。

public interface Engine {
    void fire();
    String woo();
}



public class TestEngineJdkProxy{
    public static void main(String[] args) {
        Engine engine = (Engine) Proxy.newProxyInstance(Engine.class.getClassLoader(), new Class[]{Engine.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("123");
                if ("woo".equals(method.getName()))
                    return "666";
                else
                    return "777";
            }
        });
        System.out.println(engine.woo());
    }
}

代理接口实现类的代码

可以看到EngineImpl为Engine接口实现类,jdk动态代理为EngineImpl中的方法增加了逻辑,类似切面编程。

注意,如果InvocationHandler中不慎使用了method.invoid(proxy,args) 而非 method.invoke(engine,args) ,则会造成递归直到报错。

public interface Engine {
    void fire();
    String woo();
}


public class EngineImpl implements Engine {

    private String name;

    public EngineImpl(){}

    public EngineImpl(String name) {
        this.name = name;
    }

    @Override
    public void fire() {
        System.out.println("this is fire");
    }

    @Override
    public String woo() {
        System.out.println("this is woo");
        return "woo~";
    }

    public void name(){
        System.out.println(name);
    }
}


public class TestEngineImplJdkProxy {
    public static void main(String[] args) {
        Engine engine = new EngineImpl();
        Engine proxy = (Engine) Proxy.newProxyInstance(engine.getClass().getClassLoader(), engine.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if ("fire".equals(method.getName()))
                    System.out.println("23333~");
                else
                    System.out.println("woo233333~");
                return method.invoke(engine,null);
            }
        });
        proxy.fire();
        System.out.println("==========");
        System.out.println(proxy.woo());
    }
}

究其原理

 jdk动态代理实际帮助被代理的接口生成了一个实现类,帮助被代理的类生成一个代理类。通过反编译发现这个类叫$Proxy0.class:
    class类定义为:
    public final class $Proxy0 extends Proxy implements Interface {
        public $Proxy0(InvocationHandler paramInvocationHandler) {  
            super(paramInvocationHandler);  
        } 
        
        // 这里可以发现,fire()已经被代理类重写了,他会先去调用InvocationHandler的invoke方法,再将结果返回
        public final void fire() {  
            try {  
                this.invocationHandler.invoke(this, m3, null);  
                return;  
            }catch (Error|RuntimeException localError) {  
                throw localError;  
            }catch (Throwable localThrowable) {  
                throw new UndeclaredThrowableException(localThrowable);  
            }  
      } 
    }

 

Cglib动态代理(基于Enhancer和MethodInterceptor)

cglib底层是使用一个小而快的字节码处理框架ASM,转换字节码并生成新的类。核心基于Enhancer和MethodInterceptor。

代理接口的代码

cglib代理接口相当于帮你实现接口逻辑,返回接口方法的返回值

public class TestCGlibIntf {
    public static void main(String[] args) {
       Enhancer enhancer = new Enhancer();
       enhancer.setSuperclass(Engine.class);
       enhancer.setInterfaces(new Class[]{Engine.class});
       enhancer.setCallback(new MethodInterceptor() {
           @Override
           public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
               // 此时被代理的是接口,所以o = engine$$EnhancerByCglib = null
               System.out.println("intercept方法相当于帮你完成接口中方法的实现逻辑");
               if ("fire".equals(method.getName()))
                   System.out.println("fire---------------");
               else
                   return "7777";
               return null;
           }
       });
        Engine engine = (Engine) enhancer.create();
        System.out.println(engine.woo());
        System.out.println("------------------------");
        engine.fire();
    }
}

代理类的代码

cglib动态代理类相当于对类的方法进行且面编程,返回代理类本身

public class TestCglibImpl {
    public static void main(String[] args) {
        Engine engine = new EngineImpl();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(engine.getClass());
        enhancer.setInterfaces(engine.getClass().getInterfaces());
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("before--------------");
                Object invoke = method.invoke(engine, null);
                System.out.println("after----------------");
                return o;
            }
        });
        Engine engineProxy = (Engine) enhancer.create();
        engineProxy.fire();
    }
}

究其原理

其实也是类似于jdk动态代理,通过字节码框架ASM生成子类,重写源类方法,在重写方法中先调用MethodInterceptor的intercept方法,返回代理对象

注意事项

动态代理无法重写final方法

假如我有个Urine类包含final和非final方法,并使用cglib进行动态代理,尝试调用代理对象的final方法,会发现它并没有被拦截重写

cglib被代理类的构造方法会执行两次

同样是上方cglib案例中的Urine类,在构造方法中打印日志,会发现将打印两次,而Spring4.0以后只会调用一次,是因为使用了Objesenis库,待探究。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值