Java @RestController bean在根上下文中注册,尽管在excludeFilters中被排除
因此,我使用了基于Java的配置来启动tomcat容器,并创建了spring上下文。以下是我的配置类的外观:Java @RestController bean在根上下文中注册,尽管在excludeFilters中被排除,java,spring,rest,Java,Spring,Rest,因此,我使用了基于Java的配置来启动tomcat容器,并创建了spring上下文。以下是我的配置类的外观: @Configuration public class WebAppInitializer implements WebApplicationInitializer { private static final Logger LOGGER = LoggerFactory.getLogger(WebAppInitializer.class); @Override publi
@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");
}
}
@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 {
}
如您所见,RootConfig.class
构成我的根应用程序上下文,WebMvcConfig
构成子应用程序上下文
这些类看起来像:
@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");
}
}
@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
s。我希望它们只在子上下文中初始化
因此,在我的根上下文中,我从组件扫描中排除了@RestController
注释类。所以我想那些控制器不应该在那里注册。然而,这并没有发生。我的所有rest控制器都将在根上下文中注册,然后在子上下文中注册(这将覆盖根上下文中的一个)
我不确定为什么会发生这种情况,我很幸运能解决它。我的应用程序中没有任何web.xml,因为一切都由java配置负责。这是一个问题
第二个问题是,即使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}
第一个来自根上下文(具有已解析属性),第二个来自子上下文(具有未解析属性)。为什么会这样
我正在使用Spring4.1.0.RELEASE。这有点棘手
@RestController
和@Controller
是元注释<代码>@RestController用@Controller
注释,@Controller
用@Component
注释
通过此元注释属性,组件扫描过程将查找您的propertylesController
类型。它为@RestController
忽略它,但为@Controller
(或@Component
)找到它,然后处理程序映射堆栈注册它,因为它找到@Controller
和@RequestMapping
解决方法是在excludeFilters
中列出@组件
和@控制器
,或将useDefaultFilters
设置为false
。显然,这可能不适用于您,因为您可能希望通过这些注释找到其他类型
“适当”的解决办法是将你的东西分开包装
关于财产决议。您配置的PropertyPlaceHolderConfigure
或PropertySourcesPlaceholderConfigurer
(尚未看到您的PropertiesConfig
)是BeanFactory后处理器。这种类型的处理器只处理其包含的BeanFactory
中的bean。换句话说,子上下文的BeanFactory
不会在父上下文(afaik)中使用BeanFactoryPostProcessor
。由于PropertiesConfig
是在RootConfig
中声明的,因此只有在那里定义的bean才能获得属性解析
您可以在控制器的init
方法中设置断点。您会注意到,@value
注释字段的值对于在DispatcherServlet
的上下文(子上下文)中初始化的bean是无法解析的。问题在于根配置的@ComponentScan
,尽管这不包括@RestController
,但它扫描整个a.b
包,包括其所有子包。这反过来也会检测WebMvcConfig
,后者会扫描a.b.controller
包,但不会排除任何内容。因此,您的控制器被实例化两次,基本上所有与web相关的东西(也是Spring@MVC)都被加载两次
@配置
也是一个@组件
,因此会自动检测到。要修复此问题,请在RootConfig
上为@Configuration
类添加一个排除项
@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 {}
那么,将@Component
添加到excludeFilters也将排除我的另一个@Component注释bean(如您所说)。但是useDefaultFilters
到底能做什么呢?我知道包装结构有问题。basePackages应该更细粒化,但我将继续更改它作为我的最后一个选项。目前正在寻找一些简单的工作:(我还用正则表达式尝试了excludeFilter,将a\\\.b\\.controller\\\..*
作为我的模式传递,但也没有成功。)work@RohitJainuseDefaultFilters
指示是否应搜索所有默认注释@组件
,@服务
,@存储库
,@控制器
。这是真的默认情况下为de>。将其设置为false
或多或少相当于在excludeFilters
中列出注释。那么有什么方法可以读取控制器中的属性吗?我甚至尝试了Environment\getProperty()
方法,但它返回null
@RohitJain在子上下文中声明一个propertyPlaceHolderConfiger
,并使用适当的属性源。您的WebMvcConfig
在哪个包中?WebMvcConfig
和RootConfig
都在包a.b.config
中
也在收集WebMvcConfig
,它反过来会扫描其余的类,因为额外的@ComponentScan
@Configuration
类也是@Component
s.@M.Deinum哦,亲爱的!!!这完全在我的脑海中闪过,甚至没有被注意到..忘了