A Simple Understanding of Java Callback Method

Posted by Kifebear on Wed, 12 Jun 2019 19:55:36 +0200

What is a callback method?

Personal understanding callback refers to the need for two-step operation in solving a problem. At this time, the first step can be seen as solving a problem. The second step is to improve or supplement the operation on the basis of the results of the first step. The second step here is generally the callback method. Why do we say this? In order to solve a problem, we may need to call some time-consuming and uncertain methods. If the main thread is waiting for the result of the call, it will easily cause the program to be unresponsive and jammed, affecting the user experience. At this time, the callback method can be used to solve the problem. The time-consuming and uncertain method is designed as a method with callback parameters. At the end of the execution of the time-consuming method, the follow-up operation is continued. These operations encapsulated here are callback methods.

How to use callback method

Here we take spring encapsulated JDBC template as an example to get different types of data after querying the database. As we all know, querying data from the database must be a result set, but it can not meet the actual needs in the development process. What we usually want is an entity object, or a collection, etc. Then there are two choices at this time. One is to query the data first to get the result, and then encapsulate the result through the second method. The second method is to query the data, at the same time, the caller tells spring that after the query is finished, the result is obtained, and then the encapsulation method is executed. Then give me the encapsulation results. Both methods are practicable, but the second is commonly used. Why is that? Generally speaking, callbacks are used to avoid long time-consuming methods. But the more important reason is the encapsulation of object-oriented design, decoupling between modules and cohesion within modules. Using callbacks can reduce the coupling between modules. In addition, in the previous example, because the conventional relational database is not object-oriented, the results we get are not able to meet the requirements of oop, so encapsulating directly the corresponding entity or entity set is more in line with the object-oriented idea. In the ordinary use process, it is more convenient to query and return encapsulated objects directly through sql, and it is more in line with the idea of object-oriented.
Understand the use of callbacks through the following code:
Assume that the database has the following data:


image.png

The Person entity class is as follows

package com.af.study.spring.bean;

/**
 * person entity
 * Created by zyb on 2017/06/05.
 */
public class Person {

    String id;
    String name;
    Integer sex;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getSex() {
        return sex;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }
}

PersonDao uses callbacks when encapsulating data.

package com.af.study.spring.jdbc;

import com.af.study.spring.bean.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * personDao
 * Created by zyb on 2017/07/17.
 */
@Component
public class PersonDao extends NamedParameterJdbcDaoSupport {

    @Autowired
    public PersonDao(JdbcTemplate jdbcTemplate) {
        setJdbcTemplate(jdbcTemplate);
    }

    List<Person> getBeans(){
        return this.getJdbcTemplate().query("select * from t_person", BeanPropertyRowMapper.newInstance(Person.class));
    }
}

Test class

package com.af.study.spring.jdbc;

import com.af.study.spring.bean.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

/**
 * Created by zyb on 2017/07/17.
 */
public class PersonDaoTest {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        PersonDao personDao = ctx.getBean("personDao", PersonDao.class);
        List<Person> personList = personDao.getBeans();
        for (Person person : personList) {
            System.out.println(person.getId() + " --- " + person.getName() + " --- " + person.getSex());
        }
    }
}

The results after operation are as follows:

1 --- lx --- 0
2 --- yh --- 1

As you can see from the simple example above, the collection of Person objects is directly obtained after querying the database, rather than a regular resultSet. spring automatically implements a drop-back method by passing in a row mapper parameter, and encapsulates the object after getting the result set.
By looking at the spring source code, you can see the following code:

public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
        Assert.notNull(sql, "SQL must not be null");
        Assert.notNull(rse, "ResultSetExtractor must not be null");
        if (logger.isDebugEnabled()) {
            logger.debug("Executing SQL query [" + sql + "]");
        }
        class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
            @Override
            public T doInStatement(Statement stmt) throws SQLException {
                ResultSet rs = null;
                try {
                    rs = stmt.executeQuery(sql);
                    ResultSet rsToUse = rs;
                    if (nativeJdbcExtractor != null) {
                        rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
                    }
                    // The callback method is invoked here, and the previous code gets the result set, which is encapsulated and returned by the following method
                    return rse.extractData(rsToUse);
                }
                finally {
                    JdbcUtils.closeResultSet(rs);
                }
            }
            @Override
            public String getSql() {
                return sql;
            }
        }
        return execute(new QueryStatementCallback());
    }

In this paper, internal classes are used to encapsulate operations. Time-consuming operations (i.e. database queries) and subsequent operations (i.e. encapsulated objects) are combined into a new method. Then internal classes are passed as parameters to the execute method. As can be seen in the execute method, the combined method is directly invoked and the results of the two steps are merged and returned.

public <T> T execute(StatementCallback<T> action) throws DataAccessException {
        Assert.notNull(action, "Callback object must not be null");

        Connection con = DataSourceUtils.getConnection(getDataSource());
        Statement stmt = null;
        try {
            Connection conToUse = con;
            if (this.nativeJdbcExtractor != null &&
                    this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
                conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
            }
            stmt = conToUse.createStatement();
            applyStatementSettings(stmt);
            Statement stmtToUse = stmt;
            if (this.nativeJdbcExtractor != null) {
                stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
            }
            T result = action.doInStatement(stmtToUse);
            handleWarnings(stmt);
            return result;
        }
        catch (SQLException ex) {
            // Release Connection early, to avoid potential connection pool deadlock
            // in the case when the exception translator hasn't been initialized yet.
            JdbcUtils.closeStatement(stmt);
            stmt = null;
            DataSourceUtils.releaseConnection(con, getDataSource());
            con = null;
            throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
        }
        finally {
            JdbcUtils.closeStatement(stmt);
            DataSourceUtils.releaseConnection(con, getDataSource());
        }
    }

Continuing with the extractData method, you can see that you actually traversed the result set, encapsulating each row of records.

    public List<T> extractData(ResultSet rs) throws SQLException {
        List<T> results = (this.rowsExpected > 0 ? new ArrayList<T>(this.rowsExpected) : new ArrayList<T>());
        int rowNum = 0;
        while (rs.next()) {
            results.add(this.rowMapper.mapRow(rs, rowNum++));
        }
        return results;
    }

At this point, the demo of the callback method is basically over. Here's how to design and use callbacks.

How to design your own callback method

As mentioned at the beginning of the article, callback method is actually a time-consuming method that encapsulates the behavior after the time-consuming operation and passes it as a parameter to the time-consuming method. Usually we use the method of interface cooperating with internal class to implement. The reason why we use internal class + interface is that we use the idea of agent to implement it. We use interface to define the method that needs agent. In order to implement the interface through internal class, there will be no extra class, and it will not affect other places. In short, understanding callbacks is to combine operations into new methods to pass as parameters, I believe that using callbacks in code is not difficult.

Write at the end

It is not necessary to use callbacks. Callbacks do not affect functionality, but provide a solution to improve efficiency and reduce bug s caused by waiting time and other factors. All callbacks can separate the two steps. In order to understand the use of callbacks in depth, we should not use callbacks forcibly, but create efficiency problems. It is also applicable to the study of design patterns.
This article is only a personal understanding, in the expression of unclear or inadequate understanding, you are welcome to rectify the gods, but also ask you to tap, just started to write a blog, there may be some areas are not clear enough, I hope you can be more inclusive.

Topics: Spring SQL JDBC Database