Разработка Java API и включение его в приложение Spring

У нас есть большое приложение на основе Spring, которое включает в себя различные модули, а также пользовательские API. Хотя существуют модули, которым нужен контекст Spring, поскольку они просто обертывают части модели предметной области, мы думаем, что некоторые из наших API-интерфейсов этого не делают.

Например, мы написали обертку вокруг Джексона / Джерси для предоставления и использования услуг REST. Существует центральный интерфейс для доступа к этому API. Однако для реализации необходим другой класс, другой, и так далее. Таким образом, простая инициализация будет

A a = new A(new B(new C(), new D(new E())))

Который мы в настоящее время спасены от использования @Inject, Контекст этого API сканирует пакет и затем импортируется в целевое приложение.

<import resource="classpath:api-context.xml" />

Мы не чувствуем себя комфортно с этим и хотим выкинуть весенний контекст из оболочки REST, то есть хотим, чтобы API не требовал его, но не уверены, как это сделать. Это должно означать одно из следующих двух:

аргументы конструктора

В целевом контексте это потребует создания нескольких bean-компонентов, каждый из которых инициализируется своими зависимостями.

<bean id="a" class="...A">
    <constructor-arg>
        <ref = b " />
    </constructor-arg>
</bean>

<bean id="b" class="...B">
    <constructor-arg>
        <ref = c " />
    </constructor-arg>
</bean>
<!-- And so on -->

Добытчики и сеттеры

Или, предполагая, что A это конкретная реализация AInterface и AInterface - это центральный доступ, который мы могли бы просто сказать: A использует определенную реализацию BInterface по умолчанию и т. д. и фактически устанавливает их внутренне с помощью new:

public class A implements AInterface {
    private BInterface b = new B();
    public getB() {return b;}
    public setB(B b) {this.b = b) }
}
// and so on

Затем в моем целевом контексте я могу инициализировать централизованный доступ одной строкой, если я хочу использовать конфигурацию по умолчанию

<bean id="a" class="...A" />

или используйте свойства, чтобы установить его B. Затем, однако, если я хочу изменить что-то дальше по линии, мне придется инициализировать все bean-компоненты и установить свойства.

Также мне не кажется чистым, если я использую новый для сервиса вне моих тестов.


Поэтому нам интересно, как другие разработчики API делают свои интерфейсы и bean-компоненты доступными, не полагаясь на импорт контекста (который, между прочим, также загромождает целевой контекст многими потенциально ненужными bean-компонентами, например, если API предоставляет несколько сервисов, а я хочу только использовать один)?


редактировать

Не уверен, что это лучше:

public class A implements AInterface {
    private BInterface b
    public A() {
        b = new B();
    }
    public getB() {return b;}
    public setB(B b) {this.b = b) }
}

или же

public class A implements AInterface {
    private BInterface b
    public A(B b) {
        this.b = b;
    }
}

Последний чувствует себя лучше с тестовой точки зрения, но возвращает нас к цепочке, которую я описал выше, где мне придется инициализировать все зависимые bean-компоненты в моем контексте, прежде чем я смогу инициализировать A. Это похоже на слишком большие издержки конфигурации.

Можно утверждать, что это вполне нормально, что все зависимости должны быть инициализированы перед использованием класса, и что мы должны реорганизовать наш код. Затем, однако, мы получим множество классов утилит / помощников, которые также не являются лучшим дизайном, поскольку их трудно заменить или протестировать.

2 ответа

Решение

По сути, если вашему API не нужен контекст Spring, на самом деле нет причин его помещать туда.

Обратите внимание, что второй метод, который вы предложили:

public class A implements AInterface {
    private BInterface b = new B();
    public getB() {return b;}
    public setB(B b) {this.b = b) }
}

Это немного проблематично, потому что вы инициализируете интерфейс внутри вашего класса, что вызовет проблемы с тестированием, поскольку вы не можете высмеивать эти объекты. Лучшее решение - инициализировать его в конструкторе класса, используя его.

Просто определите все ваши bean-компоненты для загрузки по умолчанию, и тогда вы не будете создавать экземпляры сервисов, которые не используете.

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>

См. http://static.springsource.org/spring/docs/2.0.x/reference/beans.html для получения дополнительной информации.

Другие вопросы по тегам