Миграция приложения для использования Guice - как внедрить транзакции в существующие объекты? - PullRequest
8 голосов
/ 04 июня 2009

Я новичок в Guice и работаю над приложением с большим количеством устаревшего кода. У него есть несколько классов, которые выглядят так:

public final class DataAccessClass {

    private Transaction txn;

    @Inject //This was just added
    public DataAccessClass(/* injectable parameters */, Transaction txn) {

        this.txn = txn;
    }

    //Maybe add @Inject here to set a new transaction when it changes?
    public void setTransaction(Transaction txn) {

        this.txn = txn;
    }

    public void writeData(/* parameters for data to be written */) {

        //Write data using the current instance of 'txn'
    }
}

Довольно ясно, как использовать Guice для привязки экземпляров, которые никогда не меняются, но как быть с экземплярами, которые меняются (т.е. транзакциями)? Могу ли я использовать Guice для добавления другого экземпляра транзакции при его изменении? Обратите внимание, что экземпляр транзакции не является одной из хорошо поддерживаемых транзакций JPA / Hibernate / Spring

Наименее инвазивный подход, который я могу придумать (избегая необходимости переносить каждый класс, использующий транзакцию одновременно), будет использовать Guice для внедрения транзакции только при создании экземпляров объектов, и я сохраню существующий код приложения, который обновляет транзакции, когда это необходимо. Например, этот поставщик может использоваться для добавления новых объектов с текущим экземпляром транзакции:

public final class TransactionProvider implements Provider<Transaction> {

    /** Nullable transaction that should be used for all operations at the moment */
    private Transaction txn;

    public TransactionProvider(Transaction txn) {

        this.txn = txn;
    }

    /**
     * @param txn Nullable transaction to use for all operations at the moment
     */
    public void setTransaction(Transaction txn) {

        this.txn = txn;
    }

    /* Provider methods */

    public Transaction get() {

        return this.txn;
    }
}

Логика приложения будет выглядеть примерно так:

public final class Application {

    private final Provider<Transaction> transactionProvider;
    private final DataAccessClass dao; //Instance provided by Guice

    public void scopedOperation() {

        //Use a fresh transaction for this operation
        final Transaction txn = ...;

        //Make transaction available to Guice (for new objects) and to legacy (already-instantiated) DAO classes
        this.transactionProvider.setTransaction(txn);
        this.dao.setTransaction(txn); //Legacy approach - can this be updated?

        //Do some kind of data operation under the scope of the current transaction
        try {
            this.dao.writeData(...);
        } catch (Exception e) {
            txn.abort();
            throw new RuntimeException("Operation failed", e);
        }

        //The operation is over now
        this.txn.commit();
    }

Существуют ли другие способы обновления экземпляра Transaction, которые используют существующие классы, без необходимости переносить каждый класс сразу?

Ответы [ 2 ]

6 голосов
/ 06 июня 2009

Если я правильно понимаю, ваша проблема состоит из двух независимых частей:

  1. использование правильного экземпляра транзакции в классах Guice
  2. с использованием правильного экземпляра Transaction на устаревших классах.

Для '1' ваш настраиваемый поставщик работает, но я бы создал настраиваемую область для транзакции и связал бы класс Transaction в этой области. См. http://code.google.com/p/google-guice/wiki/CustomScopes для получения инструкций о пользовательских областях.

Относительно '2', когда у вас есть настраиваемая область, это обычная проблема использования Guice для предоставления экземпляров унаследованным классам. Вы не упомянули код, который в настоящее время предоставляет экземпляры Transaction унаследованным классам, но вы могли просто изменить этот конкретный фрагмент кода, чтобы запросить экземпляр у Guice. Поскольку вы находитесь в пределах своей «области транзакции», Guice позаботится о предоставлении нужного экземпляра.

1 голос
/ 05 июня 2009

Взгляните на Assisted Inject . Чтобы использовать его, определите трехстрочный интерфейс:

public interface DataAccessClassFactory {
  DataAccessClass create(Transaction transaction);
}

... и затем используйте привязку FactoryProvider для привязки к фабрике:

bind(DataAccessClassFactory.class).toProvider(
    FactoryProvider.newFactory(DataAccessClassFactory.class, DataAccessClass.class));

Затем вы можете ввести DataAccessClassFactory туда, где вам нужно построить DataAccessClass. AssistedInject включен в Guice 2, хотя для него требуется отдельный файл .jar.

...