文档章节

设计模式-代理模式

请把小熊还给我_m
 请把小熊还给我_m
发布于 01/17 15:10
字数 2408
阅读 159
收藏 0

3 月,跳不动了?>>>

一、简介

代理模式:代理模式又叫委托模式,是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。

  • Subject(抽象角色): 通过接口或抽象类声明真实角色实现的业务方法。
  • RealSubject(目标角色): 实现抽象角色,定义目标角色所要实现的业务逻辑,供代理角色调用。
  • Proxy(代理角色): 实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
一个代理类可以代理多个被委托者或被代理者,因此一个代理类具体代理哪个具体主题角色,是由场景类决定的。最简单的情况是一个主题类和一个代理类。通常情况下,一个接口只需要一个代理类,具体代理哪个实现类有高层模块决定。

优点

1.增强目标对象。可以在执行目标对象方法的前后或者其他地方加上验证、日志等等代码;(Spring框架中的AOP)
2.将调用对象和被调用对象分离,一定程度上降低了耦合度。扩展性好;
3.保护目标对象;
4.职责清晰。目标对象就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成,附带的结果就是编程简洁清晰。

缺点

1.对象与对象调用之间增加了一层代理,可能会导致执行的速度变慢;
2.实现代理的代码有时会很复杂,添加了额外的工作量;
3.增加系统的复杂度。

使用场景

1.需要保护目标类,不希望直接被调用;
2.设置权限。类似Spring AOP的使用。

二、实现方式

1、静态代理: 代理类在程序运行前就编译好,因此称为静态代理。代理对象和被代理对象都要实现相同的接口或者继承相同的父类(除Object之外),一旦接口或者父类添加、修改方法,子类都要统一更改,违背开闭原则,因此静态代理具有一定的局限性。

抽象角色:

public interface IHttpInvoke {
    String invoke(String request);
}

目标角色:

public class Server implements IHttpInvoke {

    Logger logger = Logger.getLogger(String.valueOf(getClass()));

    @Override
    public String invoke(String request) {
        String response = "测试结果返回";
        return response;
    }
}

代理对象:

public class HttpInvokeProxy implements IHttpInvoke {

    private Logger logger = Logger.getLogger(String.valueOf(getClass()));

    private IHttpInvoke iHttpInvoke;

    public HttpInvokeProxy(IHttpInvoke iHttpInvoke) {
        this.iHttpInvoke = iHttpInvoke;
    }

    @Override
    public String invoke(String request) {
        String req = before(request);
        String response = iHttpInvoke.invoke(req);
        after(response);
        return response;
    }

    public String before(String request){
        logger.info("请求数据:" + request);
        byte[] req = request.getBytes();
        String requestData = null;
        try {
            requestData = new String(req,"GBK");
            logger.info("转码成功,返回请求数据");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return requestData;
    }

    public void after(String response){
        logger.info("响应数据: " + response);
    }
}

测试类:

public class Browser {
    public static void main(String[] args) {
        String request = "测试下代理类";
        IHttpInvoke httpInvokeProxy = new HttpInvokeProxy(new Server());
        httpInvokeProxy.invoke(request);
    }
}

输出结果:

五月 16, 2019 10:25:02 上午 Structural.ProxyPattern.StaticProxy.HttpInvokeProxy before
信息: 请求数据:测试下代理类
五月 16, 2019 10:25:03 上午 Structural.ProxyPattern.StaticProxy.HttpInvokeProxy before
信息: 转码成功,返回请求数据
五月 16, 2019 10:25:03 上午 Structural.ProxyPattern.StaticProxy.HttpInvokeProxy after
信息: 响应数据: 测试结果返回

静态代理的UML:

优点:

  • 静态代理对客户(测试类)隐藏了被代理类接口(目标类接口)的具体实现类,在一定程度上实现了解耦,同时提高了安全性。

缺点:

  • 静态代理类需要实现目标类(被代理类)的接口,并实现其方法,造成了代码的大量冗余。
  • 静态代理只能对某个固定接口的实现类进行代理服务,其灵活性不强。故一般大项目不会选择静态代理。

2、动态代理: 利用JDK的API,动态的在内存中构建代理对象。不需要继承父类,可扩展性高。

抽象角色:

public interface IHttpInvoke {
    String invoke(String request);
}

目标角色:

public class Server implements IHttpInvoke {

    Logger logger = Logger.getLogger(String.valueOf(getClass()));

    @Override
    public String invoke(String request) {
        String response = "测试结果返回";
        return response;
    }
}

代理对象:

public class HttpInvokeProxy implements InvocationHandler {

    private Logger logger = Logger.getLogger(String.valueOf(getClass()));

    private Object obj;

    public HttpInvokeProxy(Object obj) {
        this.obj = obj;
    }

    public String before(String request){
        logger.info("请求数据:" + request);
        byte[] req = request.getBytes();
        String requestData = null;
        try {
            requestData = new String(req,"GBK");
            logger.info("转码成功,返回请求数据");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return requestData;
    }

    public void after(String response){
        logger.info("响应数据: " + response);
    }

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

客户端:

public class Browser {
    public static void main(String[] args) {
        String request = "测试下代理类";
        Server server = new Server();
        InvocationHandler invocationHandler = new HttpInvokeProxy(server);
        Class cls = server.getClass();
        IHttpInvoke httpInvoke = (IHttpInvoke) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),invocationHandler);
        httpInvoke.httpInvoke(request);
    }
}

输出结果:

五月 16, 2019 10:25:02 上午 Structural.ProxyPattern.StaticProxy.HttpInvokeProxy before
信息: 请求数据:测试下代理类
五月 16, 2019 10:25:03 上午 Structural.ProxyPattern.StaticProxy.HttpInvokeProxy before
信息: 转码成功,返回请求数据
五月 16, 2019 10:25:03 上午 Structural.ProxyPattern.StaticProxy.HttpInvokeProxy after
信息: 响应数据: 测试结果返回

优点

  • 动态代理实现了只需要将被代理对象作为参数传入代理类就可以获取代理类对象,从而实现类代理,具有较强的灵活性。
  • 动态代理的服务内容不需要像静态代理一样写在每个代码块中,只需要写在invoke()方法中即可,降低了代码的冗余度。

缺点

  • 动态代理类仍然需要实现接口。

3、Cglib代理: 上面两种代理方式目标对象都需要实现接口,但有时候并不是所有目标对象都要实现接口,因此可以使用Cglib框架实现普通类的代理。

*使用Cglib代理需要引入Cglib的jar包,而且还需要引入asm-all的jar包。注意两个包的版本号,因为不同的版本可能不兼容导致报错。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>
<dependency>
    <groupId>asm</groupId>
    <artifactId>asm-all</artifactId>
    <version>3.3</version>
</dependency>

目标对象:

public class Server{

    Logger logger = Logger.getLogger(String.valueOf(getClass()));

    public String receiveRequest(String request) {
        String response = "测试结果返回";
        return response;
    }
}

代理类:

public class HttpInvokeProxy implements MethodInterceptor {

    private Logger logger = Logger.getLogger(String.valueOf(getClass()));

    private Object obj;

    public HttpInvokeProxy() {
    }

    public HttpInvokeProxy(Object obj) {
        this.obj = obj;
    }

    public String before(String request){
        logger.info("请求数据:" + request);
        byte[] req = request.getBytes();
        String requestData = null;
        try {
            requestData = new String(req,"GBK");
            logger.info("转码成功,返回请求数据");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return requestData;
    }

    public void after(String response){
        logger.info("响应数据: " + response);
    }

    public Object getProxy(){
        //1.工具类
        Enhancer enhancer = new Enhancer();
        //2.设置父类
        enhancer.setSuperclass(Server.class);
        //3.设置回调函数
        enhancer.setCallback(this);
        //4.创建子类(代理对象)
        Object object = enhancer.create();
        return object;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before((String) objects[0]);
        Object obj = methodProxy.invokeSuper(o,objects);
        after((String) obj);
        return obj;
    }
}

测试类:

public class Browser {
    public static void main(String[] args) {
        String request = "测试下代理类";
        Server server = new Server();
        Server proxy = (Server) new HttpInvokeProxy(server).getProxy();
        proxy.receiveRequest(request);
    }
}

输出结果同上

三、总结

静态代理作为原始的代理模式设计,代理对象和目标对象都实现同一个接口,在代理对象中指向的是目标对象的实例,这样对外暴露的是代理对象而真正调用的是目标对象。其优点是保护目标对象,提高安全性;但缺点也显而易见,不同的接口要有不同的代理类实现,代码量增加,系统冗余。

动态代理弥补了静态代理需要子类继承或者实现父类的缺点,利用反射机制创建对象,进一步降低了耦合度。动态代理必须依赖接口的实现,但并不是所有的目标对象都会继承父类或者实现接口。

Cglib代理弥补了JDK动态代理不足。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。但由于CGLib采用动态创建子类的方法,对于final方法,无法进行代理。

每种代理方式都有优缺点,我们应该根据实际的开发场景选择动态代理或者使用Cglib代理。

四、Spring AOP 与 动态代理

AOP 专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在 Java EE 应用中,常常通过 AOP 来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,AOP 已经成为一种非常常用的解决方案。

AOP机制是 Spring 所提供的核心功能之一,其既是Java动态代理机制的经典应用,也是动态AOP实现的代表。Spring AOP默认使用Java动态代理来创建AOP代理,具体通过以下几个步骤来完成:

  • Spring IOC 容器创建Bean(目标类对象);
  • Bean创建完成后,Bean后处理器(BeanPostProcessor)根据具体的切面逻辑及Bean本身使用Java动态代理技术生成代理对象;
  • 应用程序使用上述生成的代理对象替代原对象来完成业务逻辑,从而达到增强处理的目的。

 

© 著作权归作者所有

上一篇: IO流
请把小熊还给我_m
粉丝 22
博文 52
码字总数 69711
作品 0
杭州
私信 提问
加载中

评论(0)

【设计模式笔记】(十六)- 代理模式

一、简述 代理模式(Proxy Pattern),为其他对象提供一个代理,并由代理对象控制原有对象的引用;也称为委托模式。 其实代理模式无论是在日常开发还是设计模式中,基本随处可见,中介者模式中...

MrTrying
2018/06/24
0
0
《PHP设计模式大全》系列分享专栏

《PHP设计模式大全》已整理成PDF文档,点击可直接下载至本地查阅 https://www.webfalse.com/read/201739.html 文章 php设计模式介绍之编程惯用法第1/3页 php设计模式介绍之值对象模式第1/5页...

kaixin_code
2018/11/06
219
0
设计模式梳理(一)

设计模式梳理(一) 总体来说设计模式分为三大类: @案例源码地址:https://gitlab.com/lxqxsyu/DisgnPattern 创建型模式 简单工厂模式 工厂类是整个模式的关键。它包含必要的判断逻辑,能够...

lxq_xsyu
2017/11/02
0
0
JavaScript 的一些设计模式

设计模式的定义:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案 设计模式是前人解决某个特定场景下对而总结出来的一些解决方案。可能刚开始接触编程还没有什么经验的时候,会...

格西南
2019/08/20
0
0
《JavaScript设计模式与开发实践》原则篇(2)—— 最少知识原则

最少知识原则(LKP)说的是一个软件实体应当尽可能少地与其他实体发生相互作用。这 里的软件实体是一个广义的概念,不仅包括对象,还包括系统、类、模块、函数、变量等。 单一职责原则指导我们...

嗨呀豆豆呢
2018/12/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

第二天

独立工程就是打成一个war包,war包可以部署到不同的服务器中; 聚合工程至少有一个是war包,去除web后manager聚合工程剩余dao、service、pojo、interface,把service改成war包 main下加入web...

七宝1
43分钟前
19
0
开源:从“复兴”走向“商业化”

在美国版“知乎”Quora上搜“Open Source(开源)”,出来第一条问题是: “Linux的失败真的是因为开源吗?”。其中一个回答给我很多启发: “有些人把安卓和Chrome OS的成功归结于Linux开源...

编辑部的故事
今天
347
0
JavaScript等同于printf / String.Format - JavaScript equivalent to printf/String.Format

问题: I'm looking for a good JavaScript equivalent of the C/PHP printf() or for C#/Java programmers, String.Format() ( IFormatProvider for .NET). 我正在寻找一个等效于C / PHP p......

javail
今天
27
0
什么是Android上的“上下文”? - What is 'Context' on Android?

问题: In Android programming, what exactly is a Context class and what is it used for? 在Android编程中, Context类到底是什么?它的用途是什么? I read about it on the developer......

技术盛宴
今天
26
0
OkHttp配置HTTPS访问+服务器部署

1 概述 OkHttp配置HTTPS访问,核心为以下三个部分: sslSocketFactory() HostnameVerifier X509TrustManager 第一个是ssl套接字工厂,第二个用来验证主机名,第三个是证书信任器管理类.通过OkHtt...

氷泠
今天
26
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部