首先明确闭包的概念:一个代码段被用来做为方法的参数.
java中没有直接使用某个方法做为另一个方法的参数的,java使用匿名内部类来模拟这种情况。
匿名内部类往往是做为一个内部类(接口)的具体实现。在一些平台类(platform class)中有一些模板方法。模板方法的包含了固定流程。其中某些步骤是调用了内部类(接口)中的某些方法。但是平台类将这些方法的具体实现延迟到了业务类中。业务类调用平台类的模板方法,但是传入匿名内部类的实现做为模板方法的参数。
package callback;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class AnonymousBusinessTemplateExample2 {
// 内部接口也是回调接口,只定义抽象方法。
private interface Callback {
Object doIt(Connection conn) throws SQLException;
}
// 模板方法(抽象)
private Object execute(Callback callback) throws SQLException {
Connection conn = openConnection();
try {
return callback.doIt(conn);
} finally {
closeConnection(conn);
}
}
// 业务方法(具体)
public Object sqlQuery(final String sql) throws SQLException {
//匿名内部类做为模板方法的参数来模拟闭包
return execute(new Callback() {
public Object doIt(Connection conn) throws SQLException {
return conn.createStatement().executeQuery(sql);
}
});
}
public Connection openConnection() throws SQLException {
return DriverManager.getConnection("", null);
}
public void closeConnection(Connection conn) throws SQLException {
if (conn != null && !conn.isClosed()) {
conn.close();
}
}
}
公司目前采用了spring框架来构建和管理整个web项目。对于持久层的处理,使用了由spring框架提供的对hibernate3的封装。这样做无非是为了使用spring提供的对事务的统一管理。当我们用到由spring所封装的hibernate的时候一定会用到一个类:HibernateTemplate.这是一个对持久层处理封装的非常完整的类,包括对session的管理(事实上session的获取于释放是一个令人头疼的问题)等等,我们通常会使用HibernateTemplate的excute方法来进行数据库操作(即使我们调用的也许是别的类似于find、get之类的方法,但是实质上最终还是转变为了调用excute方法)。对于第一次使用这个方法一定会存在困扰。因为excute方法所需要的参数一个HibernateCallback类型的参数。而在excute方法体内部回调了HibernateCallback类型的doInHibernate方法。这是一个典型的对象回调。就到目前为止也许一切都很清晰。但是实际上如果阅读了HibernateTemplate的内部代码就会发现,对于像get、find这样的方法最终都回调用excute来完成数据库操作但是调用形式看起来却很奇怪:
public Object get(final Class entityClass, final Serializable id, final LockMode lockMode)
throws DataAccessException
{
return execute(new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException
{
if(lockMode != null)
return session.get(entityClass, id, lockMode);
else
return session.get(entityClass, id);
}
}, true);
}
Excute方法的参数是一种匿名类的方式。为什么要采用匿名类呢(不管怎么说匿名类看起来总是让人觉得不舒服)?这个地方是否必须采用匿名类呢?
首先我们来想一想这段代码涉及到几个关键点:1、回调:excute方法会回调HibernateCallback类型的doInHibernate方法;2、匿名类参数:我们为excute方法提供的参数并不是一个真正生命出来的HibernateCallback实例。3、动态创建回调方法:如果我们打开HibernateCallback类就会发现,其实这是一个abstract类型的类,而他声明了唯一的一个ie抽象方法就是doInHibernate。问题似乎已经明朗了,如果不采用匿名类,我们需要做的是为HibernateCallback创建一个实现类,并且实现doInHibernate方法。但是最要命的问题是doInHibernate方法的实现对于我们的实际需求来说每一次调用可能都是不一样的(在doInHibernate方法中我们使用session进行数据库操作,对于不同的业务逻辑,方法实现必定是不一样的),采用了匿名类我们不用在代码重创建新的类型,而且可以动态的创建我们所需要的回调函数。
总结一下,我们上面所讲的并非是如何使用HibernateTemplate这个类。我们得到的结论是:当我们需要动态的创建回调函数的时候,匿名内部类是一个好的方式。注:这种需要动态创建的回调方法通常是一个interface中的接口或者abstract class中的抽象方法。