zoukankan      html  css  js  c++  java
  • 设计模式(二)--代理模式

      代理模式可分为三种,一种是静态代理,一种是动态代理,还有一种是Cglib代理。

    一、静态代理

      静态代理和动态代理模式本质上一样的,都是在原有类的行为基础上,加入一些多出的行为,甚至完全替换原有的行为。

      举一个静态代理的例子,我们都知道,数据库连接是很珍贵的资源,频繁的开关数据库连接是非常浪费服务器的CPU资源已经内存的,所以我们一般都是使用数据库连接池来解决这一问题,即创造一堆等待被使用的连接,等到用的时候就从池里取一个,不用了再放回去,数据库连接在整个应用启动期间,几乎是不关闭的,除非是超过了最大空闲时间。现在要解决的问题是,如何替换connection的close行为,使close方法被调用的时候没有真正的关闭连接,而是将连接归还给连接池。

      下面是Connection接口,LZ去掉了很多方法,我们只关心close方法。

    import java.sql.SQLException;
    import java.sql.Statement;
    import java.sql.Wrapper;
    
    /**
     * connection接口
     * @author xiaodongdong
     * @create 2018-05-02 14:10
     **/
    public interface Connection  extends Wrapper {
    
        Statement createStatement() throws SQLException;
    
        void close() throws SQLException;
    
    }

      如何替换现有close方法的行为呢,我们组合复用一个connection对象,不关心的方法交给原connection方法去处理,比如createStatement()方法,真正关心的close方法,我们用自己的逻辑去实现。另外,为了保证对程序猿是透明的,我们实现Connection接口。

    import java.sql.SQLException;
    import java.sql.Statement;
    
    
    public class ConnectionProxy implements Connection{
        
        private Connection connection;
        
        public ConnectionProxy(Connection connection) {
            super();
            this.connection = connection;
        }
    
        public Statement createStatement() throws SQLException{
            return connection.createStatement();
        }
        
        public void close() throws SQLException{
            System.out.println("不真正关闭连接,归还给连接池");
        }
    
    }

      这个静态代理应该在什么地方使用呢,LZ写了一个简单的数据库连接池,获得连接的方法返回的就是ConnectionProxy,代码如下。

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.util.LinkedList;
    
    /**
     * 数据库连接池
     * @author xiaodongdong
     * @create 2018-05-02 14:54
     **/
    public class ConnectionPool {
        private LinkedList<Connection> pool = new LinkedList<Connection>();
    
        static{
            try {
                Class.forName("com.mysql.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        private static Connection createNewConnection() throws SQLException {
            return DriverManager.getConnection("url","username", "password");
        }
    
        private ConnectionPool(int size) {
            initialize(size);
        }
    
        private void initialize(int size) {
            try {
                if (size > 0) {
                    synchronized (pool) {
                        for (int i = 0; i < size; i++) {
                            pool.add(createNewConnection());
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        /**
         *  释放连接
         */
        public void releaseConnection(Connection conn) throws InterruptedException,
                SQLException {
            if (conn != null) {
                synchronized (pool) {
                    pool.addLast(conn);
                    // 释放连接后通知所有线程
                    pool.notifyAll();
                }
            }
        }
    
        /**
         * 获得连接
         */
        public Connection getConnection(long millons) throws InterruptedException {
            if (millons < 0) {// 完全超时
                synchronized (pool) {
                    while (pool.isEmpty()) {
                        pool.wait(millons);
                        System.out.println("完全超时");
                    }
                    return pool.removeFirst();
                }
            } else {
                synchronized (pool) {
                    long future = System.currentTimeMillis() + millons;
                    long remaining = millons;
                    while (pool.isEmpty() && remaining > 0) {
                        pool.wait(remaining);
                        remaining = future - System.currentTimeMillis();
                    }
                    Connection result = null;
                    if (!pool.isEmpty()) {
                        //result = pool.removeFirst(); 这是原有的方式,直接返回连接
                //,这样可能会被程序员把连接给关闭掉
    //下面是使用代理的方式,程序员再调用close时,就会归还到连接池 result = new ConnectionProxy(pool.removeFirst()); } return result; } } } /** * 单例 */ public static ConnectionPool getInstance(){ return DataSourceInstance.dataSource; } private static class DataSourceInstance{ private static ConnectionPool dataSource = new ConnectionPool(20); } }

      这样,ConnectionProxy中的close方法就明确了,我们将close方法修改一下。

     public void close() throws SQLException{
        ConnectionPool.getInstance().releaseConnection(connection);
     }

      至此,连接池返回的连接就全是我们自己实现的静态代理类,只要连接是从我们自己实现的连接池拿的,就算调用close方法,也不会真正的关闭连接,而是把连接返回给连接池保存。

      静态代理一般这样实现:

    1. 代理类一般要持有一个被代理的对象的引用。
    2. 对于我们不关心的方法,全部委托给被代理对象处理。
    3. 只处理我们关心的方法

    二、动态代理

      静态代理的缺点是一次只能代理一个类,而且编码相对麻烦,我们看看利用动态代理如何实现现有功能,动态代理是JDK自带的功能,你需要实现InvocationHandler接口,并且调用Proxy的静态方法产生代理类。我们重写ConnectionProxy类、

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.sql.Connection;
    
    /**
     * 动态代理
     */
    public class ConnectionProxy implements InvocationHandler{
    
        private Connection connection;
    
        public ConnectionProxy(Connection connection) {
            super();
            this.connection = connection;
        }
        
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //这里判断是Connection接口的close方法的话
            if (Connection.class.isAssignableFrom(proxy.getClass()) && method.getName().equals("close")) {
                //我们不执行真正的close方法
                //method.invoke(connection, args);
                //将连接归还连接池
                ConnectionPool.getInstance().releaseConnection(connection);
                return null;
            }else {
                return method.invoke(connection, args);
            }
        }
    
        /**
         * 调用该方法获得被代理对象
         */
        public Connection getConnectionProxy(){
            return (Connection) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class}, this);
        }
    
    }

      连接池稍作修改,将result = new ConnectionProxy(pool.removeFirst());一行改为 result = new ConnectionProxy(pool.removeFirst()).getConnectionProxy();

      上面是我们针对connection写的动态代理,InvocationHandler接口只有一个invoke方法需要实现,这个方法是用来在生成的代理类用回调使用的,很显然,动态代理是将每个方法的具体执行过程交给了我们在invoke方法里处理。而具体的使用方法,我们只需要创造一个ConnectionProxy的实例,并且将调用getConnectionProxy方法的返回结果作为数据库连接池返回的连接就可以了。

      代码如果这样写,我们从中得不到任何好处,除了能少写点代码以外,因为这个动态代理还是只能代理Connection这一个接口,我们修改代理类,使它能代理多个类。

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    
    public class DynamicProxy implements InvocationHandler{
        
        private Object source;
        
        public DynamicProxy(Object source) {
            super();
            this.source = source;
        }
        
        public void before(){
            System.out.println("在方法前做一些事,比如打开事务");
        }
        
        public void after(){
            System.out.println("在方法返回前做一些事,比如提交事务");
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //假设我们切入toString方法,其他其实也是类似的,一般我们这里大部分是针对特定的方法做事情的,通常不会对类的全部方法切入
            //比如我们常用的事务管理器,我们通常配置的就是对save,update,delete等方法才打开事务
            if (method.getName().equals("toString")) {
                before();
            }
            Object result = method.invoke(source, args);
            if (method.getName().equals("toString")) {
                after();
            }
            return result;
        }
        
        public Object getProxy(){
            return Proxy.newProxyInstance(getClass().getClassLoader(), source.getClass().getInterfaces(), this);
        }
        
        
    }

      这个代理类的作用是可以代理任何类,因为它被传入的对象是Object,而不再是具体的类,比如刚才的Connection,这些产生的代理类在调用toString方法时会被插入before方法和after方法。

      动态代理有一个强制性要求,就是被代理的类必须实现了某一个接口,或者本身就是接口,就像我们的Connection。

    三、Cglib代理

      如何目标对象没有实现任何接口,自己本身也不是接口应该怎么办呢?Cglib代理可以实现。

      Cglib子类代理实现方法:

    1. 需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入pring-core-x.x.x.jar即可。
    2. 引入功能包后,就可以在内存中动态构建子类。
    3. 代理的类不能为final,否则报错。
    4. 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。

      简单写一个例子。

    /**
     * 目标对象,没有实现任何接口
     * @author xiaodongdong
     * @create 2018-05-02 17:10
     **/
    public class UserDao {
    
        public void save() {
            System.out.println("----已经保存数据!----");
        }
    }

      接下来是代理工厂。

    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * 代理工厂
     * @author xiaodongdong
     * @create 2018-05-02 17:11
     **/
    public class ProxyFactory implements MethodInterceptor {
        //维护目标对象
        private Object target;
    
        public ProxyFactory(Object target) {
            this.target = target;
        }
    
        //给目标对象创建一个代理对象
        public Object getProxyInstance(){
            //1.工具类
            Enhancer en = new Enhancer();
            //2.设置父类
            en.setSuperclass(target.getClass());
            //3.设置回调函数
            en.setCallback(this);
            //4.创建子类(代理对象)
            return en.create();
    
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("开始事务...");
    
            //执行目标对象的方法
            Object returnValue = method.invoke(target, args);
    
            System.out.println("提交事务...");
    
            return returnValue;
        }
    }

      测试类如下。

    import org.junit.Test;
    
    /**
     * 测试类
     */
    public class App {
    
        @Test
        public void test(){
            //目标对象
            UserDao target = new UserDao();
    
            //代理对象
            UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
    
            //执行代理对象的方法
            proxy.save();
        }
    }

      执行结果:

     

      代理模式目前能整理到的也就是这些,欢迎各路大神批评补充,感谢。

  • 相关阅读:
    hdu 2222 Keywords Search
    Meet and Greet
    hdu 4673
    hdu 4768
    hdu 4747 Mex
    uva 1513 Movie collection
    uva 12299 RMQ with Shifts
    uva 11732 strcmp() Anyone?
    uva 1401
    hdu 1251 统计难题
  • 原文地址:https://www.cnblogs.com/peterxiao/p/8979883.html
Copyright © 2011-2022 走看看