Java Tomcat基础版数据连接池实现(仅供参考)

最新在阅读公司自己写的数据连接池(比较简单的),参考其仿照写了一个数据库连接池,由于过于简单,取名SimpleDataSourceFactory,在tomcat上使用的时候需如下配置:

<GlobalNamingResources>
<Resource name="jdbc/ctpDataSource" auth="Container" factory="com.seeyon.ctp.dbpool.SimpleDataSourceFactory" type="javax.sql.DataSource" />
</GlobalNamingResources>

完整代码如下:

/**
 * Author ouyp 
 * Rev 
 * Date: 2018年8月2日 下午3:00:42
 *
 * Copyright (C) 20182017 Seeyon, Inc. All rights reserved.
 *
 * This software is the proprietary information of Seeyon, Inc.
 * Use is subject to license terms.
 */
package com.seeyon.ctp.dbpool;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import javax.sql.DataSource;

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;

import com.seeyon.v3x.dbpool.util.PwdEncoder;

/**
 * <p>Title: 简单版数据连接池工厂</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2018</p>
 * <p>Company: seeyon.com</p>
 * <p>Since Seeyon V7.0</p>
 */
public class SimpleDataSourceFactory implements ObjectFactory {
    private static final Log log = LogFactory.getLog(SimpleDataSourceFactory.class);
    /**
     * 数据源名称
     */
    private String name;
    /**
     * Jdbc地址
     */
    private String url;
    /**
     * jdbc驱动
     */
    private String driverClassName;
    /**
     * 数据库账号
     */
    private String username;
    /**
     * 数据库密码
     */
    private String password;
    /**
     * 最小连接数
     */
    private int minCount = 0;
    /**
     * 最大连接数
     */
    private int maxCount = 2000;
    /**
     * 尝试连接次数
     */
    private int tryCount = 5;
    /**
     * 尝试连接等待时间
     */
    private int tryWait = 100;

    private static final String datasourceCtp = "A8.datasource.properies.filepath";
    private static final Logger logger = Logger.getLogger(SimpleDataSourceFactory.class.getCanonicalName());

    /**
     * 弱口令
     */
    private Set<String> weakPassword = new HashSet<String>(){
        private static final long serialVersionUID = 1L;
        {
            add("123456");
            add("111111");
            add("222222");
            add("333333");
            add("444444");
            add("555555");
            add("666666");
            add("777777");
            add("888888");
            add("999999");
            add("000000");
            add("123123");
            add("abc123");
        }
    };

    /**
     * <description>初始化</description>
     *
     * @author: ouyp
     * @since: Seeyon V7.0
     * @date: 2018年8月2日 下午4:35:15
     */
    private void initialize(Name name) {
        this.name = name.get(0);
        Properties p = getProperties(System.getProperty(datasourceCtp));

        String property = p.getProperty(name + ".url");
        if (property != null && property.length() > 0) {
            url = property;
        }
        property = p.getProperty(name + ".driverClassName");
        if (property != null && property.length() > 0) {
            driverClassName = property;
        }
        property = p.getProperty(name + ".username");
        if (property != null && property.length() > 0) {
            username = property;
        }
        property = p.getProperty(name + ".password");
        if (property != null && property.length() > 0) {
            password = PwdEncoder.decode(property);
        }
        property = p.getProperty(name + ".minCount");
        if (property != null && property.length() > 0) {
            minCount = Integer.parseInt(property);
        }
        property = p.getProperty(name + ".maxCount");
        if (property != null && property.length() > 0) {
            maxCount = Integer.parseInt(property);
        }
        property = p.getProperty(name + ".tryCount");
        if (property != null && property.length() > 0) {
            tryCount = Integer.parseInt(property);
        }
        property = p.getProperty(name + ".tryWait");
        if (property != null && property.length() > 0) {
            tryWait = Integer.parseInt(property);
        }
    }

    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment)
            throws Exception {
        /**
         * 暂停1分钟
         */
        TimeUnit.MINUTES.sleep(1);
        /**
         * 初始化工厂
         */
        initialize(name);

        try {
            /**
             * 注入DriverClass
             */
            Class.forName(driverClassName);

            /**
             * 弱口令检测
             */
            if (weakPassword.contains(password)) {
                String msg = "\r\n*****************************************************\r\n" + 
                        "*\r\n" + 
                        "*  数据库密码存在弱口令风险,系统无法启动\r\n" + 
                        "*  密码要求:长度6位以上,数字字母混合组成\r\n" + 
                        "*\r\n" + 
                        "*  连接池名称:" + this.name + "\r\n" + 
                        "*  数据库驱动:" + this.driverClassName + "\r\n" + 
                        "*  数据库URL: " + this.url + "\r\n" + 
                        "*  数据库账号:" + this.username + "\r\n" + 
                        "*\r\n" + 
                        "*****************************************************";
                log.error(msg);
            }

            /**
             * 检查数据库连接是否可以连接
             */
            Connection conn = null;
            Exception ee = null;
            for (int i = 0; i < 10; i ++) {
                try {
                    conn = getConnection();
                    if (conn != null) {
                        break;
                    }
                } catch (Exception e) {
                    ee = e;
                    log.warn("第" + (i + 1) + "次尝试连接失败,等待10s后重试(还有" + (9 - i) + "次重试机会)...");
                } finally {
                    if (conn != null) {
                        conn.close();
                    }
                }
            }
            if (conn == null) {
                log.error("第10次尝试连接失败,无法连接数据库,系统无法启动.");
                throw ee;
            }
        } catch (Exception e) {
            log.error("无法初始化数据库连接池,系统无法启动");
            Thread.sleep(10 * 1000L);
            System.exit(-1);
        }
        return new SimpleDataSource();
    }

    /**
     * <description>获取数据连接</description>
     *
     * @return
     * @throws SQLException
     * @author: ouyp
     * @since: Seeyon V7.0
     * @date: 2018年8月2日 下午4:52:46
     */
    private Connection getConnection() throws SQLException {
        Connection conn = DriverManager.getConnection(url, username, password);
        conn.setAutoCommit(true);
        return conn;
    }

    /**
     * <description>多次请求确保能成功获取</description>
     *
     * @return
     * @author: ouyp
     * @since: Seeyon V7.0
     * @date: 2018年8月2日 下午8:18:12
     */
    private ConnectionResult getAquiredConnection() {
        ConnectionResult result = new ConnectionResult();
        for (int i = 0; i < tryCount; i ++) {
            try {
                Connection connection = getConnection();
                if (connection != null) {
                    result.connection = connection;
                    result.success = true;
                    result.tryTimes = i;
                    break;
                }
                Thread.sleep(tryWait);
            } catch (InterruptedException e) {
                //Igore
            } catch (SQLException e) {
                if (i == tryCount - 1) {
                    result.success = false;
                    result.exp = e;
                }
            }
        }
        return result;
    }

    /**
     * <description>获取配置文件信息</description>
     *
     * @param file
     * @return
     * @author: ouyp
     * @since: Seeyon V7.0
     * @date: 2018年8月2日 下午3:30:53
     */
    private Properties getProperties(String file) {
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream(file));
        } catch (IOException e) {
            log.error("配置文件" + file + "加载失败:", e);
        }
        return properties;
    }

    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getDriverClassName() {
        return driverClassName;
    }
    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public int getMinCount() {
        return minCount;
    }
    public void setMinCount(int minCount) {
        this.minCount = minCount;
    }
    public int getMaxCount() {
        return maxCount;
    }
    public void setMaxCount(int maxCount) {
        this.maxCount = maxCount;
    }
    public int getTryCount() {
        return tryCount;
    }
    public void setTryCount(int tryCount) {
        this.tryCount = tryCount;
    }
    public int getTryWait() {
        return tryWait;
    }
    public void setTryWait(int tryWait) {
        this.tryWait = tryWait;
    }

    /**
     * <p>Title: 简单版数据连接池</p>
     * <p>Description: </p>
     * <p>Copyright: Copyright (c) 2018</p>
     * <p>Company: seeyon.com</p>
     * <p>Since Seeyon V7.0</p>
     */
    class SimpleDataSource implements DataSource {
        /**
         * 正在使用的数据连接
         */
        private ConcurrentHashMap<Long, ProxyConnection> usedList = new ConcurrentHashMap<Long, ProxyConnection>();
        /**
         * 空闲状态的数据连接
         */
        private LinkedBlockingDeque<ProxyConnection> idleList = new LinkedBlockingDeque<ProxyConnection>();
        /**
         * 正常使用数量
         */
        private AtomicInteger usedCount = new AtomicInteger(0);
        /**
         * 闲置数量
         */
        private AtomicInteger idleCount = new AtomicInteger(0);
        /**
         * 峰值
         */
        private volatile int peakCount = 0;

        private PrintWriter out;
        private int loginTimeout;

        public SimpleDataSource() throws Exception {
            for (int i = 0; i < minCount; i ++) {
                ConnectionResult result = getAquiredConnection();
                if (result.success) {
                    ProxyConnection pc = new ProxyConnection(result.connection, this);
                    idleList.addLast(pc);
                    idleCount.incrementAndGet();
                }
                if ((i+1) % 10 == 0) {
                    if (i == 9) {
                        log.info("" + (i + 1));
                    } else {
                        log.info("," + (i + 1));
                    }
                }
            }
            log.info("连接池初始化完毕!");
        }

        @Override
        public PrintWriter getLogWriter() throws SQLException {
            return out;
        }

        @Override
        public void setLogWriter(PrintWriter out) throws SQLException {
            this.out = out;
        }

        @Override
        public void setLoginTimeout(int seconds) throws SQLException {
            this.loginTimeout = seconds;
        }

        @Override
        public int getLoginTimeout() throws SQLException {
            return loginTimeout;
        }

        @Override
        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
            return logger;
        }

        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            return null;
        }

        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return false;
        }

        @Override
        public Connection getConnection() throws SQLException {
            ProxyConnection conn = null;
            try {
                do {
                    conn = idleList.pollFirst(tryCount * tryWait, TimeUnit.MILLISECONDS);
                    if (conn != null) {
                        idleCount.decrementAndGet();
                    } else{
                        break;
                    }
                } while(conn == null || conn.isClosed());
            } catch (InterruptedException e) {
                log.warn(e.getLocalizedMessage());
            }
            if (conn == null) {
                if (usedCount.intValue() >= maxCount) {
                    throw new RuntimeException("数据库连接紧张,超过最大连接数,请稍后重试!");
                }
                ConnectionResult result = getAquiredConnection();
                if (result.success) {
                    conn = new ProxyConnection(result.connection, this);
                    if (result.tryTimes > 0) {
                        log.warn("数据库连接不稳定,请及时检查!");
                    }
                    usedList.put(conn.getUuid(), conn);
                    usedCount.incrementAndGet();
                } else {
                    throw result.exp;
                }
            }

            {
                int usedInt = usedCount.intValue();
                if (usedInt > peakCount) {
                    peakCount = usedInt;
                }

            }

            return conn;
        }

        /**
         * <description>释放数据连接</description>
         *
         * @author: ouyp
         * @since: Seeyon V7.0
         * @date: 2018年8月2日 下午8:58:21
         */
        public void releaseConnection(ProxyConnection conn) {
            usedList.remove(conn.getUuid());
            usedCount.decrementAndGet();
            idleList.addLast(conn);
            idleCount.incrementAndGet();
        }

        @Override
        public Connection getConnection(String username, String password) throws SQLException {
            throw new UnsupportedOperationException();
        }
    }
    /**
     * <p>Title: 数据连接</p>
     * <p>Description: </p>
     * <p>Copyright: Copyright (c) 2018</p>
     * <p>Company: seeyon.com</p>
     * <p>Since Seeyon V7.0</p>
     */
    class ProxyConnection implements java.sql.Connection {
        /**
         * 原始数据连接
         */
        private Connection source;
        /**
         * 创建时间
         */
        private long createTime;
        /**
         * 数据源
         */
        private SimpleDataSource datasource;
        /**
         * 唯一标志
         */
        private long uuid = UUID.randomUUID().getMostSignificantBits();

        public ProxyConnection(Connection source, SimpleDataSource datasource) {
            super();
            this.source = source;
            this.createTime = System.currentTimeMillis();
            this.datasource = datasource;
        }

        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            return source.unwrap(iface);
        }
        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return source.isWrapperFor(iface);
        }
        @Override
        public Statement createStatement() throws SQLException {
            return source.createStatement();
        }
        @Override
        public PreparedStatement prepareStatement(String sql) throws SQLException {
            return source.prepareStatement(sql);
        }
        @Override
        public CallableStatement prepareCall(String sql) throws SQLException {
            return source.prepareCall(sql);
        }
        @Override
        public String nativeSQL(String sql) throws SQLException {
            return source.nativeSQL(sql);
        }
        @Override
        public void setAutoCommit(boolean autoCommit) throws SQLException {
            source.setAutoCommit(autoCommit);
        }
        @Override
        public boolean getAutoCommit() throws SQLException {
            return source.getAutoCommit();
        }
        @Override
        public void commit() throws SQLException {
            source.commit();
        }
        @Override
        public void rollback() throws SQLException {
            source.rollback();
        }
        @Override
        public void close() throws SQLException {
            datasource.releaseConnection(this);
        }
        @Override
        public boolean isClosed() throws SQLException {
            return false;
        }
        @Override
        public DatabaseMetaData getMetaData() throws SQLException {
            return source.getMetaData();
        }
        @Override
        public void setReadOnly(boolean readOnly) throws SQLException {
            source.setReadOnly(readOnly);
        }
        @Override
        public boolean isReadOnly() throws SQLException {
            return source.isReadOnly();
        }
        @Override
        public void setCatalog(String catalog) throws SQLException {
            source.setCatalog(catalog);
        }
        @Override
        public String getCatalog() throws SQLException {
            return source.getCatalog();
        }
        @Override
        public void setTransactionIsolation(int level) throws SQLException {
            source.setTransactionIsolation(level);
        }
        @Override
        public int getTransactionIsolation() throws SQLException {
            return source.getTransactionIsolation();
        }
        @Override
        public SQLWarning getWarnings() throws SQLException {
            return source.getWarnings();
        }
        @Override
        public void clearWarnings() throws SQLException {
            source.clearWarnings();
        }
        @Override
        public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
            return source.createStatement();
        }
        @Override
        public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
                throws SQLException {
            return source.prepareStatement(sql, resultSetType, resultSetConcurrency);
        }
        @Override
        public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
            return source.prepareCall(sql, resultSetType, resultSetConcurrency);
        }
        @Override
        public Map<String, Class<?>> getTypeMap() throws SQLException {
            return source.getTypeMap();
        }
        @Override
        public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
            source.setTypeMap(map);
        }
        @Override
        public void setHoldability(int holdability) throws SQLException {
            source.setHoldability(holdability);
        }
        @Override
        public int getHoldability() throws SQLException {
            return source.getHoldability();
        }
        @Override
        public Savepoint setSavepoint() throws SQLException {
            return source.setSavepoint();
        }
        @Override
        public Savepoint setSavepoint(String name) throws SQLException {
            return source.setSavepoint(name);
        }
        @Override
        public void rollback(Savepoint savepoint) throws SQLException {
            source.rollback(savepoint);
        }
        @Override
        public void releaseSavepoint(Savepoint savepoint) throws SQLException {
            source.releaseSavepoint(savepoint);
        }
        @Override
        public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
                throws SQLException {
            return source.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
        }
        @Override
        public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
                int resultSetHoldability) throws SQLException {
            return source.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
        }
        @Override
        public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
                int resultSetHoldability) throws SQLException {
            return source.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
        }
        @Override
        public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
            return source.prepareStatement(sql, autoGeneratedKeys);
        }
        @Override
        public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
            return source.prepareStatement(sql, columnIndexes);
        }
        @Override
        public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
            return source.prepareStatement(sql, columnNames);
        }
        @Override
        public Clob createClob() throws SQLException {
            return source.createClob();
        }
        @Override
        public Blob createBlob() throws SQLException {
            return source.createBlob();
        }
        @Override
        public NClob createNClob() throws SQLException {
            return source.createNClob();
        }
        @Override
        public SQLXML createSQLXML() throws SQLException {
            return source.createSQLXML();
        }
        @Override
        public boolean isValid(int timeout) throws SQLException {
            return source.isValid(timeout);
        }
        @Override
        public void setClientInfo(String name, String value) throws SQLClientInfoException {
            source.setClientInfo(name, value);
        }
        @Override
        public void setClientInfo(Properties properties) throws SQLClientInfoException {
            source.setClientInfo(properties);
        }
        @Override
        public String getClientInfo(String name) throws SQLException {
            return source.getClientInfo(name);
        }
        @Override
        public Properties getClientInfo() throws SQLException {
            return source.getClientInfo();
        }
        @Override
        public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
            return source.createArrayOf(typeName, elements);
        }
        @Override
        public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
            return source.createStruct(typeName, attributes);
        }
        @Override
        public void setSchema(String schema) throws SQLException {
            source.setSchema(schema);
        }
        @Override
        public String getSchema() throws SQLException {
            return source.getSchema();
        }
        @Override
        public void abort(Executor executor) throws SQLException {
            source.abort(executor);
        }
        @Override
        public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
            source.setNetworkTimeout(executor, milliseconds);
        }
        @Override
        public int getNetworkTimeout() throws SQLException {
            return source.getNetworkTimeout();
        }
        public Connection getSource() {
            return source;
        }
        public void setSource(Connection source) {
            this.source = source;
        }
        public long getCreateTime() {
            return createTime;
        }
        public void setCreateTime(long createTime) {
            this.createTime = createTime;
        }
        public SimpleDataSource getDatasource() {
            return datasource;
        }
        public void setDatasource(SimpleDataSource datasource) {
            this.datasource = datasource;
        }
        public long getUuid() {
            return uuid;
        }
        public void setUuid(long uuid) {
            this.uuid = uuid;
        }
    }

    /**
     * <p>Title: 数据连接封装对象</p>
     * <p>Description: </p>
     * <p>Copyright: Copyright (c) 2018</p>
     * <p>Company: seeyon.com</p>
     * <p>Since Seeyon V7.0</p>
     */
    class ConnectionResult {
        /**
         * 是否获取成功
         */
        private boolean success;
        /**
         * 数据连接
         */
        private Connection connection;
        /**
         * 连接次数
         */
        private int tryTimes;
        /**
         * 异常信息
         */
        private SQLException exp;
    }
}

内部系统会对数据库密码有加密措施,再此就不提供源码了.

猜你喜欢

转载自blog.csdn.net/cockroach02/article/details/81393368