BoneCP官网上有其配置的使用文档,看最基本的Manual configuration:
Class.forName("org.hsqldb.jdbcDriver"); // load the DB driver
BoneCPConfig config = new BoneCPConfig(); // create a new configuration object
config.setJdbcUrl("jdbc:hsqldb:mem:test"); // set the JDBC url
config.setUsername("sa"); // set the username
config.setPassword(""); // set the password
config.setXXXX(...); // (other config options here)
BoneCP connectionPool = new BoneCP(config); // setup the connection pool
Connection connection;
connection = connectionPool.getConnection(); // fetch a connection
... do something with the connection here ...
connection.close(); // close the connection
connectionPool.shutdown(); // close the connection pool
其使用步骤为先加载数据库驱动,然后声明一个配置文件描述类BoneCPConfig,再根据该类实例化一个连接池实例BoneCP,根据该实例的getConnection()方法可得到一个数据库连接对象Connection,可进行数据库操作,最后关闭Connection对象和连接池。
BoneCPConfig为配置文件类(Configuration class),它在com.jolbox.bonecp包下:
package com.jolbox.bonecp;
/**
* Configuration class.
*
* @author wallacew
*/
public class BoneCPConfig implements BoneCPConfigMBean, Cloneable, Serializable {
......
}
BoneCPConfigMBean为配置接口,MBean interface for config。
BoneCP有四个构造函数:
/**
* Default constructor. Attempts to fill settings in this order:
* 1. bonecp-default-config.xml file, usually found in the pool jar
* 2. bonecp-config.xml file, usually found in your application's classpath
* 3. Other hardcoded defaults in BoneCPConfig class.
*/
public BoneCPConfig(){
// try to load the default config file, if available from somewhere in the classpath
loadProperties("bonecp-default-config.xml");
// try to override with app specific config, if available
loadProperties("bonecp-config.xml");
}
/** Creates a new config using the given properties.
* @param props properties to set.
* @throws Exception on error
*/
public BoneCPConfig(Properties props) throws Exception {
this();
this.setProperties(checkNotNull(props));
}
/** Initialize the configuration by loading bonecp-config.xml containing the settings.
* @param sectionName section to load
* @throws Exception on parse errors
*/
public BoneCPConfig(String sectionName) throws Exception{
this(BoneCPConfig.class.getResourceAsStream("/bonecp-config.xml"), checkNotNull(sectionName));
}
/** Initialise the configuration by loading an XML file containing the settings.
* @param xmlConfigFile file to load
* @param sectionName section to load
* @throws Exception
*/
public BoneCPConfig(InputStream xmlConfigFile, String sectionName) throws Exception{
this();
setXMLProperties(xmlConfigFile, checkNotNull(sectionName));
}
上面构造函数提供了基于XML文件和Properties两种配置方式,默认构造函数去加载根目录下的默认配置文件bonecp-default-config.xml,该配置文件随着jar包一起发布,在根目录下:
/**
* Loads the given properties file using the classloader.
* @param filename Config filename to load
*
*/
protected void loadProperties(String filename) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader != null){
URL url = classLoader.getResource(filename);
if (url != null){
try {
this.setXMLProperties(url.openStream(), null);
} catch (Exception e) {
// do nothing
}
}
}
}
对于以XML文件为参数的构造函数,先调用javax.xml包下的类对XML文件解析,然后再把XML文件转换成Properties形式去实例化(调用setProperties(Properties props)方法):
/**
* @param xmlConfigFile
* @param sectionName
* @throws Exception
*/
private void setXMLProperties(InputStream xmlConfigFile, String sectionName)
throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db;
// ugly XML parsing, but this is built-in the JDK.
try {
db = dbf.newDocumentBuilder();
Document doc = db.parse(xmlConfigFile);
doc.getDocumentElement().normalize();
// get the default settings
Properties settings = parseXML(doc, null);
if (sectionName != null){
// override with custom settings
settings.putAll(parseXML(doc, sectionName));
}
// set the properties
setProperties(settings);
} catch (Exception e) {
throw e;
} finally {
if (xmlConfigFile != null){ // safety
xmlConfigFile.close();
}
}
}
对于setProperties(Properties props)方法是把配置文件读取并设置到字段的实现,先用Java返射机会,读出BoneCPConfig类中所有设置属性的方法,以is开头或set开头的方法:
for (Method method: BoneCPConfig.class.getDeclaredMethods()){
String tmp = null;
if (method.getName().startsWith("is")){
tmp = lowerFirst(method.getName().substring(2));
} else if (method.getName().startsWith("set")){
tmp = lowerFirst(method.getName().substring(3));
} else {
continue;
}
......
}
然后再根据方法的参数个数和参数类型进行赋值, BoneCPConfig将属性字段类型限制为4种类型:int、long、String、boolean:
if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(int.class)){
String val = props.getProperty(tmp);
if (val == null){
val = props.getProperty("bonecp."+tmp); // hibernate provider style
}
if (val != null) {
try{
method.invoke(this, Integer.parseInt(val));
} catch (NumberFormatException e){
// do nothing, use the default value
}
}
} else if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(long.class)){
String val = props.getProperty(tmp);
if (val == null){
val = props.getProperty("bonecp."+tmp); // hibernate provider style
}
if (val != null) {
try{
method.invoke(this, Long.parseLong(val));
} catch (NumberFormatException e){
// do nothing, use the default value
}
}
} else if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(String.class)){
String val = props.getProperty(tmp);
if (val == null){
val = props.getProperty("bonecp."+tmp); // hibernate provider style
}
if (val != null) {
method.invoke(this, val);
}
} if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(boolean.class)){
String val = props.getProperty(tmp);
if (val == null){
val = props.getProperty("bonecp."+tmp); // hibernate provider style
}
if (val != null) {
method.invoke(this, Boolean.parseBoolean(val));
}
}
个人认为这段代码写得不够优雅,至少相同部分可以提取出来,从设计的角度说这里也最好采用工厂方式便于多种实现。