关于装饰模式和动态代理模式

摘要:
装饰模式和动态代理模式乍一看差不多,都是动态的增加行为,其实有各自的区别。˃iface)throwsSQLException{53returnconn.isWrapperFor;54}除了重写的close方法进行改写,其余的都调用被装饰对象的,即conn.接下来,继承标准的数据源,写一个MyDataSource,这里只演示重写getConnection()方法。这里我们采用基于接口的动态代理进行演示刚才的例子:不用写MyConn

装饰模式和动态代理模式乍一看差不多,都是动态的增加行为,其实有各自的区别。

一、首先我们看一下装饰设计模式,其基本思想如下:

1、编写一个类,实现与被装饰类相同的接口。目的使他们有相同的行为

2、定义一个实例变量,引用被装饰对象。目的和原来的老对象进行交接

3、定义构造方法,把被装饰对象注入进来。

4、对于不需要改写的方法,调用被装饰对象的。

5、对于要改写的方法,改写即可。

废话不多说,举一个例子,模拟实现一个数据库连接池,在这里,我想重写close方法,以实现调用close方法之后不是关闭连接,而是归还连接。

首先,继承java.sql.Connection接口,写一个类MyConnection:

1 packagepool;
2 
3 importjava.sql.Array;
4 importjava.sql.Blob;
5 importjava.sql.CallableStatement;
6 importjava.sql.Clob;
7 importjava.sql.Connection;
8 importjava.sql.DatabaseMetaData;
9 importjava.sql.NClob;
10 importjava.sql.PreparedStatement;
11 importjava.sql.SQLClientInfoException;
12 importjava.sql.SQLException;
13 importjava.sql.SQLWarning;
14 importjava.sql.SQLXML;
15 importjava.sql.Savepoint;
16 importjava.sql.Statement;
17 importjava.sql.Struct;
18 importjava.util.List;
19 importjava.util.Map;
20 importjava.util.Properties;
21 importjava.util.concurrent.Executor;
22 
23 //1、编写一个类,实现与被装饰类(com.mysql.jdbc.Connection)相同的接口。目的使他们有相同的行为
24 //2、定义一个实例变量,引用被装饰对象。目的和原来的老对象进行交接
25 //3、定义构造方法,把被装饰对象注入进来。
26 //4、对于不需要改写的方法,调用被装饰对象的。
27 //5、对于要改写的方法,改写即可。
28 
29 public class MyConnection implementsConnection {
30     
31     privateConnection conn;
32     private List<Connection>pool;
33     
34     public MyConnection(Connection conn, List<Connection>pool){
35         this.conn =conn;
36         this.pool =pool;
37 }
38 
39     //把conn还回池中
40 @Override
41     public void close() throwsSQLException {
42 pool.add(conn);
43 }
44     
45     //静态代理
46 @Override
47     public <T> T unwrap(Class<T> iface) throwsSQLException {
48         returnconn.unwrap(iface);
49 }
50 
51 @Override
52     public boolean isWrapperFor(Class<?> iface) throwsSQLException {
53         returnconn.isWrapperFor(iface);
54     }

除了重写的close方法进行改写,其余的都调用被装饰对象的,即conn.

接下来,继承标准的数据源,写一个MyDataSource,这里只演示重写getConnection()方法。

packagepool;
importjava.awt.Menu;
importjava.io.PrintWriter;
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
importjava.sql.Connection;
importjava.sql.SQLException;
importjava.sql.SQLFeatureNotSupportedException;
importjava.util.ArrayList;
importjava.util.Collections;
importjava.util.List;
importjava.util.logging.Logger;
importjavax.sql.DataSource;
importday02.JDBCUtil;
//继承标准的数据源
public class MyDataSource implementsDataSource {
    //存放链接对象的池
    private static List<Connection> pool =
            Collections.synchronizedList(new ArrayList<Connection>());
    //最开始初始化一些链接到池中
    static{
        for (int i = 0; i < 10; i++) {
            Connection conn =JDBCUtil.getConnection();
            pool.add(conn);
        }
    }
    @Override
    public Connection getConnection() throwsSQLException {
        if(pool.size() > 0){
            Connection connection =  pool.remove(0);
            //使用装饰模式
            MyConnection myConnection = newMyConnection(connection, pool);
            returnmyConnection;
        }else{
            throw new RuntimeException("服务器忙");
        }
    }

可以通过以下代码进行验证:

1 @Test
2     public void test1() throwsSQLException{
3         MyDataSource ds = newMyDataSource();
4         //保存
5         Connection conn =ds.getConnection();
6 System.out.println(conn.getClass().getName()); //输出MyConnection
7         //-----------
8         conn.close();//需要重新写一下close方法,不让它关闭而是归还连接
9         
10         //删除
11         Connection conn1 =ds.getConnection();
12         //-----------
13 conn1.close();
14     }

二、对于动态代理模式,是AOP技术实现的关键,实现它有两种方式:

基于接口的动态代理:

前提:被代理对象的类,实现了至少一个接口

基于子类的动态代理:

借助第三方-CGLIB。

这里我们采用基于接口的动态代理进行演示刚才的例子:

不用写MyConnection了,只需要写MyDataSource:

packagepool;
importjava.awt.Menu;
importjava.io.PrintWriter;
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
importjava.sql.Connection;
importjava.sql.SQLException;
importjava.sql.SQLFeatureNotSupportedException;
importjava.util.ArrayList;
importjava.util.Collections;
importjava.util.List;
importjava.util.logging.Logger;
importjavax.sql.DataSource;
importday02.JDBCUtil;
//继承标准的数据源
public class MyDataSource implementsDataSource {
    //存放链接对象的池
    private static List<Connection> pool =
            Collections.synchronizedList(new ArrayList<Connection>());
    //最开始初始化一些链接到池中
    static{
        for (int i = 0; i < 10; i++) {
            Connection conn =JDBCUtil.getConnection();
            pool.add(conn);
        }
    }
    @Override
    public Connection getConnection() throwsSQLException {
        if(pool.size() > 0){
            final Connection connection =  pool.remove(0);//使用动态代理模式
            Connection proxyConnection =(Connection) Proxy.newProxyInstance(
                    connection.getClass().getClassLoader(), 
                    new Class[]{Connection.class},
                    newInvocationHandler() {
                        @Override
                        publicObject invoke(Object proxy, Method method, Object[] args)
                                throwsThrowable {
                            if("close".equals(method.getName())){
                                //用户调用close方法,返回池中
pool.add(connection);
                            }else{
                                //其他方法,调用原来对象的对应方法
method.invoke(connection, args);
                            }
                            return null;
                        }
                    });
            returnproxyConnection;
        }else{
            throw new RuntimeException("服务器忙");
        }
    }
    

然后进行验证:

@Test
    public void test1() throwsSQLException{
        MyDataSource ds = newMyDataSource();
        //保存
        Connection conn =ds.getConnection();
        System.out.println(conn.getClass().getName());//com.sun.proxy.$Proxy4
        //-----------
        conn.close();//需要重新写一下close方法,不让它关闭而是归还连接
        //删除
        Connection conn1 =ds.getConnection();
        //-----------
conn1.close();
    }

对装饰器模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个 接口。对代理模式来说,代理类(proxy class)和真实处理的类(real class)都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。

然而,实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。

免责声明:文章转载自《关于装饰模式和动态代理模式》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Redis内存碎片率powerBI 连接报表服务器提示 发生意外错误下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

maven全局配置文件settings.xml详解

概要 settings.xml有什么用? 如果在Eclipse中使用过Maven插件,想必会有这个经验:配置settings.xml文件的路径。settings.xml文件是干什么的,为什么要配置它呢?从settings.xml的文件名就可以看出,它是用来设置maven参数的配置文件。并且,settings.xml是maven的全局配置文件。而pom.xm...

SqlServer数据库主从同步

分发/订阅模式实现SqlServer主从同步 在文章开始之前,我们先了解一下几个关键的概念: 分发服务器分发服务器是负责存储在同步过程中所用复制信息的服务器。可以比喻成报刊发行商。 分发数据库分发数据库用于存储发布数据库所做的更改。它还可以存储快照和合并发布的历史信息。存在于系统数据库中,默认为destribution. 发布服务器使服务器能够成为发布...

如何调试 Android 上 HTTP(S) 流量

转自: http://greenrobot.me/devpost/how-to-debug-http-and-https-traffic-on-android/ 如何调试 Android 上 HTTP(S) 流量 前面的话 在Android开发中我们常常会和API 打交道,可能你不想,但是这是避不开的。大部分情况下,调试发送网络请求和接收响应的过程都是...

Javassist实现动态代理

介绍 Javassist 也是一个字节码框架,和其他字节码框架不同的是,它提供了两种层级的API,源层级和字节码层级,源层级不需要对字节码规则了解太多就可以操作。Hibernate的懒加载就使用到了Javassist。官网 使用Javassist提供的动态代理接口实现 maven依赖 <dependency> <groupId>...

浅尝装饰器和AOP

【写在前面】 参考文章:https://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html【从简单的例子入手进行讲解,由浅入深,很到位】 装饰器部分总共写了三篇博客,这是其一,另外两篇博客如下,都是比较浅显的记录的自己对装饰器的理解,感兴趣的可以踩一踩^_^ 浅尝装饰器-@staticmethod 和@...

【Mybatis进阶】动态代理

如果你想更深刻的理解Mybatis动态代理的原理,那么你应该先知道 什么是代理模式? 在没有动态代理的时候Mybatis是如何实现dao层的? 什么是代理模式 具体可以阅读笔者的博客—— 代理模式 在没有动态代理的时候Mybatis是如何实现dao层的 本篇博客基于mybatis的环境已经搭建完成,如果不知道如何搭建,具体可以阅读笔者的博客——【从零开...