模拟AOP动态代理

 

动态代理(dynamic proxy) 

动态代理(如下称代理),利用Java的反射技术(Java Reflection),在运行时建立一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象)html

(Using Java Reflection to create dynamic implementations of interfaces at runtime)。java

代理的是对象是接口(Interfaces),不是类(Class),更不是抽象类。git

 

动态代理做用

解决特定问题:一个接口的实如今编译时没法知道,须要在运行时才能实现github

实现某些设计模式:适配器(Adapter)或修饰器(Decorator)编程

面向切面编程:如AOP in Spring.设计模式

 

建立动态代理

利用Java的Proxy类,调用Proxy.newProxyInstance(),api

而Proxy.newProxyInstance()方法有三个参数:数组

1. 类加载器(Class Loader);缓存

2. 须要实现的接口数组;oracle

3. InvocationHandler接口,全部动态代理类的方法调用,都会交由InvocationHandler接口实现类里的invoke()方法去处理。这是动态代理的要义所在。

下面是官方文档解释:

ublic 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. This method is equivalent to:

Proxy.getProxyClass(loader, interfaces).
         getConstructor(new Class[] { InvocationHandler.class }).
         newInstance(new Object[] { handler });

Proxy.newProxyInstance throws IllegalArgumentException for the same reasons that Proxy.getProxyClass does.

Parameters:

loader - the class loader to define the proxy class

interfaces - the list of interfaces for the proxy class to implement

h - the invocation handler to dispatch method invocations to

Returns:

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

Throws:

IllegalArgumentException - if any of the restrictions on the parameters that may be passed to getProxyClass are violated

NullPointerException - if the interfaces array argument or any of its elements are null, or if the invocation handler, h, is null

如何使用InvocationHandler接口

接口里有一个invoke()方法。基本的作法是,建立一个类,实现这个方法,利用反射在invoke()方法里实现需求:

public class MyInvocationHandler implements InvocationHandler{

  public Object invoke(Object proxy, Method method, Object[] args)
  throws Throwable {
    //do something "dynamic"
  }
}

 

invoke()方法也一样有三个参数:

1. 动态代理类的引用,一般状况下不须要它。但可使用getClass()方法,获得proxy的Class类从而取得实例的类信息,如方法列表,annotation等。

2. 方法对象的引用,表明被动态代理类调用的方法。从中可获得方法名,参数类型,返回类型等等。

3. args对象数组,表明被调用方法的参数。注意基本类型(int,long)会被装箱成对象类型(Interger, Long)。

动态代理模拟

//interface
public interface DynamicQuiz {
    void run();
}

//do something
public class DynamicProxyTest implements DynamicQuiz {
    @Override
    public void run() {
        System.out.println("this is a dynmic quiz");
    }
}

//proxy process
class DynamicProxy implements InvocationHandler {
    DynamicQuiz dynamicQuiz;

    DynamicProxy() {
    }
    DynamicQuiz bind(DynamicQuiz dynamicQuiz) {
        this.dynamicQuiz = dynamicQuiz;
        return (DynamicQuiz) Proxy.newProxyInstance(dynamicQuiz.getClass().getClassLoader(), dynamicQuiz.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("welcome");
        Object result = method.invoke(dynamicQuiz, args);
        System.out.println("see you agin");
        return result;
    }
}

//main 
public class Main {
    public static void main(String[] args) {
        DynamicQuiz dynamicQuiz = new DynamicProxyTest();
        DynamicQuiz quizeResult = new DynamicProxy().bind(dynamicQuiz);
        quizeResult.run();

    }
}

从上面的代码能够看出,对DynamicQuiz接口的调用,会交由Proxy的invoke方法处理,并在不改变run()的源代码下,新增了动态的逻辑(before running/after running),这正式AOP所作的。

深刻讲,Proxy.newInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)作了如下几件事.
(1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)建立代理类$Proxy.
$Proxy0类实现了interfaces的接口,并继承了Proxy类.
(2)实例化$Proxy0并在构造方法中把BusinessHandler传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值.

注意两个地方:

1.接口的run()会触发invoke(), 因此在proxy里面切不可将invoke()替换成(DynamicQuiz)poxy.run(),不然就形成死循环,形成栈溢出。

2.跟踪源码能够看到,程序进行了验证、优化、缓存、同步、生成字节码、显示类加载等操做,前面的步骤并非咱们关注的重点,而最后它调用了sun.misc.ProxyGenerator.generateProxyClass()方法完成生成字节码的动做,这个方法能够在运行时产生一个描述代理类的字节码byte[]数组。若是想看这个运行时产生的代理类中的内容,能够在main方法加以下这句代码:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

运行会生成一个名为"$Proxy).class"的代理class文件,反编译以后就能看见内容。

 

代码地址:https://github.com/wangtao1/dynamic

 

参考:

深刻理解java虚拟机第9章节

http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html

http://www.cnblogs.com/techyc/p/3455950.html

相关文章
相关标签/搜索