java动态代理技术

Posted gavanwanggw

tags:

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

主要用来做方法的增强。让你能够在不改动源代码的情况下,增强一些方法,在方法运行前后做不论什么你想做的事情(甚至根本不去运行这种方法)。由于在InvocationHandler的invoke方法中,你能够直接获取正在调用方法相应的Method对象。详细应用的话。比方能够加入调用日志,做事务控制等。
另一个有趣的作用是能够用作远程调用,比方如今有Java接口,这个接口的实现部署在其他server上,在编写client代码的时候,没办法直接调用接口方法,由于接口是不能直接生成对象的,这个时候就能够考虑代理模式(动态代理)了,通过Proxy.newProxyInstance代理一个该接口相应的InvocationHandler对象,然后在InvocationHandler的invoke方法内封装通讯细节就能够了。详细的应用,最经典的当然是Java标准库的RMI。其他比方hessian,各种webservice框架中的远程调用,大致都是这么实现的。

在java的动态代理机制中。有两个重要的类或接口。一个是 InvocationHandler(Interface)、还有一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描写叙述的:

InvocationHandler:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

每个动态代理类都必需要实现InvocationHandler这个接口。而且每个代理类的实例都关联到了一个handler。当我们通过代理对象调用一个方法的时候,这种方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

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

我们看到这种方法一共接受三个參数。那么这三个參数分别代表什么呢?

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

proxy:  指代我们所代理的那个真实对象
method:  指代的是我们所要调用真实对象的某个方法的Method对象
args:  指代的是调用真实对象某个方法时接受的參数

假设不是非常明确,等下通过一个实例会对这几个參数进行更深的解说。

接下来我们来看看Proxy这个类:

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods. 

Proxy这个类的作用就是用来动态创建一个代理对象的类。它提供了很多的方法,可是我们用的最多的就是 newProxyInstance 这种方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.

这种方法的作用就是得到一个动态的代理对象,其接收三个參数。我们来看看这三个參数所代表的含义:

技术分享
public static Object newProxyInstance(ClassLoader loader, Class<?

>[] interfaces, InvocationHandler h) throws IllegalArgumentException loader:  一个ClassLoader对象。定义了由哪个ClassLoader对象来对生成的代理对象进行载入 interfaces:  一个Interface对象的数组,表示的是我将要给我须要代理的对象提供一组什么接口,假设我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了 h:  一个InvocationHandler对象。表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

技术分享

好了,在介绍完这两个接口(类)以后。我们来通过一个实例来看看我们的动态代理模式是什么样的:

首先我们定义了一个Subject类型的接口,为其声明了两个方法:

public interface Subject
{
    public void rent();
    
    public void hello(String str);
}

接着,定义了一个类来实现这个接口。这个类就是我们的真实对象。RealSubject类:

技术分享
public class RealSubject implements Subject
{
    @Override
    public void rent()
    {
        System.out.println("I want to rent my house");
    }
    
    @Override
    public void hello(String str)
    {
        System.out.println("hello: " + str);
    }
}
技术分享

下一步。我们就要定义一个动态代理类了,前面说个,每个动态代理类都必需要实现 InvocationHandler 这个接口,因此我们这个动态代理类也不例外:

技术分享
public class DynamicProxy implements InvocationHandler
{
    // 这个就是我们要代理的真实对象
    private Object subject;
    
    //    构造方法。给我们要代理的真实对象赋初值
    public DynamicProxy(Object subject)
    {
        this.subject = subject;
    }
    
    @Override
    public Object invoke(Object object, Method method, Object[] args)
            throws Throwable
    {
        //  在代理真实对象前我们能够加入一些自己的操作
        System.out.println("before rent house");
        
        System.out.println("Method:" + method);
        
        //    当代理对象调用真实对象的方法时。其会自己主动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        method.invoke(subject, args);
        
        //  在代理真实对象后我们也能够加入一些自己的操作
        System.out.println("after rent house");
        
        return null;
    }

}
技术分享

最后,来看看我们的Client类:

技术分享
public class Client
{
    public static void main(String[] args)
    {
        //    我们要代理的真实对象
        Subject realSubject = new RealSubject();

        //    我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        InvocationHandler handler = new DynamicProxy(realSubject);

        /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个參数
         * 第一个參数 handler.getClass().getClassLoader() 。我们这里使用handler这个类的ClassLoader对象来载入我们的代理对象
         * 第二个參数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口。表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
         * 第三个參数handler。 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
         */
        Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);
        
        System.out.println(subject.getClass().getName());
        subject.rent();
        subject.hello("world");
    }
}
技术分享

我们先来看看控制台的输出:

技术分享
$Proxy0
before rent house Method:
public abstract void com.xiaoluo.dynamicproxy.Subject.rent() I want to rent my house after rent house
before rent house Method:
public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String) hello: world after rent house
技术分享

我们首先来看看 $Proxy0 这东西,我们看到。这个东西是由 System.out.println(subject.getClass().getName()); 这条语句打印出来的,那么为什么我们返回的这个代理对象的类名是这种呢?

Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);

可能我以为返回的这个代理对象会是Subject类型的对象,或者是InvocationHandler的对象,结果却不是,首先我们解释一下为什么我们这里能够将其转化为Subject类型的对象?原因就是在newProxyInstance这种方法的第二个參数上。我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口。这个时候我们当然能够将这个代理对象强制类型转化为这组接口中的随意一个,由于这里的接口是Subject类型,所以就能够将其转化为Subject类型了

同一时候我们一定要记住。通过 Proxy.newProxyInstance 创建的代理对象是在jvm执行时动态生成的一个对象。它并非我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在执行是动态生成的一个对象。而且命名方式都是这种形式。以$开头,proxy为中。最后一个数字表示对象的标号

接着我们来看看这两句 

subject.rent();
subject.hello("world");

这里是通过代理对象来调用实现的那种接口中的方法。这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去运行,而我们的这个 handler 对象又接受了一个 RealSubject类型的參数。表示我要代理的就是这个真实对象。所以此时就会调用 handler 中的invoke方法去运行:

技术分享
public Object invoke(Object object, Method method, Object[] args)
            throws Throwable
    {
        //  在代理真实对象前我们能够加入一些自己的操作
        System.out.println("before rent house");
        
        System.out.println("Method:" + method);
        
        //    当代理对象调用真实对象的方法时,其会自己主动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        method.invoke(subject, args);
        
        //  在代理真实对象后我们也能够加入一些自己的操作
        System.out.println("after rent house");
        
        return null;
    }
技术分享

我们看到,在真正通过代理对象来调用真实对象的方法的时候,我们能够在该方法前后加入自己的一些操作,同一时候我们看到我们的这个 method 对象是这种:

public abstract void com.xiaoluo.dynamicproxy.Subject.rent()

public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)

正好就是我们的Subject接口中的两个方法,这也就证明了当我通过代理对象来调用方法的时候,起实际就是托付由其关联到的 handler 对象的invoke方法中来调用。并非自己来真实调用。而是通过代理的方式来调用的。

这就是我们的java动态代理机制








以上是关于java动态代理技术的主要内容,如果未能解决你的问题,请参考以下文章

Java动态代理学习Spring AOP基础之一

java动态代理技术

动态代理

JDK动态代理与CGLib动态代理

java中的动态代理机制

学习Spring必学的Java基础知识----动态代理