Java基础-动态代理

一、代理模式简介

给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。

根据代理类的创建时机和创建方式的不同,可以将其分为静态代理和动态代理两种形式:

在程序运行前即编译时就存在的代理类是为静态代理
在程序运行时根据需要动态创建代理类及其实例为动态代理

静态代理,必需手动创建N个代理类,这显然让人相当不爽。

动态代理则可以简单地为各个主题类分别生成代理类,共享“预处理,后处理”功能,这样可以大大减小程序规模,这也是动态代理的一大亮点。

在动态代理中,代理类是在运行时期生成的。因此,相比静态代理,动态代理可以很方便地对委托类的相关方法进行统一增强处理,如添加方法调用次数、添加日志功能等等

 

二、JDK动态代理机制的相关类与接口

java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入被监控对象隶属的类文件在内存中真实地址、被监控对象隶属的类文件实现接口以及InvocationHandler通知对象便可为目标接口生成代理类及代理对象。

// 方法 1: 该方法用于获取指定代理对象所关联的

InvocationHandler static InvocationHandler getInvocationHandler(Object proxy)

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象

static Class getProxyClass(ClassLoader loader, Class[] interfaces)

// 方法 3:该方法用于判断指定类是否是一个动态代理类

static boolean isProxyClass(Class cl)

// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例

static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h)

java.lang.reflect.InvocationHandler:该接口包含一个invoke方法,通过该方法实现对委托类的代理的访问,是代理类完整逻辑的集中体现,包括要切入的增强逻辑和进行反射执行的真实业务逻辑。

Object invoke(Object proxy, Method method, Object[] args)

该方法是代理类完整逻辑的集中体现。在被监控行为将要执行时,会被JVM拦截。被监控行为和行为实现方法会被作为参数输送invoke,通常通过反射完成对具体角色业务逻辑的调用,并对其进行增强。
第一个参数既是代理类实例。
第二个参数是被调用的方法对象。
第三个方法是调用参数。

java.lang.ClassLoader:类加载器类,负责将类的字节码装载到Java虚拟机中并为其定义类对象,然后该类才能被使用。Proxy静态方法生成动态代理类同样需要通过类加载器来进行加载才能使用,它与普通类的唯一区别就是其字节码是由JVM在运行时动态生成的而非预存在于任何一个.class 文件中。

 

代码实例:

public void proxyForPeople(){
    User user = new User();
    People userProxy = (People) Proxy.newProxyInstance(user.getClass().getClassLoader(),
            user.getClass().getInterfaces(), (proxy, methods, args) -> {
                if(methods.getName().equals("toExcute")){
                    System.out.println("preparing......");
                    return methods.invoke(user, args);
                }
                if(methods.getName().equals("getStatus")){
                    System.out.println("status types [coding, rest, learning]");
                    return methods.invoke(user, args);
                }
                return null;
            });

    userProxy.toExcute();
    userProxy.getStatus();
}

 

三、动态代理的原理

          其实大概就是把接口复制出来,通过这些接口和类加载器,拿到这个代理类cl。然后通过反射的技术复制拿到代理类的构造函数(这部分代码在Class类中的getConstructor0方法),最后通过这个构造函数new个一对象出来,同时用InvocationHandler绑定这个对象。

 

四、实现动态代理的方式

了让生成的代理类与目标对象(真实主题角色)保持一致性,从现在开始将介绍以下两种最常见的方式:

    通过实现接口的方式 -> JDK动态代理

    通过继承类的方式 -> CGLIB动态代理

JDK代理需要一组需要实现的接口,然后通过这些接口获取构造方法,用这个构造方法和InvocationHandler,实例化一个对象出来。所以JDK的方式是基于接口的。

而CGLIB的代理是基于类的,用目标类生成一个子类,子类重写父类的方法,从而达到动态代理的效果。

一般情况下使用jdk动态代理,如果目标对象的代理至少实现了一个接口,那么就用JDK动态代理,所有由目标对象实现的接口将全部都被代理。如果目标对象没有实现任何接口,那么就用CGLIB代理。

 

五、动态代理的好处

    比较灵活,可以在运行的时候才切入改变类的方法,而不需要预先定义它。

动态代理一般我们比较少去手写,但我们用得其实非常多。在Spring项目中用的注解,例如依赖注入的@Bean、@Autowired,事务注解@Transactional等都有用到,换言之就是Srping的AOP(切面编程)。

这种场景的使用是动态代理最佳的落地点,可以非常灵活地在某个类,某个方法,某个代码点上切入我们想要的内容,就是动态代理其中的内容。

 

 

参考:

https://www.jianshu.com/p/95970b089360

https://www.jianshu.com/p/4dcc74b63f1c

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章