欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java动态代理详解

程序员文章站 2024-02-25 21:05:21
代理都知道吧,你去买东西就有很多的代理商,他们就是卖原厂的东西。比如,你天天要买肉,猪是农民伯伯养的,但你是从屠夫手上买到肉的,这个屠夫就可以当成是代理。那为什么要代理呢,...

代理都知道吧,你去买东西就有很多的代理商,他们就是卖原厂的东西。比如,你天天要买肉,猪是农民伯伯养的,但你是从屠夫手上买到肉的,这个屠夫就可以当成是代理。那为什么要代理呢,代理有什么用呢,当然是有事给他做了,对于屠夫这个代理就好理解了,因为你自己不可能去宰猪吧,所以代理就是去买活猪,然后宰掉再卖给你,当然屠夫有可能给肉注点水,关键看他坏不坏,所以屠夫的整个流程就是:

这个流程用代码怎么实现呢:我们应该要用三个类you、butcher、farmer分别指你、屠夫、农民伯伯。其中农民伯伯又提供一个买肉的方法给屠夫调用,这个方法输入是钱的数量,返回是肉的数量,都用int型,代码如下:

复制代码 代码如下:

class farmer {
    public int buymeat(int money) {
        int meat = 0;
        // ... meat = ***;
        return meat;
    }
}

而屠夫则提供一个买肉的方法给你调用,同样是输入钱,返回肉,但是会把肉加工一下(杀猪和刮猪毛在代码中就省了,要不然还得为猪写个类),代码如下:

复制代码 代码如下:

class butcher {
    public int buymeat(int money) {
        farmer farmer = new farmer();            // 1.find a farmer.
        int meat = farmer.buymeat(money);        // 2.buy meat from the farmer.
        meat += 5;                               // 3.inject 5 pound water into the meat, so weight will increase.
        return meat;                             // 4.return to you.
    }
}

然你从屠夫手上买肉的代码就变成这样:

复制代码 代码如下:

class you {
    public void work() {
        int youmoney = 10;
        butcher butcher = new butcher();        // find a butcher.
        int meat = butcher.buymeat(youmoney);
        system.out.println("cook the meat, weight: " + meat);  // you cooked it. 
    }
}

这个程序我们还可以优化一下,我们发现屠夫有农民有一个相同的买肉方法,我们可以提取一个接口,叫为商贩(pedlar)吧,以后你买肉就不用管他是屠夫还是农民伯伯了,只要他有肉卖就可以了,我们提取一个接口后,代码就变成这样:

复制代码 代码如下:

class you {
    public void work() {
        int youmoney = 10;
        peldar peldar= new butcher();                               // find a peldar.
        int meat = peldar.buymeat(youmoney);
        system.out.println("cook the meat, weight: " + meat);        // you cooked it.   
    }
}
interface peldar {
 int buymeat(int money);
}
class butcher implements peldar {
    @override
    public int buymeat(int money) {
        farmer farmer = new farmer();            // 1.find a farmer.
        int meat = farmer.buymeat(money);        // 2.buy meat from the farmer.
        meat += 5;                               // 3.inject 5 pound water into the meat, so weight will increase.
        return meat;                             // 4.return to you.
    }
}

class farmer implements peldar {
    @override
    public int buymeat(int money) {
        int meat = 0;
        // ... meat = ***;
        return meat;
    }
}

这就是代理,值得注意的是一般代理类和最终类会实现同一接口,这样的好处是,调用者就不用关心当前引用的到底是代理还是最终类。

不过这叫静态代理,因为代理类(屠夫类)是你亲手写,动态代理就是java在运行的时候,动态生成一个等价的代理类。虽然类是动态生成的,但是杀猪和注水的代码还是要写的,只是不要写一个类了。写到哪里呢,写到下面这个接口里面:

复制代码 代码如下:

public interface invocationhandler {
    public object invoke(object proxy, method method, object[] args) throws throwable;
}

参数是什么意思呢,我写成这样,可能你就明白了:

复制代码 代码如下:

public interface invocationhandler {
    public object invoke(object butcher, method buymeat, object[] money) throws throwable;
}

第一个参数是自动生成的代理类的一个对象(自动生成的屠夫类的对象),第二个参数是正前正在被调用的方法的对象(方法怎么还有对象呢,参见java反射机制),我们这里只有一个方法叫buymeat,所以这个参数代表的肯定就是它了,第三个参数是传给前面那个方法的参数数组,buymeat只有一个参数,所以这个数组只会有一个元素。于是杀猪注水的代码写进来就变成这样了:

复制代码 代码如下:

invocationhandler minvocationhandler = new invocationhandler() {  
    @override
    public object invoke(object butcher, method buymeat, object[] args) throws throwable {
        farmer farmer = new farmer();              // 1.find a farmer.
        int meat = (integer) buymeat.invoke(farmer, args);      // 2.buy meat from the farmer.
        meat += 5;                                 // 3.inject 5 pound water into the meat, so weight will increase.
        return meat;                               // 4.return to you.
    }
};

这个里调用农民伯伯的买肉方法有点不符常规,这里是反射机制调用法,意思是这样的,以farmer对象为接受者来调用buymeat方法,跟直接调用farmer的方法是一样的,你可能会问那为什么不直接调用呢,你可能没注意,invoke的第一个参数类型是object,所以你可以向任何对象发布调用命令(但不一定会成功,什么时候会成功等下说),如果你有很多farmer对象,甚至不是farmer对象,只要某接口的实例就可以(哪个接口等下说明,我们先命名为a接口),就可以当成参数传进来,然后对其进行方法调用。现在我们来看看如何生成代理类吧,很简单,可以调用proxy的工厂方法,如下:

复制代码 代码如下:

public static object newproxyinstance(classloader loader, class<?>[] interfaces, invocationhandler h) 
    throws illegalargumentexception

解释参数,第一个classloader是用来加载代理类的(关于classloader,本文暂不讲解),你暂不了解也没关系,第二个是一个数组,每个元数都是一个接口,新生成的代理都会实现所有这些接口,传给invocationhandler.invoke第二个参数的方法,必定属于所有这些接口中的方法,上一段落说的那个a接口必须是数组中的一个元素,上一段落说的那个调用成失败问题也明了了。第三个参数invocationhandler更好理解了,就是只要代理类中的任何方法被调用,就会通知这个invocationhandler。下面写出完整代码:

复制代码 代码如下:

class you {
    public void work() {
        int youmoney = 10;

        peldar peldarproxy = (peldar) proxy.newproxyinstance(getclass().getclassloader(), new class[]{peldar.class}, minvocationhandler);
        int meat = peldarproxy.buymeat(youmoney);

        system.out.println("cook the meat, weight: " + meat);   
    }

    invocationhandler minvocationhandler = new invocationhandler() {       
        @override
        public object invoke(object butcher, method buymeat, object[] args)
                throws throwable {
            farmer farmer = new farmer();                           // 1.find a farmer.
            int meat = (integer) buymeat.invoke(farmer, args);      // 2.buy meat from the farmer.
            meat += 5;                                              // 3.inject 5 pound water into the meat, so weight will increase.
            return meat;                                            // 4.return to you.
        }
    };

}
interface peldar {
    int buymeat(int money);
}

class farmer implements peldar {
    @override
    public int buymeat(int money) {
        int meat = 0;
        // ... meat = ***;
        return meat;
    }
}

这里you类里生成一个代理类,在代理类的buymeat被调用时,代码就跟之前的静态代理一样的了。