java代理模式以及实现

概念
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。
使用图表表示如下:
这里写图片描述
目的
在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
使用场景
但需要对现有的代码增加新的功能时,如果直接在原有代码上修改,一方面要熟悉之前的代码,另外修改的代码还可能带来新的问题。因此这时代理模式就可以很好的解决这个问题:不修改之前已经写好的代码或者方法,通过代理的方式对该方法进行功能拓展。

两种代理模式
1 静态代理
关键点
用户访问的是目标对象的代理对象,目标对象与代理对象实现同一接口或继承同一父类,且代理对象中持有目标对象。
举例如下
用户(User)去买东西(buy),如果自己去市场买,那么花费说要购买物品的价格即可;如果不想跑远,只想到楼下的小商铺那里买,那人家就要收取点福利了。。。。
代码实现
买东西接口:

public interface IBuy {
    void buy(long money);
}

买东西的用户,实现这个买的接口:

public class User implements IBuy {
    @Override
    public void buy(long money) {
        System.out.println("买东西,一共花了"+ money+"元!");
    }
}

然后呢,楼下小商铺代理,帮用户从市场把东西买来了,那么就要收点福利。。所以楼下小商铺代理也具有买这个功能:

public class UserProxy implements IBuy {
    private IBuy mIBuy; // 真实代理的对象

    public UserProxy(IBuy IBuy) {
        mIBuy = IBuy;
    }

    @Override
    public void buy(long money) {
        long fee = (long) (money * 0.01); // 代理对象的一些附加福利
        System.out.println("静态代理中介,坑了" + fee + "中介费!");

        mIBuy.buy(money + fee); // 真实对象执行
    }
}

然后是我们的测试代码:

    @Test
    public void buy() {
        IBuy user = new User();
        user.buy(10000);

        IBuy userProxy = new UserProxy(user);
        userProxy.buy(10000);
    }

执行,测试,输出如下结果:
这里写图片描述
可以看到,第一行输出是用户直接购买物品说需要花费的钱,下面两行是用户使用了小商铺这种代理,来购买东西,所需要付给小商铺的福利,以及一共花费的金额。

这就是静态代理模式的实现,从实现上看,代理对象的确没有对目标对象的代码做修改,只是在目标对象功能的基础上,添加了一些其他的附加功能;并且从代理对象中持有目标对象这一设计可以看出,代理对象可以完全控制目标对象的功能是否执行。但是,从中我们也可以看出静态代理模式也存在下面这个缺点:

代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.

所以既然有静态代理,那么对应的应该也有个动态代理,不知道动态代理能不能解决这个缺点呢?

1 动态代理
关键点
1.代理对象,需要实现JDK中的接口:InvocationHandler,并实现方法invoke;
2.代理对象的生成,是利用JDK的API,程序运行时生成,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
JDK api介绍
JDK中生成代理对象的API
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

ClassLoader loader:当前目标对象的类加载器
Class<>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:实现了InvocationHandler接口的代理对象

代码实现
动态代理对象:

public class DProxy implements InvocationHandler {
    private IBuy mBuy; // 持有的目标对象

    public DProxy(IBuy buy) {
        mBuy = buy;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("buy")) {
            long money = (long) args[0];
            long fee = (long) (money * 0.02);
            System.out.println("动态代理中介,坑了" + fee + "中介费!");
            long newMoney = money + fee;
            args[0] = newMoney;
            return method.invoke(mBuy, args);
        }
        return null;
    }
}

代码中,由于我们知道接口实现的名称以及接口参数,所以在invoke中,我们直接对buy方法进行了额外的功能添加,如果不知道接口对应的名称以及接口参数,那么可以直接使用反射的方法执行method对应的invoke方法:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("附加功能1");
                        //执行目标对象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("附加功能2");
                        return returnValue;
                    }

然后呢,是我们的测试代码:

    @Test
    public void buy() {
        IBuy user = new User();
        user.buy(10000);

        IBuy userProxy = new UserProxy(user);
        userProxy.buy(10000);

        IBuy dProxy = (IBuy) Proxy.newProxyInstance(user.getClass().getClassLoader(),user.getClass().getInterfaces(),new DProxy(user));
        dProxy.buy(10000);
    }

最终的执行结果:
这里写图片描述

总结:动态代理的invoke方法中,是利用反射的方法来执行接口中的method方法的,所以无论接口中的方法增加还是减少,都不影响动态代理的invoke方法执行,因而可以解决静态代理中的问题。另外,动态代理的目标对象需要实现某一接口,这样动态代理才可以代理该对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值