詳解java模板和回調(diào)機制
最近看spring的JDBCTemplete的模板方式調(diào)用時,對模板和回調(diào)產(chǎn)生了濃厚興趣,查詢了一些資料,做一些總結。
回調(diào)函數(shù):
所謂回調(diào),就是客戶程序C調(diào)用服務程序S中的某個函數(shù)A,然后S又在某個時候反過來調(diào)用C中的某個函數(shù)B,對于C來說,這個B便叫做回調(diào)函數(shù)?;卣{(diào)函數(shù)只是一個功能片段,由用戶按照回調(diào)函數(shù)調(diào)用約定來實現(xiàn)的一個函數(shù)?;卣{(diào)函數(shù)是一個工作流的一部分,由工作流來決定函數(shù)的調(diào)用(回調(diào))時機。一般說來,C不會自己調(diào)用B,C提供B的目的就是讓S來調(diào)用它,而且是C不得不提供。由于S并不知道C提供的B姓甚名誰,所以S會約定B的接口規(guī)范(函數(shù)原型),然后由C提前通過S的一個函數(shù)R告訴S自己將要使用B函數(shù),這個過程稱為回調(diào)函數(shù)的注冊,R稱為注冊函數(shù)。Web Service以及Java 的RMI都用到回調(diào)機制,可以訪問遠程服務器程序?;卣{(diào)函數(shù)包含下面幾個特性:
1、屬于工作流的一個部分;
2、必須按照工作流指定的調(diào)用約定來申明(定義);
3、他的調(diào)用時機由工作流決定,回調(diào)函數(shù)的實現(xiàn)者不能直接調(diào)用回調(diào)函數(shù)來實現(xiàn)工作流的功能;
回調(diào)機制:
回調(diào)機制是一種常見的設計模型,他把工作流內(nèi)的某個功能,按照約定的接口暴露給外部使用者,為外部使用者提供數(shù)據(jù),或要求外部使用者提供數(shù)據(jù)。
java回調(diào)機制:
軟件模塊之間總是存在著一定的接口,從調(diào)用方式上,可以把他們分為三類:同步調(diào)用、回調(diào)和異步調(diào)用。
同步調(diào)用:一種阻塞式調(diào)用,調(diào)用方要等待對方執(zhí)行完畢才返回,它是一種單向調(diào)用;
回 調(diào):一種雙向調(diào)用模式,也就是說,被調(diào)用方在接口被調(diào)用時也會調(diào)用對方的接口;
異步調(diào)用:一種類似消息或事件的機制,不過它的調(diào)用方向剛好相反,接口的服務在收到某種訊息或發(fā)生某種事件時,會主動通知客戶方(即調(diào)用客戶方的接口)。
回調(diào)和異步調(diào)用的關系非常緊密:使用回調(diào)來實現(xiàn)異步消息的注冊,通過異步調(diào)用來實現(xiàn)消息的通知。
回調(diào)實例
1、回調(diào)接口
public interface Callback {
String callBack();
}
2、調(diào)用者
public class Another {
private Callback callback;
//調(diào)用實現(xiàn)類的方法
public void setCallback(Callback callback) {
this.callback = callback;
}
//業(yè)務需要的時候,通過委派,來調(diào)用實現(xiàn)類的具體方法
public void doCallback(){
System.out.println(callback.callBack());
}
}
3、測試回調(diào)函數(shù)
public class TestCallcack {
public static void main(String[] args) {
//創(chuàng)建調(diào)用者的實現(xiàn)類
Another another = new Another();
//將回掉接口注冊到實現(xiàn)類中
another.setCallback(new Callback() {
@Override
public String callBack() {
return "you are a pig";
}
});
//執(zhí)行回調(diào)函數(shù)
another.doCallback();
}
}
回調(diào)方法的使用通常發(fā)生在“java接口”和“抽象類”的使用過程中。模板方法設計模式就使用方法回調(diào)的機制,該模式首先定義特定的步驟的算法骨架,而將一些步驟延遲到子類中去實現(xiàn)的設計模式。模板方法設計模式使得子類可以不改變一個算法的結構即可重新定義該算法的某些特定步驟。
模板方式設計模式的適用性:
1、一次性實現(xiàn)一個算法的不變部分,并將可變的算法留給子類來實現(xiàn)。
2、各子類中公共的行為應該被提取出來并集中一個公共父類中以避免代碼重復。
3、可以控制子類擴展。
模板實例:
抽象模板方法類:
public abstract class AbstractSup {
//需要子類實現(xiàn)的方法
public abstract void print();
//模板方法
public void doPrint(){
System.out.println("執(zhí)行模板方法");
for (int i = 0; i < 3; i++) {
print();
}
}
}
子類實現(xiàn)模板方式類:
public class SubClass extends AbstractSup{
@Override
public void print() {
System.out.println("子類的實現(xiàn)方法");
}
}
模板方法測試類:
public class TempleteTest {
public static void main(String[] args) {
SubClass subClass = new SubClass();
subClass.print();
subClass.doPrint();
}
}
下面深入介紹下spring模板方法的使用,以JdbcTemplete為例,詳細說明模板模式和回調(diào)機制的使用。
首先看一下經(jīng)典的JDBC編程的例子:
public List<User> query() {
List<User> userList = new ArrayList<User>();
String sql = "select * from User";
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
con = HsqldbUtil.getConnection();
pst = con.prepareStatement(sql);
rs = pst.executeQuery();
User user = null;
while (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setUserName(rs.getString("user_name"));
user.setBirth(rs.getDate("birth"));
user.setCreateDate(rs.getDate("create_date"));
userList.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(!con.isClosed()){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return userList;
}
一個簡單的查詢,就要做這么一大堆事情,而且還要處理異常,我們不防來梳理一下:
1、獲取connection
2、獲取statement
3、獲取resultset
4、遍歷resultset并封裝成集合
5、依次關閉connection,statement,resultset,而且還要考慮各種異常等等。
如果是多個查詢會產(chǎn)生較多的重復代碼,這時候就可以使用模板機制,通過觀察我們發(fā)現(xiàn)上面步驟中大多數(shù)都是重復的,可復用的,只有在遍歷ResultSet并封裝成集合的這一步驟是可定制的,因為每張表都映射不同的java bean。這部分代碼是沒有辦法復用的,只能定制。
抽象類代碼:
public abstract class JdbcTemplate {
//模板方法
public final Object execute(String sql) throws SQLException{
Connection con = HsqldbUtil.getConnection();
Statement stmt = null;
try {
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(sql);
Object result = doInStatement(rs);//抽象方法(定制方法,需要子類實現(xiàn))
return result;
}
catch (SQLException ex) {
ex.printStackTrace();
throw ex;
}
finally {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(!con.isClosed()){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//抽象方法(定制方法)
protected abstract Object doInStatement(ResultSet rs);
}
這個抽象類中,封裝了SUN JDBC API的主要流程,而遍歷ResultSet這一步驟則放到抽象方法doInStatement()中,由子類負責實現(xiàn)。
子類實現(xiàn)代碼:
public class JdbcTemplateUserImpl extends JdbcTemplate {
@Override
protected Object doInStatement(ResultSet rs) {
List<User> userList = new ArrayList<User>();
try {
User user = null;
while (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setUserName(rs.getString("user_name"));
user.setBirth(rs.getDate("birth"));
user.setCreateDate(rs.getDate("create_date"));
userList.add(user);
}
return userList;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
}
我們在doInStatement()方法中,對ResultSet進行了遍歷,最后并返回。
測試代碼:
String sql = "select * from User"; JdbcTemplate jt = new JdbcTemplateUserImpl(); List<User> userList = (List<User>) jt.execute(sql);
模板機制的使用到此為止,但是如果每次調(diào)用jdbcTemplate時,都要繼承一下上面的父類,這樣挺不方便的,這樣回調(diào)機制就可以發(fā)揮作用了。
所謂回調(diào),就是方法參數(shù)中傳遞一個接口,父類在調(diào)用此方法時,必須調(diào)用方法中傳遞的接口的實現(xiàn)類。
回調(diào)加模板模式實現(xiàn)
回調(diào)接口:
public interface StatementCallback {
Object doInStatement(Statement stmt) throws SQLException;
}
模板方法:
public class JdbcTemplate {
//模板方法
public final Object execute(StatementCallback action) throws SQLException{
Connection con = HsqldbUtil.getConnection();
Statement stmt = null;
try {
stmt = con.createStatement();
Object result = action.doInStatement(rs);//回調(diào)方法
return result;
}
catch (SQLException ex) {
ex.printStackTrace();
throw ex;
}
finally {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(!con.isClosed()){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public Object query(StatementCallback stmt) throws SQLException{
return execute(stmt);
}
}
測試的類:
public Object query(final String sql) throws SQLException {
class QueryStatementCallback implements StatementCallback {
public Object doInStatement(Statement stmt) throws SQLException {
ResultSet rs = stmt.executeQuery(sql);
List<User> userList = new ArrayList<User>();
User user = null;
while (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setUserName(rs.getString("user_name"));
user.setBirth(rs.getDate("birth"));
user.setCreateDate(rs.getDate("create_date"));
userList.add(user);
}
return userList;
}
}
JdbcTemplate jt = new JdbcTemplate();
return jt.query(new QueryStatementCallback());
}
為什么spring不用傳統(tǒng)的模板方法,而加之以Callback進行配合呢?
試想,如果父類中有10個抽象方法,而繼承它的所有子類則要將這10個抽象方法全部實現(xiàn),子類顯得非常臃腫。而有時候某個子類只需要定制父類中的某一個方法該怎么辦呢?這個時候就要用到Callback回調(diào)了。
另外,上面這種方式基本上實現(xiàn)了模板方法+回調(diào)模式。但離spring的jdbcTemplate還有些距離。 我們上面雖然實現(xiàn)了模板方法+回調(diào)模式,但相對于Spring的JdbcTemplate則顯得有些“丑陋”。Spring引入了RowMapper和ResultSetExtractor的概念。 RowMapper接口負責處理某一行的數(shù)據(jù),例如,我們可以在mapRow方法里對某一行記錄進行操作,或封裝成entity。 ResultSetExtractor是數(shù)據(jù)集抽取器,負責遍歷ResultSet并根據(jù)RowMapper里的規(guī)則對數(shù)據(jù)進行處理。 RowMapper和ResultSetExtractor區(qū)別是,RowMapper是處理某一行數(shù)據(jù),返回一個實體對象。而ResultSetExtractor是處理一個數(shù)據(jù)集合,返回一個對象集合。
當然,上面所述僅僅是Spring JdbcTemplte實現(xiàn)的基本原理,Spring JdbcTemplate內(nèi)部還做了更多的事情,比如,把所有的基本操作都封裝到JdbcOperations接口內(nèi),以及采用JdbcAccessor來管理DataSource和轉(zhuǎn)換異常等。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助。
相關文章
java mybatis框架實現(xiàn)多表關系查詢功能
這篇文章主要介紹了java mybatis框架實現(xiàn)多表關系查詢,基于Maven框架的整體設計 —— 一多一的關系,文中通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-10-10
IDEA 2020.1 for Mac 下載安裝配置及出現(xiàn)的問題小結
這篇文章主要介紹了IDEA 2020.1 for Mac 下載安裝配置及出現(xiàn)的問題小結,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03
Mybatis使用collection標簽進行樹形結構數(shù)據(jù)查詢時攜帶外部參數(shù)查詢
這篇文章主要介紹了Mybatis使用collection標簽進行樹形結構數(shù)據(jù)查詢時攜帶外部參數(shù)查詢,需要的朋友可以參考下2023-10-10

