侧边栏壁纸
博主头像
惊羽博主等级

hi ,我是惊羽,前生物学逃兵,现系统工程沉迷者 . 贝壳签约工程师 , 曾被雇佣为 联拓数科 · 支付研发工程师 、京东 · 京东数科 · 研发工程师、中国移动 · 雄安产业研究院 · 业务中台技术负责人 .

  • 累计撰写 101 篇文章
  • 累计创建 14 个标签
  • 累计收到 14 条评论

设计模式(2) - 模板

惊羽
2021-06-18 / 0 评论 / 0 点赞 / 119 阅读 / 7,348 字
温馨提示:
本文为原创作品,感谢您喜欢~

高扩展性场景考虑使用模板设计模式

例如 java中,jdbc连接不同的数据库服务的实现.

sun公司在最初想要用java实现集成连接所有数据厂商的数据服务软件,不过后来发现这是一件庞大到几乎不可能完成的事情.于是sun公司和数据服务厂商联合会议制定了jdbc接口规范,java负责给出数据库连接的规范,而不同的厂商则负责实现连接自身数据服务软件的部分即可,后来这部分被称作"驱动".

其大致思路是

java 在java.sql包中,给出了 Driver 接口规范,同时实现了 DriverManager 类,用来负责数据库驱动的注册(registerDriver),数据库连接的获取(getConnection);而Driver和Connection本身是接口类,其具体实现则有不同的厂商自己去实现,他们只需要继承这两个接口类,那么我们就能够在我们的application中调用数据服务,而不用去关心其具体实现细节.

(1)客户端部分

java.sql.Driver 部分源码 :

package java.sql;
public interface Driver {
	 // 获取连接
	 Connection connect(String url, java.util.Properties info)  throws SQLException;

}

java.sql.DriverManager 部分源码 :

package java.sql;
	// 注册驱动静态方法
    public static synchronized void registerDriver(java.sql.Driver driver)
        throws SQLException {
        registerDriver(driver, null);
    }
	
	 ...
	
	// 获取连接
	private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }

        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }

        println("DriverManager.getConnection(\"" + url + "\")");

        // Walk through the loaded registeredDrivers attempting to make a connection.
        // Remember the first exception that gets raised so we can reraise it.
        SQLException reason = null;
		
		// 在注册Driver集合中,找到我们注册的数据服务厂商提供的实现类,这样我们就能用Driver调用getConnection的API,获取数据库的链接资源
        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }

        }

        // if we got here nobody could connect.
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }

在最新版本的 jdbc(com.mysql.cj.jdbc) 中,调用了DriverManager.registerDriver(new Driver())这一方法,来注册驱动;
注册驱动后,在调用了DriverManager.getConnection() API中,遍历注册Driver集合,找到我们可用的第三方实现的Driver实现子类,调用其提供的connect() API获取连接即可建立与数据服务软件的连接;

(2)驱动部分

一 . mysql官方驱动 (registerDriver 和 connect 分别在com.mysql.cj.jdbc.Driver类 和父类 (NonRegisteringDriver) 中实现)

package com.mysql.cj.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

	// 静态代码块注册驱动;
    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

其父类 NonRegisteringDriver :

package com.mysql.cj.jdbc;
	......
public class NonRegisteringDriver implements Driver {
	......
	// 获取连接的API
   public Connection connect(String url, Properties info) throws SQLException {
        try {
            try {
                ConnectionUrl conStr = ConnectionUrl.getConnectionUrlInstance(url, info);
                if(conStr.getType() == null) {
                    return null;
                } else {
                    switch(null.$SwitchMap$com$mysql$cj$conf$ConnectionUrl$Type[conStr.getType().ordinal()]) {
                    case 1:
                        return LoadBalancedConnectionProxy.createProxyInstance((LoadbalanceConnectionUrl)conStr);
                    case 2:
                        return FailoverConnectionProxy.createProxyInstance(conStr);
                    case 3:
                        return ReplicationConnectionProxy.createProxyInstance((ReplicationConnectionUrl)conStr);
                    case 4:
                    default:
                        return ConnectionImpl.getInstance(conStr.getMainHost());
                    }
                }
            } catch (CJException var5) {
                throw (UnableToConnectException)ExceptionFactory.createException(UnableToConnectException.class, Messages.getString("NonRegisteringDriver.17", new Object[]{var5.toString()}), var5);
            }
        } catch (CJException var6) {
            throw SQLExceptionsMapping.translateException(var6);
        }
    }
}	
	......

二 . 第三方驱动
我们以阿里的DruidDriver(Druid驱动)中的实现为例 ,部分源码:

package com.alibaba.druid.proxy;
	
	// 提供了注册驱动的API
	public static boolean registerDriver(Driver driver) {
        try {
            DriverManager.registerDriver(driver);

            try {
                MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
                ObjectName objectName = new ObjectName("com.alibaba.druid:type=DruidDriver");
                if(!mbeanServer.isRegistered(objectName)) {
                    mbeanServer.registerMBean(instance, objectName);
                }
            } catch (Throwable var3) {
                if(LOG == null) {
                    LOG = LogFactory.getLog(DruidDriver.class);
                }

                LOG.warn("register druid-driver mbean error", var3);
            }

            return true;
        } catch (Exception var4) {
            if(LOG == null) {
                LOG = LogFactory.getLog(DruidDriver.class);
            }

            LOG.error("registerDriver error", var4);
            return false;
        }
    }
	......
	// 获取连接的API
    public Connection connect(String url, Properties info) throws SQLException {
        if(!this.acceptsURL(url)) {
            return null;
        } else {
            this.connectCount.incrementAndGet();
            DataSourceProxyImpl dataSource = this.getDataSource(url, info);
            return dataSource.connect(info);
        }
    }

我们可以看到,通过继承或实现java.sql包下的Driver,并编写内部的逻辑,用户可以自由选择应用不同的Driver子类实例,并用该实例获取不同的Connection子类实例,达到访问不同数据服务的目的;而对于这些Connection实例的管理,又可以用池化的思想提升其利用率,优化使用性能

③可以向池化思想靠近;

在某些场景中某些类只需要被实例化一次,后续所有客户端只需要公用此同一实例时,静态构造方法内部可以实现局部的缓存,以便为整个应用提供一个单例的实体,避免了相同功能的实例的重复创健,销毁过程中的开销.

在②中提到,数据库建立连接后,对于这些Connection的生命周期的管理,便演变成了数据库连接池,关于连接池的一些应用,以后有时间再看看源码.

0
广告 广告

评论区