2015-02-03 2 views
2

Я запускаю приложение Spring в среде Servlet 3.0+, чтобы программно настроить контекст сервлета, используя всю конфигурацию Java. Мой вопрос (с подробными сведениями ниже): как структурирован проект для поддержки сканирования компонентов как для контекста root, так и для веб-приложений без дублирования инициализации компонента?Контекст корня корня и сервлета с конфигурацией Java

Как я понимаю, существуют два контекста, в которых можно зарегистрировать весенние бобы. Во-первых, корневой контекст - это компоненты, не связанные с сервлетом. Например, пакетные задания, DAO и т. Д. Во-вторых, контекст сервлета - это компоненты, связанные с сервлетом, такие как контроллеры, фильтры и т. Д.

Я внедрил WebApplicationInitializer, чтобы зарегистрировать эти два контекста так же, как JavaDoc в WebApplicationInitializer указывает с AppConfig.class и DispatcherConfig.class.

Я хочу, чтобы оба автоматически находили свои соответствующие компоненты, поэтому я добавил @ComponentScan для обоих (что приводит к тому, что мои объекты Hibernate инициируются дважды). Spring находит эти компоненты, сканируя некоторые определенные базовые пакеты. Означает ли это, что мне нужно поместить все объекты, связанные с DAO, в отдельный пакет с контроллеров? Если это так, это будет довольно неудобно, поскольку я обычно предпочитаю упаковывать по функциональности (в отличие от типа).

Фрагменты кода ...

WebApplicationInitializer:

public class AppInitializer implements WebApplicationInitializer { 

    @Override 
    public void onStartup(ServletContext container) throws ServletException { 
     // Create the 'root' Spring application context 
     AnnotationConfigWebApplicationContext rootContext = 
       new AnnotationConfigWebApplicationContext(); 
     rootContext.register(AppConfig.class); 

     // Manage the lifecycle of the root application context 
     container.addListener(new ContextLoaderListener(rootContext)); 

     // Create the dispatcher servlet's Spring application context 
     AnnotationConfigWebApplicationContext dispatcherContext = 
       new AnnotationConfigWebApplicationContext(); 
     dispatcherContext.register(WebAppConfig.class); 

     // Register and map the dispatcher servlet 
     ServletRegistration.Dynamic dispatcher = 
       container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext)); 
     dispatcher.setLoadOnStartup(1); 
     dispatcher.addMapping("/"); 
    } 

} 

AppConfig:

@Configuration 
@ComponentScan 
public class AppConfig { 
} 

WebAppConfig:

@Configuration 
@EnableWebMvc 
@EnableSpringDataWebSupport 
@ComponentScan(basePackageClasses = AppConfig.class) 
public class WebAppConfig extends WebMvcConfigurerAdapter { 
} 

ответ

8

Просто определите, что вы хотите сканировать в каждой конфигурации. Обычно ваша корневая конфигурация должна сканировать все, кроме @Controller s, и ваша веб-конфигурация должна определять только @Controller.

Вы можете выполнить это, используя атрибуты includeFilters и excludeFilters аннотации @ComponentScan. При использовании включенных фильтров в этом случае вам также необходимо отключить использование фильтров по умолчанию, установив useDefaultFilters на false.

@Configuration 
@ComponentScan(excludeFilters={@Filter(org.springframework.stereotype.Controller.class)}) 
public class AppConfig {} 

И для WebConfig

@Configuration 
@EnableWebMvc 
@EnableSpringDataWebSupport 
@ComponentScan(basePackageClasses = AppConfig.class, useDefaultFilters=false, includeFilters={@Filter(org.springframework.stereotype.Controller.class)}) 
public class WebAppConfig extends WebMvcConfigurerAdapter {} 

Кроме того, вам нужно импортировать @Filter аннотацию:

import static org.springframework.context.annotation.ComponentScan.Filter; 
+0

Огромное спасибо за то, что это решает проблему упаковки по функциональности! – Corey

+1

AppConfig: @ComponentScan ( excludeFilters = {@ ComponentScan.Filter (Controller.class)} ) – Corey

+0

WebAppConfig: @ComponentScan ( basePackageClasses = AppConfig.class , useDefaultFilters = ложь , includeFilters = {@ ComponentScan.Filter (Controller.class)} ) – Corey

1

Короткий ответ: Да, вы должны определить отдельные проверки компонентов для каждого контекста, таким образом, моделируя свой проект по-разному и извлекая DAO в отдельное пространство имен (пакет).

Более длинный ответ разбит на две части, одну относительно контекстов сервлетов, а другую - на моделирование проекта.

Что касается контекстов корневого и сервлет: вы правильно определили различные функции корневого контекста и контекста сервлета. Это понимание, которое большинство разработчиков склонны пропустить, и это очень важно.
Чтобы уточнить эту тему, вы можете создать один корневой контекст и несколько (0+) контекстов сервлета. Все компоненты, определенные в корневом контексте, будут доступны для всех контекстов сервлета, но бобы, определенные в каждом контексте сервлета, не будут использоваться совместно с другими контекстами сервлетов (разные весенние контейнеры).

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

  1. Используйте один контейнер, а не два, а значит, вы можете определить только корневой контейнер или только контейнер сервлетов (я видел много приложений, как это).
  2. Отделите свои бобы в разных местах и ​​предоставьте каждому контейнеру уникальное сканирование компонентов. помните, что все бобы, определенные в корневом контейнере, доступны для использования в контейнере сервлетов (вам не нужно их определять дважды).

И наконец, несколько слов относительно моделирования проекта. Мне лично нравится писать свой проект в слоях, разделяя мой код на контроллеры (прикладной уровень), бизнес-логику (уровень bl) и DAO (уровень базы данных). Моделирование, как это, помогает во многих отношениях, включая проблему с корневым/сервлет-контекстом, с которой вы столкнулись. Есть тонны информации о многоуровневой архитектуре, здесь есть быстрый способ: wiki-Multilayered_architecture

+0

Упаковка слоем является плохой практикой имхо, потому что вы теряете инкапсуляция, вы пытаетесь чтобы скрыть все за слоем сервиса, но сделать все публичным, потому что вы разделили все по слоям. –

+0

Я не согласен, все зависит от того, как вы модулируете свой проект. например, вы можете предоставить api и несколько реализаций. Другим примером является то, что правильное моделирование может создавать несколько строительных блоков и позволять вам делиться различными частями вашего кода. В этом примере вы можете назвать свой сервисный уровень не только «одним набором контроллеров», но и несколькими протоколами (tcp, rest, бережливость ...), и все они используют один и тот же блок построения службы, – Amit

+0

. Вы должны объединить оба подходит. Ваш бизнес должен быть структурирован с помощью фиктивности и eveyrthing else (web, ws, rest, jms) - это небольшой уровень интеграции для вашей функциональности. Вы должны защищать свой домен, и с техническим уровнем все будет общедоступным, и ваша защита будет защищена. –