SpringRest (MVC + Security) Конфигурация Java: не удается инициализировать контекст, поскольку уже присутствует корневой контекст приложения

Я новичок в Spring, и у меня уже есть очень странная проблема с базовой конфигурацией.

Вызвано: java.lang.IllegalStateException: Невозможно инициализировать контекст, поскольку уже присутствует корневой контекст приложения — проверьте, есть ли в вашем файле web.xml несколько определений ContextLoader*! в org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:297) в org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107) в io.undertow.servlet.core.ApplicationListeners.contextInitialized (ApplicationListeners.java:173) на io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:194) ... еще 7

Мне нужны два разных сервлета REST: один для внутренних вызовов (просмотр на сервер) и один для внешнего API. Я также хочу, чтобы некоторые службы использовались обоими сервлетами, поэтому я использовал SpringRootConfiguration для сканирования пакетов, содержащих два перехватчика и одну службу.

Вся конфигурация выполняется на Java, только с контекстом и прослушивателем в файле web.xml.

web.xml

<context-param>
    <param-name>contextClass</param-name>
    <param-value>
        org.springframework.web.context.support.AnnotationConfigWebApplicationContext
    </param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Инициализатор SpringRestExternalApp

@Override
protected Class<?>[] getRootConfigClasses() {
    return new Class[]{SpringRootConfiguration.class};
}

@Override
protected Class<?>[] getServletConfigClasses() {
    return new Class[]{SpringRestExternalConfiguration.class};
}

@Override
protected String[] getServletMappings() {
    return new String[]{"/api/*"};
}

@Override
protected String getServletName() {
    return "restexternal";
}

Конфигурация SpringRoot

@ComponentScan({"be.xperthis.common"})
@Configuration
public class SpringRootConfiguration {

}

Внешняя конфигурация SpringRest

@ComponentScan("com.polymedis.result.web.api")
@Configuration
public class SpringRestExternalConfiguration extends WebMvcConfigurationSupport {

@Autowired
private NoCacheHandler noCacheHandler;

@Autowired
private RestMetricHandler restMetrics;

@Override
public void addInterceptors(final InterceptorRegistry registry) {
    registry.addInterceptor(noCacheHandler);
    registry.addInterceptor(restMetrics);
}

@Override
public void configureDefaultServletHandling(final DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
}

@Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
    ....
}

@Override
public RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
    return new ApiVersionRequestMappingHandlerMapping(); //used to check a custom annotation on every RestController methods
}
}

По идее, должна быть вторая конфигурация Rest (внутренняя), но я ее пока удалил, пытаясь понять исключение...

Примечание. Следуя руководству по безопасности Spring, у меня была такая же ошибка при помещении SpringSecurityConfiguration в getRootConfigClasses() { ... }

Должен ли я удалить/добавить что-то из web.xml? Я использую Wildfly 8, и мне уже пришлось искать обходной путь для ошибки в Wildfly: конфигурации Spring не подбираются, если библиотеки Spring (jars) не добавлены в папку WEB-Inf/lib...


person ALansmanne    schedule 11.08.2016    source источник
comment
Проблема в том, что у вас есть и web.xml, и SpringRestExternalAppInitializer (который, вероятно, расширяет AbstractAnnotationConfigDispatcherServletInitializer). На самом деле это почти то же самое: web.xml — это вариант xml, а расширение AbstractAnnotationConfigDispatcherServletInitializer — это способ сделать то же самое в Java. Поэтому вам нужно выбрать либо оставить web.xml, либо класс Java. Проверьте ответ на этот вопрос - это может помочь stackoverflow.com /вопросы/26676782/   -  person lenach87    schedule 11.08.2016
comment
Удаление части web.xml решило мою первую проблему. Но теперь я хочу добавить второй сервлет для внутренних вызовов REST. Если я создаю два AbstractAnnotationConfigDispatcherServletInitializer, я получаю ту же ошибку...   -  person ALansmanne    schedule 12.08.2016
comment
Ну, если просто добавить два класса, это не должно работать сразу. Проверьте этот ответ, он может помочь вам получить общее представление и узнать, где искать дальнейший заголовок stackoverflow.com/questions/12059307/   -  person lenach87    schedule 12.08.2016


Ответы (1)


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

public class SpringAppInitializer implements WebApplicationInitializer {

@Override
public void onStartup(final ServletContext servletContext) throws ServletException {
    final AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    rootContext.register(SpringRootConfiguration.class, SpringSecurityConfig.class);
    servletContext.addListener(new ContextLoaderListener(rootContext));

    // Internal REST
    buildInternalRestContext(servletContext, rootContext);

    // External REST
    buildExternalRestContext(servletContext, rootContext);

    ...
}

public void buildExternalRestContext(final ServletContext servletContext, final AnnotationConfigWebApplicationContext rootContext) {
    final AnnotationConfigWebApplicationContext externalRestContext = new AnnotationConfigWebApplicationContext();
    externalRestContext.setParent(rootContext);
    externalRestContext.register(SpringRestExternalConfiguration.class);

    final ServletRegistration.Dynamic externalRestServlet = servletContext.addServlet("externalrest", new DispatcherServlet(externalRestContext));
    externalRestServlet.addMapping("/api/*");
}

public void buildInternalRestContext(final ServletContext servletContext, final AnnotationConfigWebApplicationContext rootContext) {
    final AnnotationConfigWebApplicationContext internalRestContext = new AnnotationConfigWebApplicationContext();
    internalRestContext.setParent(rootContext);
    internalRestContext.register(SpringRestConfiguration.class);

    final ServletRegistration.Dynamic restDispatcherServlet = servletContext.addServlet("rest", new DispatcherServlet(internalRestContext));
    restDispatcherServlet.addMapping("/rest/*");
}

}

Сейчас продолжу настройку SpringSecurity, но это уже другая тема :)

person ALansmanne    schedule 12.08.2016