Java中两种动态代理的使用及原理

Java中两种动态代理:

1、JDK自带的动态代理(mybatis使用的是JDK自带的 包路径:java.lang.reflet)
2、CGLib提供的动态代理

代理模式:
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性。

动态代理:
代理类在程序运行时创建的代理方式被称为动态代理
动态代理是在运行时根据我们在Java代码中的“指示”动态生成的。
相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,

1、JDK自带的动态代理
先实现一个接口和具体的实现类
A.接口:

public interface IUser {
    void talk();
}

B.实现类

public class User implements IUser {
    @Override
    public void talk() {
        System.out.println("doing User.talk");
    }
}

C.创建一个实现InvocationHandler这个接口的类

 public class UserProxy implements InvocationHandler {
     private Object object;
 
     public UserProxy(Object object){
         super();
         this.object = object;
     }
      /**
       * 实现InvocationHandler接口要重写invoke方法,方法的三个参数分别:
      * proxy:就是动态代理生成的代理类对象
      * method:就是调用的方法
      * args:表示该方法的参数
      */
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         System.out.println("doing UserProxy.invoke");
         method.invoke(object, args);
         System.out.println("doing UserProxy.invoke end");
         return null;
     }
}

D.在main函数中创建代理对象,通过该对象,调用委托类的方法,每个方法的调用JVM都会给我们调用上面实现

InvocationHandler接口的类对象的invoke方法
 public static void main(String[] args) {
     IUser user = (IUser) Proxy.newProxyInstance
                         (ProxyDemo.class.getClassLoader(), 
                         new Class[]{IUser.class}, 
                         new UserProxy(new User()));
     user.talk();
 }

输出结果:

doing UserProxy.invoke
doing User.talk
doing UserProxy.invoke end

JDK动态代理原理
mian中调用了代码user.talk()方法后,JVM会帮助自动实现invoke调用呢?
在上面main添加一个监控

 public static void main(String[] args) {
     //可以查看JDK生成的动态代理类
     System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
     IUser user = (IUser) Proxy.newProxyInstance(ProxyDemo.class.getClassLoader(), new Class[]{IUser.class}, new UserProxy(new User()));
     user.talk();
 }

生成一个$Proxy().class文件,代理类的源码如下

public final class $Proxy0 extends Proxy implements IUser
 

$Proxy()的定义,确实实现了IUser接口,和代理模式下的代理类完全一样

因此当user.talk()调用时,根据JAVA的多态原理,调用的应该是代理对象 P r o x y ( ) 的 t a l k ( ) 方 法 , 看 Proxy()的talk()方法,看 Proxy()talk()Proxy()重写的talk方法

public final void talk() throws  {
         try {
             super.h.invoke(this, m3, (Object[])null);
         } catch (RuntimeException | Error var2) {
             throw var2;
         } catch (Throwable var3) {
             throw new UndeclaredThrowableException(var3);
         }
 }

当调用IUser接口的引用变量user调用talk的时候,其实调用代理对象重写的talk方法,用你传入的InvocationHandler对象,并调用了他的invoke方法

2、CGLib动态代理
代理提供了一种可扩展的机制来控制被代理的对象的访问,就是在对象访问的时候加了一层封装,JDK从1.3版本开始提供了上述动态代理机制,使用简单,缺点显而易见,需要目标对象实现一个或者多个接口
当代理没有接口的类,此时Proxy和InvocationHandler机制不能使用了,此时可以使用CGLib库
CGLib采用非常底层的字节码技术,原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术,拦截所有父类方法的调用,顺势织入横切逻辑,JDK动态代理与CGLib动态代理均是实现Spring AOP的基础

A.创建父类

public class CGLibSuper {
    public void doing() {
        System.out.println("CGLibSuper.doing");
    }
}

B.创建子类的方法与代理的方法

/**
 * 该类实现了创建子类的方法与代理的方法,getProxy(CGLibSuper.class)方法通过入参即父类的字节码,
 * 通过扩展父类的class来创建代理对象,intercept()方法拦截所有目标类方法的调用,Object表示目标类的实例,
 * method为目标类方法的反射对象,objects为方法的动态入参,methodProxy为方法代理
 * methodProxy.invokeSuper(o, objects)通过代理类来调用父类的方法
 */
public class CGLibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    public <T> T getProxy(Class<T> clazz) {
        //设置需要创建子类的类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        //通过字节码结束动态的创建子类实例
        return (T)enhancer.create();
    }

    //实现MethodInterceptor
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("CGLibProxy.intercept");
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("CGLibProxy.intercept end");
        return o1;
    }

}

C.Main

public static void main(String[] args) {
    CGLibProxy cgLibProxy = new CGLibProxy();
    CGLibSuper proxy = cgLibProxy.getProxy(CGLibSuper.class);
    proxy.doing();
}

CGLib参考文献:https://blog.csdn.net/xiaohai0504/article/details/6832990

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值