Разработка 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 для получения дополнительной информации.