簡體   English   中英

@RestController bean在根上下文中注冊,盡管未包含在excludeFilters中

[英]@RestController bean registered in root context, despite being excluded in excludeFilters

因此,我已經使用基於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上的@ComponentScan abcontroller包含我的@RestController 我希望僅在子上下文中對它們進行初始化。

因此,在我的根上下文中,我從組件掃描中排除了@RestController注釋的類。 所以我猜那些控制器不應該在那兒注冊。 但是,這沒有發生。 我所有的其余控制器都先在根上下文中注冊,然后在子上下文中注冊(在根上下文中覆蓋子控制器)。

我不確定為什么會這樣,並且無法解決問題。 我的應用程序中沒有任何web.xml ,因為java config可以確保一切正常。 這是一個問題。

第二個問題是,即使RestController已注冊兩次,當在子上下文中加載@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設置為false 顯然,這可能對您不起作用,因為您可能希望通過這些注釋找到其他類型。

“適當”的解決方案是將您的東西分開包裝。

關於財產決議。 您配置的PropertyPlaceholderConfigurerPropertySourcesPlaceholderConfigurer (尚未看到PropertiesConfig )是BeanFactoryPostProcessor 這種類型的處理器僅處理其包含的BeanFactory的bean。 換句話說,一個孩子上下文的BeanFactory將不使用BeanFactoryPostProcessor在父上下文(據我所知)。 由於您的PropertiesConfig是在RootConfig聲明的,因此只有在那里定義的bean才能獲得屬性解析。

您可以在控制器的init方法中放置一個斷點。 您會注意到,對於在DispatcherServlet的上下文(子上下文)中初始化的bean,未解析@Value注釋字段的值。

問題是@ComponentScan您的RootConfig ,雖然排除@RestController它會掃描整個ab包,包括所有的子包。 依次檢測WebMvcConfig ,該WebMvcConfig依次掃描abcontroller程序包,但不排除任何內容。 因此,您的控制器實例化了兩次,基本上所有與Web相關的內容(也包括Spring @MVC)都被加載了兩次。

@Configuration也是@Component ,因此會自動檢測到。 要解決此問題,請在RootConfig上為@Configuration類添加排除RootConfig

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM