Server可能与多个系统建立连接,当Shutdown的时候需要释放资源。比如数据库连接,Socket连接等等。当然也可能需要释放本地资源。本文主要展示如何运用Command设计模式来实现服务器资源的释放。得到的效果是不同的资源释放过程具有独立性,新增释放过程不会影响到原来的架构设计。
首先定义一个释放资源的统一接口Releaser,为Command模式中的Command。所有释放资源的具体类都实现该接口。代码如下:
/**
* 释放资源的接口
*/
public interface Releaser {
void release();
}
接下来设计一个命令管理器,统一管理释放资源的命令。该类不是传统的Command模式的一部分。项目中用Spring框架,命令通过Spring IOC注入进来。解耦了命令与管理器。管理器代码如下:
/**
* Server正常关闭时释放资源的类
*/
public class ReleaserContainer {
private static Logger log = LoggerFactory
.getLogger(ReleaserContainer.class);
private List<Releaser> releaserList;
public ReleaserContainer() {
}
private void addReleaseHook(final Releaser releaser) {
Thread thread = new Thread() {
@Override
public void run() {
releaser.release();
}
};
Runtime.getRuntime().addShutdownHook(thread);
}
public List<Releaser> getReleaserList() {
return releaserList;
}
public void setReleaserList(List<Releaser> releaserList) {
this.releaserList = releaserList;
if (this.releaserList != null && this.releaserList.size() > 0) {
log.info("Starting add releasers' hook...");
for (Releaser releaser : this.releaserList) {
addReleaseHook(releaser);
}
log.info("Add releasers' hook completed!");
}
}
}
值得注意的是当释放资源命令注入管理器之后会马上挂到Java正常退出的钩子上。Runtime.getRuntime().addShutdownHook(thread)。
对应的Spring配置如下:
<bean id="releaserContainer" class="com.mt.kms.release.ReleaserContainer">
<property name="releaserList">
<list>
<ref bean="socketConnectionReleaser" />
<ref bean="databaseConnectionReleaser" />
</list>
</property>
</bean>
最后就是具体的去实现资源的释放命令,下面是JDBC Pool和Socket pool的释放。
/**
* 释放Database JDBC connection pool的类
*/
public class DatabaseConnectionReleaser implements Releaser,
ApplicationContextAware {
private static Logger log = LoggerFactory
.getLogger(DatabaseConnectionReleaser.class);
private ApplicationContext appContext;
@Override
public void release() {
if (appContext != null) {
// 从Spring容器中找到JDBC connection pool并释放资源
BasicDataSource dataSource = (BasicDataSource) appContext
.getBean("dataSource");
if (dataSource != null) {
try {
log.info("Starting release jdbc connection poll...");
dataSource.close();
log.info("Jdbc connection poll releasing completed!");
} catch (SQLException e) {
e.printStackTrace();
log.error("Jdbc connection poll releasing failed!", e);
}
}
}
}
@Override
public void setApplicationContext(ApplicationContext appContext)
throws BeansException {
this.appContext = appContext;
}
}
/**
* 释放Socket 连接
*/
public class SocketConnectionReleaser implements Releaser {
private static Logger log = LoggerFactory
.getLogger(SocketConnectionReleaser.class);
private DataSourceDAO dataSourceDAO;
@Override
public void release() {
log.info("Starting release socket connections...");
dataSourceDAO.releaseAllConnections();
log.info("Socket connections releasing completed!");
}
public DataSourceDAO getDataSourceDAO() {
return dataSourceDAO;
}
public void setDataSourceDAO(
DataSourceDAO dataSourceDAO) {
this.dataSourceDAO = dataSourceDAO;
}
}
Spring配置如下
<!-- Release resources after server shutdown -->
<bean id="socketConnectionReleaser" class="com.mt.kms.release.SocketConnectionReleaser" />
<bean id="databaseConnectionReleaser" class="com.mt.kms.release.DatabaseConnectionReleaser" />
总结:通过以上的设计,需要释放新的资源时,只需要实现Releaser接口并且注入到ReleaserContainer中即可,完全不需要更改原来的代码。如果没有使用Spring,可以通过XML注册释放命令的方式来实现它们之间的解耦。