动态代理及代理模式

1.  什么是动态代理

关于动态代理,先引用JDK上的一段话

动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。 代理接口 是代理类实现的一个接口。代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口 InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke 方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。

    看了上面的一段话,想必你已经明白什么叫动态代理了吧。没明白?没关系,老实说,我也不明白,上面的一段话看了一半就看不下去了,实在没这个耐心看。不过还好,旁边就有个例子 来说明:

     InvocationHandler handler = new MyInvocationHandler(...);
        Class proxyClass = Proxy.getProxyClass(
            Foo.class.getClassLoader(), new Class[] { Foo.class });
        Foo f = (Foo) proxyClass.
            getConstructor(new Class[] { InvocationHandler.class }).
            newInstance(new Object[] { handler });

可以看到,通过一系列方法,最终创建出了一个Foo的实例。 

那么MyInvocationHandler是什么呢?正如jdk上所说,它实现了InvocationHandler接口,拥有一个invoke(Object proxy, Method method, Object[]args)方法。也就是说,它是通过反射的方式,调用了Foo接口的实例的具体实现方法。具体的调用,肯定发生在invoke里面了。

看到这里我们明白了吧,其实动态代理就是为其他对对象提供一种动态的可以在运行中改变的代理以控制对这个对象的访问。也就是说,在运行过程中,Foo的实例,甚至Foo的这个接口,都可以作出改变,这也就是为什么叫动态代理了。真的和具体应用无关,很抽象的东东。

2.  动态代理的作用

好了,现在我们都知道什么是动态代理了,但是它有什么作用呢?

通过查看JDK我们知道,Proxy这个类从JDK1.3开始就有了,为什么设计这个类,我还是没想明白,网上也没找到,不过也是,如果那帮人的思维我们可以理解,那么我们也可以去设计jdk了。

那么我们可以自己想下,我们可以要在什么情况下使用动态代理。很明显,在本地代码里面,我们不会很费力的把每个接口的实现和引用都改成动态代理的方式的,因为那样既难看,又难以理解,而且还不易于维护,但是如果我们在设计一个框架,一个可以在运行期间动态改变其表现的框架呢?

还有,如果我们有远程的调用(RMI),那么我们就还要集成remoteobj的类,并且实现自己的接口,像这样似的:

public class RemoteTest extends RemoteObject implements Foo
{
    Foo f;
    public RemoteTest(Foo f)
    {
        this.f = f;
    }
   
    void fooMethod1()
    {
       
    }
}

在解决了RMI的应用问题之后呢,我们还发现,动态代理还有很多的应用,比如;这样可以看到,我们需要继承,还需要传入Foo的一个实例,也就是说,我们要实现不同的业务实例,好麻烦了,如果有了泛型,还会简单一些,但是在JDK1.3的年代,哪里来的什么泛型啊,所以这个时候我们就要想办法创建一个代理类,可以胜任任何业务接口,可以在运行时候决定实现哪个接口,现在我们明白了吧,这不就是动态代理么,写动态代理JDK的是个人才,而将动态代理应用到RMI上面的人简直就是个天才(他确实是个天才,他就是RickardOberg,关于他的事迹,太多了,自己查查吧。)。

日志模块:比如需要在方法的开始和结束打印参数和执行时间,又不愿意修改应用代码,怎么半?

安全性检查:比如论坛注册用户访问和非注册用户的访问之类的。

事务管理:比如需要访问数据库,在每个方法调用之前打开连接,调用之后关闭连接,这样就可以大大简化我们的代码,多优雅的设计,不是么?

仔细看着上面的一系列应用,一个词渐渐的浮现在脑海,AOP(面向切面编程),这些不就是AOP要解决的问题么?

的确如此,Struts、Spring等框架的实现原理就是来源与动态代理,通过动态代理,我们来实现拦截器的功能,然后我们将多个拦截器连成一串,那就是过滤器链模式(ChannelPipeline)。好了不多扯了,再撤就多了 ,还有控制反转、依赖倒置、依赖注入等。

3.  动态代理的实现

由于从动态代理中引申的东西比较多,一时半会儿也说不完,仅仅粘贴基本的动态代理调用,至于简单的AOP实现,代理模式实现等,我们就不管了。有时间再写写吧。


1、bird.java 

package com.test.proxy;
 
public interface Bird
{
    void fly(String arg);
 
    void display();
}
2、Angel.java
package com.test.proxy;
 
/**
 * 鸟人类
 * @author Ransom
 *
 */
public class Angel implements Bird
{
    private String name;
 
    public Angel(String name)
    {
        this.name = name;
    }
 
    @Override
    public void fly(String arg)
    {
        System.out.println("Bird " + name + " called fly method,args:" + arg);
    }
 
    @Override
    public void display()
    {
        System.out.println("Bird " + name + " called display method");
    }
 
}
 
3、BirdInvocationHandler.java

package com.test.proxy;

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

/**
 * 虽然起名为bird,但是确实是一个动态代理类,所以可以创建任何接口的实例
 * @author Ransom
 *
 */
public class BirdInvocationHandler implements InvocationHandler
{
    private Object target;

    public BirdInvocationHandler(Object target)
    {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
        /*
         * 可以在方法调用的前后加入时间或者日志的打印
         * 也算是实现了一个基本的AOP了吧。
         */
        System.out.println("excute medhod:" + method.getName());
        Object obj = method.invoke(target, args);
        return obj;
    }

    public void setTarget(Object target)
    {
        this.target = target;
    }

}

4、Testmain.java

package com.test.proxy;

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

public class TestMain
{
    public static void main(String[] args) throws InstantiationException,
            IllegalAccessException, IllegalArgumentException,
            InvocationTargetException, NoSuchMethodException, SecurityException
    {
        /*
         * 创建代理方法1
         */
        InvocationHandler handler = new BirdInvocationHandler(new Angel(
                "birdman"));
        // Class proxyClass =
        // Proxy.getProxyClass(Bird.class.getClassLoader(),
        // Bird.class);
        // Bird b = (Bird) proxyClass.getConstructor(new Class[]
        // { InvocationHandler.class }).newInstance(new Object[]
        // { handler });
        // b.fly();
        // b.display();

        /**
         * 创建代理方法2
         */
        Bird b2 = (Bird) Proxy.newProxyInstance(
                BirdInvocationHandler.class.getClassLoader(), new Class[]
                { Bird.class }, handler);

        b2.fly("abc");
        b2.display();

    }
}


 

你可能感兴趣的:(动态代理及代理模式)