zl程序教程

您现在的位置是:首页 >  后端

当前栏目

框架源码常客JDK动态代理模式

2023-09-14 09:14:06 时间

目录

一、前言

二、开发中使用的组件及其方法

三、实验数据

1、Subject接口

2、Subject接口的实现类RealSubject

3、InvocationHandler的实现类,Cache调用处理器

4、测试类


一、前言


    JDK动态代理,在源码的世界里扮演的角色格外重要,如MyBatis的日志模块、binding模块等,Spring的AOP等。使用代理的一个重要目标是控制对被代理类的访问,可以在代理类中做一些前置/后置处理,如:缓存日志事务权限等等。使用JDK动态代理的前提是:被代理类必须是一个接口或者其实现类。如果被代理类不是接口或者接口实现类,而你又想使用动态代理,可考虑使用CGLIBJavassist技术。这是一项硬技能,好的Java工程师、架构师都必须具备的知识。

二、开发中使用的组件及其方法


    开发中用到的JDK动态代理组件为InvocationHandler接口以及Proxy类,它们的职责如下表:

类  类的职责
Proxy负责生成代理类并关联调用处理器(InvocationHandler接口的实现类)
InvocationHandler接口负责处理代理的逻辑,如:缓存日志事务权限等等

1、Proxy生成的代理类是不处理逻辑的,抽象又很真实你连代理类体都没看到如何写逻辑,但是它会委派给调用处理器invoke()方法处理逻辑,这也就是为什么多了个InvocationHandler接口及其实现类的原因。我们都知道,要调用一个类的非静态方法,一般都得经过它的实例去调用方法。好办,Proxy生成的代理类不处理逻辑,而委托给调用处理器的invoke()方法处理,那么要调用调用处理器的方法,你至少要拿到它的实例。故Proxy的newProxyInstance()方法(这个方法是开发中经常用到的)中就得把调用处理器的实例传入进来了。我们来看看源码中这个方法有哪些参数:
 


    /**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     * @param   loader the class loader to define the proxy class
     * @param   interfaces the list of interfaces for the proxy class
     *          to implement
     * @param   h the invocation handler to dispatch method invocations to
     * @return  a proxy instance with the specified invocation handler of a
     *          proxy class that is defined by the specified class loader
     *          and that implements the specified interfaces
     */
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {

方法职责是创建代理类实例,并且代理类关联接管处理逻辑任务的调用处理器。

loader:类加载器,负责加载代理类。

interfaces:数组类型,表示提供了一组接口对象数组,那么代理类就实现了这些接口的所有方法并可调用。

h:InvocationHandler接口类型,代理类会通过InvocationHandler实现类的实例去调用invoke()方法处理逻辑。

2、上面已经说了,代理类会委派给InvocationHandler接口的实现类去处理任务,任务逻辑必经invoke()方法,那看看源码中它是什么样子的:
 

 /**
     * Processes a method invocation on a proxy instance and returns
     * the result.  This method will be invoked on an invocation handler
     * when a method is invoked on a proxy instance that it is
     * associated with.
     *
     * @param   proxy the proxy instance that the method was invoked on
     *
     * @param   method the {@code Method} instance corresponding to
     * the interface method invoked on the proxy instance.  The declaring
     * class of the {@code Method} object will be the interface that
     * the method was declared in, which may be a superinterface of the
     * proxy interface that the proxy class inherits the method through.
     *
     * @param   args an array of objects containing the values of the
     * arguments passed in the method invocation on the proxy instance,
     * or {@code null} if interface method takes no arguments.
     * Arguments of primitive types are wrapped in instances of the
     * appropriate primitive wrapper class, such as
     * {@code java.lang.Integer} or {@code java.lang.Boolean}.
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

proxy:被代理的类。

method:调用时被代理的类的方法的Method对象。

args:调用时被代理的类的方法的参数。

三、实验数据

1、Subject接口


public interface Subject {
 
    String doSomething();
}

2、Subject接口的实现类RealSubject


public class RealSubject implements Subject {
 
    @Override
    public String doSomething() {
        System.out.println("我是真实的Object");
        return "我是RealSubject";
    }
}

3、InvocationHandler的实现类,Cache调用处理器


package com.ceam.designer.proxy.jdk;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
public class Cache implements InvocationHandler {
 
    /**
     * 这里封装真实类一般可以是:Object类型、接口类型、Class类型
     */
//    private Object object;
//
//    Cache(Object object) {
//        this.object = object;
//    }
 
    private Subject object;
 
    /**
     * 初始化字段,method.invoke(object, args)中会用到该字段
     * 
     * @param object 真实对象
     */
    Cache(Subject object) {
        this.object = object;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("----Before handler----");
 
        // 调用真实对象的方法,返回值result
        Object result = method.invoke(object, args);
        System.out.println((String)result);
 
        System.out.println("----After handler----");
 
        // 返回返回值
        return result;
    }
 
}

4、测试类


package com.ceam.designer.proxy.jdk;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
 
public class JDKProxyTest {
 
    public static void main(String[] args) {
        // 声明父类创建子类
        Subject subject = new RealSubject();
        // 封装真实的类
        InvocationHandler cache = new Cache(subject);
        // 生成代理实例,关联调用处理器
        /**
         * 关于入参的第一个第二个参数,一般是:第一个为接口的类类加载器,第二个为new Class[]{接口的Class}
         */
        Subject proxy = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
                new Class[]{Subject.class}, cache);
        String result = proxy.doSomething();
        System.out.println("返回结果:" + result);
 
        System.out.println("***********************************");
 
        Subject proxy2 = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
                subject.getClass().getInterfaces(), cache);
        proxy2.doSomething();
    }
 
}

控制台打印:


----Before handler----
我是真实的Object
我是RealSubject
----After handler----
返回结果:我是RealSubject
***********************************
----Before handler----
我是真实的Object
我是RealSubject
----After handler----
 
Process finished with exit code 0

实验不会说谎,多动动手去做做实验,加深印象。改变一些数据,你可能获得意外的惊喜噢!本篇文章是本人在研究MyBatis源码时总结出的一些结论,含金量饱满,分享给大家,可谓用心良苦。