Разработка 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" />

Нам это не очень удобно, и мы хотим выкинуть контекст Spring из оболочки 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-компоненты и установить свойства.

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


Поэтому нам интересно, как другие разработчики 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) }
}

or

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

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

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


person Pete    schedule 05.07.2012    source источник
comment
Я действительно не уверен, что вы имеете в виду, когда хотите удалить контекст Spring из оболочки REST, и я также не уверен, как вы используете контексты. Контекст не обязательно должен сопоставляться с пакетом или API, но может быть любой группой bean-компонентов, воплощающих приложение. Если вы не хотите использовать сканирование пакетов, у вас нет другого выбора, кроме как определить bean-компоненты по отдельности, используя XML или @Configuration, и указать ссылки между bean-компонентами в этой конфигурации или с помощью @Autowired или эквивалентного.   -  person EngineerBetter_DJ    schedule 05.07.2012


Ответы (2)


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

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

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

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

person Tomer    schedule 05.07.2012
comment
Да, тестирование действительно беспокоит нас, хотя, используя геттер/сеттер, мы могли бы издеваться над классом, я думаю. Однако, используя аргументы конструктора, мы получим цепочку, которую я описал.. A a = new A( new B( new C(...))) - person Pete; 05.07.2012
comment
Ну, это в основном зависит от вашей реализации, если вы можете использовать ленивую загрузку (инициализацию классов только при необходимости), тогда вы можете инициализировать A без B, а затем инициализировать B (использовать установщик) только при необходимости и так далее. - person Tomer; 05.07.2012

Просто задайте для всех своих 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#beans-factory-lazy-init для более подробной информации.

person Stuart    schedule 05.07.2012