函数回调机制浅析以及Spring框架中对函数回调的经典应用

  • 回调函数的概念

       回调函数顾名思义就是:A调用B,然后B又可以回调A中的方法。可以举如下例子:晚上下班回家开灯准备用电饭煲煮晚饭,发现家里由于欠费导致停电了,这个时候通过支付宝缴纳电费,电费缴纳成功之后呢支付宝需要通知电力公司,我家的电费缴纳成功了,然后我查看家里的电表,发现电费缴纳成功,可以继续做饭了(只是举例说明回调函数的含义,具体支付宝缴纳电费不一定是这么实现)。

  • 样例代码

回调接口,电费缴纳成功之后需要通知客户端调用方,说电费缴纳成功了。

package com.test;

/**
 * 回调接口
 */
public interface ICallBackAction {
    void doOnLight(int number);
}

 具体缴纳费用的接口

/**
 * 缴纳费用的接口
 */
public interface IPayFees {

    void executor(ICallBackAction action);
}

 缴纳电费的具体实现类,客户端调用此方法缴纳电费,缴纳成功之后需要回调客户端传入的回调函数的类对象实例中的方法,简称回调函数。

package com.test;

/**
 * 缴纳电费的实现模板类
 */
public class PayElectricityFeesTemplate implements IPayFees{

    private int money ; 
    
    public PayElectricityFeesTemplate(int money) {
        this.money = money;
    }
    
    @Override
    public void executor(ICallBackAction action) {
        System.out.println("缴纳电费" + money + "元");
        System.out.println("0.5元一度电,共" + money * 2 + "度电, 充值成功");
        action.doOnLight(money * 2);
    }

    
}

 测试类

package com.test;

public class CallBackMain {

    public static void main(String[] args) {
        final CallBackMain main = new CallBackMain(); 
        System.out.println("开始用电饭锅煮午饭..........");
        System.out.println("发现家里断电了,发现是两个月没有充电费,欠费了..........");
        System.out.println("开始充电费");
        main.doBeginPayFee(100);
    }
    
    
    
    private void doBeginPayFee(int money) {
        
        IPayFees instance = new PayElectricityFeesTemplate(money);
        instance.executor(new ICallBackAction(){
           //具体的回调函数,又被调用方在执行完具体逻辑之后调用此方法。还可以将被调用
           //方法中的相关参数传递给调用方
            @Override
            public void doOnLight(int number) {
                System.out.println("电费充值成功,来电了,共剩" + number + "度电");
            }
        });
    }
}
  • Spring中JdbcTemplate应用

    JdbcTemplate.java为Spring中关于数据库操作的模板类,主要用于数据库操作的相关功能,例如获取

    数据库的连接、超时处理等公共逻辑的模板类。

org.springframework.jdbc.core.JdbcTemplate.java


        /**
         * execute方法主要用于调用方需要执行数据库操作时的执行入口
         * 此方法会统一获取数据库的底层连接,然后通过回调接口回传给调用方执行具体的数据库
         * 操作。
         * 这样做的好处是将获取数据库的连接操作统一封装到模板类中,调用方无需显示获取数据库
         * 连接而是通过回调函数回传connection去操作数据库。这样代码即达到了复用,也达到有解耦
         * 的效果,调用方只需要关心自己的执行业务即可。
         */
	@Override
	public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {
		Assert.notNull(action, "Callback object must not be null");

		Connection con = DataSourceUtils.getConnection(getDataSource());
		try {
			Connection conToUse = con;
			if (this.nativeJdbcExtractor != null) {
				// Extract native JDBC Connection, castable to OracleConnection or the like.
				conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
			}
			else {
				// Create close-suppressing Connection proxy, also preparing returned Statements.
				conToUse = createConnectionProxy(con);
			}
			return action.doInConnection(conToUse);
		}
		catch (SQLException ex) {
			// Release Connection early, to avoid potential connection pool deadlock
			// in the case when the exception translator hasn't been initialized yet.
			DataSourceUtils.releaseConnection(con, getDataSource());
			con = null;
			throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
		}
		finally {
			DataSourceUtils.releaseConnection(con, getDataSource());
		}
	}
org.springframework.jdbc.core.simple.AbstractJdbcInsert.java

/**
	 * Delegate method to execute the insert, generating any number of keys.
	 */
	private KeyHolder executeInsertAndReturnKeyHolderInternal(final List<?> values) {
		.........
                .........
			else {
		  getJdbcTemplate().execute(new ConnectionCallback<Object>() {
	                            /**
                                     * 注意参数conn对象,此对象为被调用方通过回调函数
                                     * 传入的值,这样调用方只需要关心自己的数据库操作业务。
                                     */                                       @Override
					public Object doInConnection(Connection con) throws SQLException, DataAccessException {
						// Do the insert
						PreparedStatement ps = null;
						try {
							ps = con.prepareStatement(getInsertString());
							setParameterValues(ps, values, getInsertTypes());
							ps.executeUpdate();
						}
						finally {
							JdbcUtils.closeStatement(ps);
						}
						//Get the key
						Statement keyStmt = null;
						ResultSet rs = null;
						Map<String, Object> keys = new HashMap<String, Object>(1);
						try {
							keyStmt = con.createStatement();
							rs = keyStmt.executeQuery(keyQuery);
							if (rs.next()) {
								long key = rs.getLong(1);
								keys.put(getGeneratedKeyNames()[0], key);
								keyHolder.getKeyList().add(keys);
							}
						}
						finally {
							JdbcUtils.closeResultSet(rs);
							JdbcUtils.closeStatement(keyStmt);
						}
						return null;
					}
				});
			}
			return keyHolder;
		}
		return keyHolder;
	}

总结:

    回调函数不只是有同步,也可以做到异步操作,这里就不举例说明,大体模式是一样的。

猜你喜欢

转载自278672937.iteye.com/blog/2396324
今日推荐