码迷,mamicode.com
首页 > 编程语言 > 详细

Java设计模式之动态代理

时间:2019-08-08 21:22:32      阅读:133      评论:0      收藏:0      [点我收藏+]

标签:stat   创建   sam   with   设计模式   cep   ESS   创建对象   实例   

动态代理是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设计模式之动态代理

标签:stat   创建   sam   with   设计模式   cep   ESS   创建对象   实例   

原文地址:https://www.cnblogs.com/GaryZz/p/11323552.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!