代理模式

 

 

         接之前介绍的策略模式,今天我们学习另一种设计模式:代理模式。

代理模式

         定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

       它的uml示意图如下所示:

 

         我理解的代理模式是为了让编码更符合单一职责的设计,一些不符合类设计初衷的方法通过代理模式来实现。

         举个例子,在我们生活中,每个歌手都会有自己的经纪公司,歌手要做的就是创作作品,其他演出,活动等安排都交由经纪公司来安排,公司也了解自己旗下的歌手,不会安排超出能力以外的工作。这就是现实中一个很好的代理例子。

         我们用面向对象的思想来分析下:

         歌手对象Singer,它提供一个方法create(),沟通演出不属于他的职责,所以有了一个代理对象company,它持有singer,还额外提供arrangePerformance().通过这个company,我们既可以安排演出,还可以欣赏歌手创作的作品,这也就是代理模式设计的初衷:

  1. 隐藏被代理对象的实现细节:
  2. 调用者和被代理对象之间解耦
  3. 方便扩展

 

 

静态代理和动态代理

         代理模式主要分为两种:静态代理和动态代理,其中动态代理根据实现方式不同也分为jdk动态代理和cglib动态代理。

 

我们分别用静态代理和动态代理来实现上面的小例子

静态代理:

                   编译时代理类就已经被创建。

Singer:

public interface Singer {
    /**
     * 创作
     */
    void create();
}

 

SingerImpl:

public class SingerImpl implements Singer {

    @Override

    public void create() {

        System.out.println("创作作品");

    }

}

 

SingerProxy:

public class SingerProxy {

    SingerImpl singer;



    public SingerProxy(SingerImpl singer) {

        this.singer = singer;

    }



    public void proxy(){

        before();

        singer.create();

        after();

    }



    private void before(){

        System.out.println("经纪公司沟通活动");

    }



    private void after(){

        System.out.println("经纪公司安排散场");

    }

}

 

Test:

 
public class TestSingerProxy {

    public static void main(String[] args) {

        SingerProxy proxy = new SingerProxy(new SingerImpl());

        proxy.proxy();

    }

}

 

运行结果:

 

         可以看到我们通过SingerProxy在实现singer的功能基础上,还进行了扩展。这样看起来静态代理效果很明显,实现也很简单,那么它会不会存在什么缺点还没有被我们发现呢?

动态代理:

         运行时动态的创建代理类。动态代理有两种实现方式:jdk动态代理和cglib动态代理。

Jdk

         改用jdk动态代理,我们需要重新创建一个代理类来实现InvocationHandler接口,既然是动态的,我们这里面就不需要指定目标对象了。

         DynamicProxy:

public class DynamicProxy implements InvocationHandler {

    Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target,args);
        after();
        return result;
    }

    private void before(){
        System.out.println("经纪公司沟通活动");
    }

    private void after(){
        System.out.println("经纪公司安排散场");
    }
}

                  

test

public class TestDynamicProxy {
    public static void main(String[] args) {
        SingerImpl singerimpl = new SingerImpl();
        DynamicProxy proxy = new DynamicProxy(singerimpl);
        Singer singer = (Singer) Proxy.newProxyInstance(singerimpl.getClass().getClassLoader(), singerimpl.getClass().getInterfaces(), proxy);
        singer.create();
    }
}

 

运行结果:

        

         可以看到动态代理在使用时,需要通过Proxy.newProxyInstance来获取代对象,在执行目标方法时,通过反射来实现。

         上面虽然我们已经实现了代理的功能,但这样子的写法实在是太不友好了,要是直接通过代理对象就能实现所有的操作就好了。

         DynamicProxy2.0

public class DynamicProxy2 implements InvocationHandler {

    Object target;

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

    public <T>T getProxy(){
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target,args);
        after();
        return result;
    }

    private void before(){
        System.out.println("经纪公司沟通活动");
    }

    private void after(){
        System.out.println("经纪公司安排散场");
    }
}

 

test:

public class TestDynamicProxy {
    public static void main(String[] args) {
        Singer singer = new DynamicProxy2(new SingerImpl()).getProxy();
        singer.create();
    }
}

 

运行结果:

 

         可以看到经过上面的改造,现在动态代理使用起来已经很简单了,我们要做的就是将需要代理的对象放进去,然后正常调用我们的目标方法即可。

         现在看来jdk动态代理比静态代理好很多,我们貌似有它就足够了,但事实上我们还有一种动态代理的实现方式:通过cglib来实现动态代理,这又是为什么呢?

 

 

静态代理和动态代理优缺点分析:

         静态代理的优点就是代理模式的优点:隐藏细节,解耦,扩展功能,但是它的缺点同样明显,如果最顶层的内容发生变化,那么我们需要修改它的实现类和代理类,如果这种情况很多的话,那么恐怖修改的工作量就够受的了。

         在这个基础上,动态代理很好的解决了这个问题,不管你的目标对象做什么修改都不会影响到我们的代理类,但是细心的你在使用jdk动态代理创建代理实例的时候肯定发现他有一个方法入参是:

Class<?>[] interfaces,

这不就是接口的意思么,难道jdk动态代理只能针对接口来进行代理么? 答案是肯定的,jdk动态代理只能针对接口。

         如果没有接口又需要代理那该怎么办呢?不要急,cglib该登场了。

        

Cglib

         有关cglib的说明网上有很多,我们这里不加赘述,只需要知道他可以操作字节码生成新的类,我们用它来实现动态代理即可。

         Cglib的代理类需要实现MethodInterceptor

CglibProxy:

public class CglibProxy implements MethodInterceptor {
    Object target;

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

    public <T>T getProxy(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = method.invoke(target, objects);
        after();
        return result;
    }

    private void before(){
        System.out.println("经纪公司沟通活动");
    }

    private void after(){
        System.out.println("经纪公司安排散场");
    }
}

 

test:

public class TestCglibProxy {
    public static void main(String[] args) {
        Singer singer = new CglibProxy(new SingerImpl()).getProxy();
        singer.create();
    }
}

 

运行结果:  

 

         其中获取代理的方式需要通过Enhancer对象来创建,主要参数就是superClass 和 代理类对象。

代理模式在spring中的应用

         SpringAop中通过动态代理来实现aop的功能,其中针对接口使用jdk动态代理来实现,其他的实现类则通过cglib来实现动态代理,在spring中基本都是面向接口编程,所以其实jdk动态代理是里面的默认实现方式。

 

总结

         代理模式分为静态代理和动态代理,动态代理根据实现方式又可以分为jdk动态代理和cglib动态代理,其中jdk动态代理针对接口,cglib通过动态创建子类的方式生成代理对象(所以无法对目标类种的final和private方法代理)。这也是spring aop的选择逻辑。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值