AOP底层原理之动态代理

Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类.

所以AOP的底层原理实现,实际就是利用动态代理来实现拦截的.

Java代理模式和静态代理

Proxy是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,Proxy是代理的意思,我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理.

设计模式中定义: 为其他对象提供一种代理以控制对这个对象的访问.

为什么要使用Proxy?

1.授权机制 不同级别的用户对同一对象拥有不同的访问权利,如Jive论坛系统中,就使用Proxy进行授权机制控制,访问论坛有两种人:注册用户和游客(未注册用户),Jive中就通过类似ForumProxy这样的代理来控制这两种用户对论坛的访问权限.

2.某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动.
举例两个具体情况:
(1)如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很迅速,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片.

(2)如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象.

总之原则是,对于开销很大的对象,只有在使用它时才创建,这个原则可以为我们节省很多宝贵的Java内存. 所以,有些人认为Java耗费资源内存,我以为这和程序编制思路也有一定的关系.

下面举例说明:
假设有这样一个接口:

package com.dynamicproxy;
public interface IHello {
    /**
     * 业务方法
     * @param str
     */
    void sayHello(String str);
}

我们正常的实现是:

package com.dynamicproxy;
public class Hello implements IHello{
    public void sayHello(String str) {
        System.out.println("hello "+str);
    }
}

那么,我们想要运用代理模式,同时在调用sayHello的时候,前面和后面各打印一条log,怎么实现呢?
我们需要实现一个代理类:

package com.dynamicproxy;
public class ProxyHello implements IHello{    
    private IHello hello;    
    public ProxyHello(IHello hello) {
        super();
        this.hello = hello;
    }
    public void sayHello(String str) {
        Logger.start();//添加特定的方法
        hello.sayHello(str);
        Logger.end();
    }

}

使用的时候,直接用代理类来代理原来的Hello类:

package com.dynamicproxy;
public class Test {
    public static void main(String[] args) {
        IHello hello = new ProxyHello(new Hello());
      hello.sayHello("你好");    
    }
}

我们实际已经实现了代理模式,但是这是一个静态代理的模式, 试想,我们想要对程序中所有的方法都加类似的log呢,我们是不是需要写无数的这种代理类来实现呢? 那样肯定不好.

动态代理

现在,引入动态代理,只用一个代理类,就实现.

在jdk1.3以后,jdk跟我们提供了一个API java.lang.reflect.InvocationHandler的类, 这个类可以让我们在JVM调用某个类的方法时动态的添加操作.
动态代理实现主要是实现InvocationHandler,并且将目标对象注入到代理对象中,利用反射机制来执行目标对象的方法。

还是基于上面的例子:

我们实现动态代理

package com.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynaProxyHello implements InvocationHandler{
    
    private Object target;//目标对象
    /**
     * 通过反射来实例化目标对象
     * @param object
     * @return
     */
    public Object bind(Object object){
        this.target = object;
        return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);
    }
    
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result = null;
        Logger.start();
        //通过反射机制来运行目标对象的方法
        result = method.invoke(this.target, args);
        Logger.end();
        return result;
    }

    
}

这样,我们可以动态的代理所有的类:

package com.dynamicproxy;
public class DynaTest {
    public static void main(String[] args) {
        IHello hello = (IHello) new DynaProxyHello().bind(new Hello());
        hello.sayHello("你好");

         ICat cat = (ICat) new DynaProxyHello().bind(new Cat());
        cat.sayHello("miao");
    }
}

快来拷贝代码实际跑一跑吧.

点赞