java - @RestController bean 在根上下文中注册,尽管被排除在 exceptFilters 中

标签 java spring rest

因此,我使用基于 Java 的配置来启动 tomcat 容器,并创建 spring 上下文。这就是我的配置类的样子:

@Configuration
public class WebAppInitializer implements WebApplicationInitializer {

  private static final Logger LOGGER = LoggerFactory.getLogger(WebAppInitializer.class);

  @Override
  public void onStartup(ServletContext servletContext) throws ServletException {
    LOGGER.debug("Starting Spring Container");
    WebApplicationContext rootContext = createRootContext(servletContext);
    configureSpringMvc(servletContext, rootContext);
  }

  private WebApplicationContext createRootContext(ServletContext servletContext) {
    LOGGER.debug("Creating Root Context");
    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    rootContext.register(RootConfig.class);

    servletContext.addListener(new ContextLoaderListener(rootContext));
    LOGGER.debug("Created Root Context");
    return rootContext;
  }

  private void configureSpringMvc(ServletContext servletContext, WebApplicationContext rootContext) {
    LOGGER.debug("Creating Child Context");
    AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
    mvcContext.register(WebMvcConfig.class);
    mvcContext.setParent(rootContext);

    ServletRegistration.Dynamic appServlet = servletContext.addServlet("dispatcher", new DispatcherServlet(mvcContext));
    FilterRegistration.Dynamic authFilter = servletContext.addFilter("authFilter", AuthenticationFilter.class);
    authFilter.addMappingForServletNames(EnumSet.of(DispatcherType.REQUEST), true, "dispatcher");
    appServlet.setLoadOnStartup(1);
    appServlet.addMapping("/");
    LOGGER.debug("Created Child Context");
  }
}

如您所见,RootConfig.class 构成了我的根应用程序上下文,WebMvcConfig 构成了子应用程序上下文。

这些类看起来像:

@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan(basePackages = { "a.b.controller" })
public class WebMvcConfig extends WebMvcConfigurerAdapter {
}

@Configuration
@Import(value = { PropertiesConfig.class, AppConfig.class })
@ComponentScan(basePackages = "a.b", excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = RestController.class))
public class RootConfig {
}

注意这两个类上的@ComponentScan。包a.b.controller包含我的@RestController。我希望它们仅在子上下文中初始化。

因此,在我的根上下文中,我已从组件扫描中排除了 @RestController 带注释的类。所以我想这些 Controller 不应该在那里注册。然而,这并没有发生。我所有的其余 Controller 都在根上下文中注册,然后在子上下文中(它覆盖根上下文中的 Controller )。

我不确定为什么会发生这种情况,并且不幸无法解决它。我的应用程序中没有任何 web.xml,因为一切都由 java 配置处理。这是一个问题。

第二个问题是,即使 RestController 注册了两次,当在子上下文中加载时,我也无法解析这些 Controller 中的 @Value 带注释的属性。但是,当从根上下文加载时,属性将被解析。

这是一个例子:

@RestController
public class PropertyLessController {

  @Value("${prop1}")
  private String prop1;

  @PostConstruct
  public void init() {
    LOGGER.debug("Property loaded: {}", prop1);
  }
}

我正在 init() 方法中记录该属性。该日志出现两次:

Property loaded: someActualValue
Property loaded: @{prop1}

第一个来自根上下文(具有已解析的属性),第二个来自子上下文(具有未解析的属性)。现在为什么会发生这种情况?

我正在使用Spring 4.1.0.RELEASE

最佳答案

这有点棘手。

@RestController@Controller 是元注释。 @RestController@Controller 注解,@Controller@Component 注解。

通过这个元注释属性,组件扫描过程会找到您的PropertyLessController类型。它会忽略 @RestController,但它会为 @Controller(或 @Component)找到它,然后处理程序映射堆栈会注册它,因为它找到 @Controller@RequestMapping

解决方案是在 excludeFilters 中列出 @Component@Controller 或将 useDefaultFilters 设置为。显然,这可能对您不起作用,因为您可能希望通过这些注释找到其他类型。

“正确”的解决方案是将您的东西分开包装。

关于属性解析。您已配置的 PropertyPlaceholderConfigurerPropertySourcesPlaceholderConfigurer(尚未看到您的 PropertiesConfig)是一个 BeanFactoryPostProcessor。这种类型的处理器仅处理其包含的 BeanFactory 中的 Bean。换句话说,子上下文的 BeanFactory 不会在父上下文中使用 BeanFactoryPostProcessor(据我所知)。由于您的 PropertiesConfig 是在 RootConfig 中声明的,因此只有其中定义的 beans 才会获得属性解析。

您可以在 Controller 的 init 方法中放置一个断点。您会注意到,对于在 DispatcherServlet 的上下文(子上下文)中初始化的 bean,@Value 注释字段的值未解析。

关于java - @RestController bean 在根上下文中注册,尽管被排除在 exceptFilters 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28220239/

相关文章:

java - 在多个独立的 post 请求期间使 Servlet 中的对象保持 Activity 状态

java - 如何在没有ListActivity的情况下创建ListView

未正确传递 java 编译器注释处理器选项

spring - 使用 RestTemplate 时如何配置内部 Jackson 映射器?

ruby-on-rails - Rails 中的 RESTful DCI 上下文

spring - 如何在 Spring Boot 中使用异步?

java - 获取对象的绝对路径

java - 是否可以覆盖通过导入的 BOM 默认管理的依赖项的范围而不覆盖版本?

java - 访问此资源需要完全身份验证 - 休息 web 服务调用

rest - 如何在jmeter中以unix格式获取当前UTC时间