Java设计模式之动态代理

Posted garyzz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java设计模式之动态代理相关的知识,希望对你有一定的参考价值。

动态代理是IOC的核心,理解动态代理对于IOC的学习很有帮助。

学习动态代理之前,必须要先有反射的知识。所以我们从反射开始,一步步剖析

java中的反射

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

注意,反射是在运行状态中这句话。这句话的理解是:当我们平时在使用一个类的时候,我们一般的方式是:Apple a = new Apple;这种方式。我们必须先在堆中new一个对象,并且类加载器会将此对象放到运行时内存中,比如对象放到了堆,对象的信息,引用等放到了方法区,此时的我们是知道这个对象的类型等等信息的。但是,当我们一开始不知道我们要用哪个类的时候就不能通过new来创建具体的对象了。这时候就可以通过反射来生成一个对象。

因为我们今天主要要看到是动态代理,所以,只说一些跟动态代理有关的。就是在反射的时候调用某个类的方法。

package net.xsoftlab.baike;
import java.lang.reflect.Method;
public class TestReflect 
    public static void main(String[] args) throws Exception 
        Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
        // 调用TestReflect类中的reflect1方法
        Method method = clazz.getMethod("reflect1");
        method.invoke(clazz.newInstance());
        // Java 反射机制 - 调用某个类的方法1.
        // 调用TestReflect的reflect2方法
        method = clazz.getMethod("reflect2", int.class, String.class);
        method.invoke(clazz.newInstance(), 20, "张三");
        // Java 反射机制 - 调用某个类的方法2.
        // age -> 20. name -> 张三
    
    public void reflect1() 
        System.out.println("Java 反射机制 - 调用某个类的方法1.");
    
    public void reflect2(int age, String name) 
        System.out.println("Java 反射机制 - 调用某个类的方法2.");
        System.out.println("age -> " + age + ". name -> " + name);
    

这段代码应该对于学习了反射的人不陌生。

其中,我们可以看到核心的几句

        Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
        // 调用TestReflect类中的reflect1方法
        Method method = clazz.getMethod("reflect1");
        method.invoke(clazz.newInstance());
        // Java 反射机制 - 调用某个类的方法1.
        // 调用TestReflect的reflect2方法
        method = clazz.getMethod("reflect2", int.class, String.class);
        method.invoke(clazz.newInstance(), 20, "张三");

先获取到某个类的Class对象。这个Class对象是在类加载器初次加载某个类的时候就生成了的。所以可以通过getClass或者forName来获取到。

接下来我们找到这个class代表的对象的某一个method,这个方法可以通过方法名+参数类型获得。

记住这里的核心的代码;

动态代理

这里只研究动态代理的原理,不解释动态代理到底是干嘛的;

先看一段经典的动态代理代码。

public interface Inter 
    void doSomething();
public class RealObj implements Inter 
    @Override
    public void doSomething() 
        System.out.println("real obj func");
    
public class ProxyHandler implements InvocationHandler 
    public ProxyHandler(Object object) 
        this.object = object;
    

    private Object object;
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
        System.out.println("方法的增强");
        return method.invoke(object,args);
    
public class Main 
    public static void main(String[] args) 
        RealObj realObj = new RealObj();
        Inter proxy = (Inter) Proxy.newProxyInstance(Inter.class.getClassLoader(),new Class[]Inter.class,new ProxyHandler(realObj));
        proxy.doSomething();

    

运行main之后的结果:

方法的增强
real obj func

Process finished with exit code 0

这里是个简单的jdk动态代理的例子。动态代理旨在不改变现有方法的情况下“增强”现有的方法。

在这里可以看到一句熟悉的话:

method.invoke(object,args);

这不就是我们反射中使用的吗。所以说,要理解动态代理必须跟反射合起来理解。

我们从main方法开始一步步分析动态代理。

在main方法中比较重要的一句话是:

Inter proxy = (Inter) Proxy.newProxyInstance(Inter.class.getClassLoader(),new Class[]Inter.class,new ProxyHandler(realObj));

这里,使用了Proxy类的静态方法newProxyInstance()这个方法的如下:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

这个方法的参数需要:类加载器、接口数组、还有一个InvocationHandler实例

我认为要理解一个方法需要搞清楚其每个参数都是用来干嘛的,这样会很容易理解一个方法的作用,以及为什么要这么设计这个方法。

所以我们先看这个方法的官方注释:

/**
 * Returns an instance of a proxy class for the specified interfaces
 * that dispatches method invocations to the specified invocation
 * handler.
 *
 * <p>@code Proxy.newProxyInstance throws
 * @code IllegalArgumentException for the same reasons that
 * @code Proxy.getProxyClass does.
 *
 * @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
 */

可以看到,这个方法是返回一个实现了某个接口的代理类,并且将一个分发一个method invocation给一个特定的handler。

参数:loarder:用来加载代理类

interfaces:让这个代理类要去实现的接口

h:一个用来分发激活方法的invocation handler

对于一个代理类而言,首先,我们要“有”这个代理类才可以,这里的类加载器就是用来加载这个代理类或者说生成这个代理类的。然后,我们有了一个代理类,但是怎么知道这个代理类是代理哪个类的呢?这时候就去实现被代理类实现了的接口。最后,我们的代理类是用来干什么的?是用来增强被代理类的。具体是增强被代理类的某个方法。所以,我们就需要一个Invocation handler来“定位”这个被代理类的方法。这里的定位其实就是反射的方式,去找到这个具体的方法。因为我们在反射的过程中,需要执行method.invoke(object,args);

再想一遍这个过程:

首先要有被代理类的代理类。也就是创建这个代理类对象。这里的创建对象是采用的Class<?> cl = getProxyClass0(loader, intfs);这个来创建的。再通过反射获取到这个Class的构造方法再去创建代理类。生成的代理类再去执行InvocationHandler的invoke,但是这里执行的是接口的某一个方法,最终执行的是被代理类的某个方法并且在invoke中增强了这个方法。

写着写着感觉自己讲的有很多矛盾的地方,就先留着吧。等再变得厉害一点重新回来解决这个问题。

以上是关于Java设计模式之动态代理的主要内容,如果未能解决你的问题,请参考以下文章

浅谈-Java设计模式之动态代理

设计模式之Jdk动态代理

Java之动态代理简介

Java设计模式之动态代理

#yyds干货盘点# 设计模式之代理模式:动态代理

java设计模式之代理模式