Application du modèle de modèle au printemps

avant-propos

Le mode modèle est largement utilisé dans le printemps. Ici, nous combinerons le code source de JdbcTemplate pour apprendre avec vous et acquérir une compréhension plus approfondie du mode modèle, afin que nous puissions utiliser le mode modèle de manière flexible dans le développement quotidien pour réduire le code répétitif et améliorer l'évolutivité du code.

Qu'est-ce que le mode modèle

Définition de la méthode de modèle classique :

La classe parent définit le squelette (quelles méthodes sont appelées et dans quel ordre), et certaines méthodes spécifiques sont implémentées par la sous-classe. Le plus grand avantage : réutilisation du code, réduction de la duplication de code. À l'exception des méthodes spécifiques à implémenter par la sous-classe, d'autres méthodes et l'ordre des appels de méthode sont pré-écrits dans la classe parent. Il existe donc deux types de méthodes dans la méthode du modèle de classe parent :

Méthode commune :  code utilisé par toutes les sous-classes

Différentes méthodes :  Les méthodes à redéfinir par les sous-classes sont divisées en deux types :

  • Méthode abstraite : la classe parent est une méthode abstraite et la sous-classe doit la remplacer
  • Méthode Hook : il s'agit d'une méthode vide dans la classe parent, et la sous-classe hérite de la valeur par défaut et est également vide

Remarque : Pourquoi est-ce appelé un crochet ? Les sous-classes peuvent contrôler la classe parent via ce crochet (méthode), car ce crochet est en fait une méthode (méthode vide) de la classe parent !

public abstract class AbstractTemplate {
    
    public void templateMethod() {
        // 执行一些通用的操作
        
        step1();
        step2();
        step3();
        
        // 执行一些通用的操作
    }
    
    protected abstract void step1();
    
    protected abstract void step2();
    
    protected abstract void step3();
}

public class ConcreteTemplate extends AbstractTemplate {
    
    protected void step1() {
        // 实现具体的业务逻辑
    }
    
    protected void step2() {
        // 实现具体的业务逻辑
    }
    
    protected void step3() {
        // 实现具体的业务逻辑
    }
}

public class Client {
    public static void main(String[] args) {
        AbstractTemplate template = new ConcreteTemplate();
        template.templateMethod();
    }
}

Dans l'exemple ci-dessus, AbstractTemplate est une classe de modèle qui définit une méthode templateMethod() en tant que méthode de modèle. Cette méthode contient des opérations courantes et appelle trois méthodes abstraites (step1(), step2(), step3()) pour compléter une logique métier spécifique. ConcreteTemplate est une classe de modèles concrets qui implémente trois méthodes abstraites. Dans la classe Client, nous instancions l'objet ConcreteTemplate et appelons la méthode templateMethod(), complétant ainsi l'utilisation de l'ensemble du modèle de méthode de modèle. En utilisant le modèle de méthode de modèle, nous pouvons personnaliser une logique métier spécifique en implémentant différentes méthodes de rappel sans modifier la classe de modèle dans Spring, afin d'atteindre l'objectif de réutilisation et d'extension du code.

Comment Spring utilise-t-il le modèle de modèle ?

L'essence du modèle de méthode de modèle Spring : c'est la combinaison du modèle de méthode de modèle et du modèle de rappel, et c'est une autre implémentation de la méthode de modèle qui n'a pas besoin d'être héritée. Presque toutes les extensions complémentaires de Spring utilisent ce mode. Jetons un coup d'œil au diagramme de classes de JdbcTemplate

 Définir la méthode abstraite d'exécution dans l'interface JdbcOperations

La méthode d'exécution est implémentée dans JdbcTemplate

@Override
	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());
		}
	}

 Dans la méthode ci-dessus, certains codes courants pour établir un lien, créer un objet Statement, libérer l'objet Statement et fermer le lien sont tous à logique fixe. La logique modifiée réelle est dans T result = action.doInStatement(stmtToUse); faites attention à l'action ici, qui est un objet de rappel, nous pouvons regarder la définition de cet objet

public interface StatementCallback<T> {

	/**
	 * Gets called by {@code JdbcTemplate.execute} with an active JDBC
	 * Statement. Does not need to care about closing the Statement or the
	 * Connection, or about handling transactions: this will all be handled
	 * by Spring's JdbcTemplate.
	 * <p><b>NOTE:</b> Any ResultSets opened should be closed in finally blocks
	 * within the callback implementation. Spring will close the Statement
	 * object after the callback returned, but this does not necessarily imply
	 * that the ResultSet resources will be closed: the Statement objects might
	 * get pooled by the connection pool, with {@code close} calls only
	 * returning the object to the pool but not physically closing the resources.
	 * <p>If called without a thread-bound JDBC transaction (initiated by
	 * DataSourceTransactionManager), the code will simply get executed on the
	 * JDBC connection with its transactional semantics. If JdbcTemplate is
	 * configured to use a JTA-aware DataSource, the JDBC connection and thus
	 * the callback code will be transactional if a JTA transaction is active.
	 * <p>Allows for returning a result object created within the callback, i.e.
	 * a domain object or a collection of domain objects. Note that there's
	 * special support for single step actions: see JdbcTemplate.queryForObject etc.
	 * A thrown RuntimeException is treated as application exception, it gets
	 * propagated to the caller of the template.
	 * @param stmt active JDBC Statement
	 * @return a result object, or {@code null} if none
	 * @throws SQLException if thrown by a JDBC method, to be auto-converted
	 * to a DataAccessException by a SQLExceptionTranslator
	 * @throws DataAccessException in case of custom exceptions
	 * @see JdbcTemplate#queryForObject(String, Class)
	 * @see JdbcTemplate#queryForRowSet(String)
	 */
	T doInStatement(Statement stmt) throws SQLException, DataAccessException;

}

Vous pouvez voir où l'exécution (action StatementCallback<T>) est appelée ici, correspondant aux opérations de requête, de mise à jour et de mise à jour par lots

 Allons à UpdateStatementCallback pour voir

	@Override
	public int update(final String sql) throws DataAccessException {
		Assert.notNull(sql, "SQL must not be null");
		if (logger.isDebugEnabled()) {
			logger.debug("Executing SQL update [" + sql + "]");
		}

		class UpdateStatementCallback implements StatementCallback<Integer>, SqlProvider {
			@Override
			public Integer doInStatement(Statement stmt) throws SQLException {
				int rows = stmt.executeUpdate(sql);
				if (logger.isDebugEnabled()) {
					logger.debug("SQL update affected " + rows + " rows");
				}
				return rows;
			}
			@Override
			public String getSql() {
				return sql;
			}
		}

		return execute(new UpdateStatementCallback());
	}

On peut voir que UpdateStatementCallback implémente l'interface StatementCallback et place la logique d'opération de mise à jour spécifique dans la couche externe d'exécution, de sorte que la modification est transmise à la méthode d'exécution en tant qu'objet de rappel. Vous pouvez aller à QueryStatementCallback pour voir, la même chose est vraie

	@Override
	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);
					}
					return rse.extractData(rsToUse);
				}
				finally {
					JdbcUtils.closeResultSet(rs);
				}
			}
			@Override
			public String getSql() {
				return sql;
			}
		}

		return execute(new QueryStatementCallback());
	}

Résumer

Pourquoi JdbcTemplate n'utilise-t-il pas l'héritage ? Parce qu'il y a trop de méthodes dans cette classe, mais nous voulons toujours utiliser la connexion de base de données stable et publique existante de JdbcTemplate, alors que devons-nous faire ?

 Nous pouvons extraire les éléments modifiés et les transmettre à la méthode JdbcTemplate en tant que paramètre. Mais ce qui change, c'est un morceau de code, et ce code utilisera des variables dans JdbcTemplate. ce qu'il faut faire? Ensuite, nous utilisons l'objet de rappel. Dans cet objet de rappel, définissez une méthode de manipulation des variables dans JdbcTemplate. Lorsque nous implémenterons cette méthode, nous concentrerons les modifications ici. Ensuite, nous transmettons cet objet de rappel au JdbcTemplate pour terminer l'appel.

Je suppose que tu aimes

Origine blog.csdn.net/qq_28165595/article/details/131751192
conseillé
Classement