是否可以仅在Java配置中定义Spring RestController(带有@RestController
的类)(在带有@Configuration
的方法中带有@Bean
的类)?
我有一个由Spring Boot管理的应用程序(出于问题的考虑,版本无关紧要,即使最后一个可用)。该应用程序通过REST公开了一些终结点,因此有多个rest控制器,它们依次调用服务(与往常一样)。
现在取决于配置(application.yml
中的属性),我想避免启动某些服务,并且说2个带有@RestController
批注的类,因为它们处理了我要排除的“功能X”。
我想通过Java配置来配置我的所有bean,这是必需的。因此,我最初的方法是在单独的配置中定义所有Bean(控制器和服务),并在扫描过程中通过Spring Boot找到该配置),并在该配置上放置@ConditionalOnProperty
使其出现在一个位置:
@Configuration
public class MyAppGeneralConfiguration {
// here I define all the beans that are not relevant for "feature X"
@Bean
public ServiceA serviceA() {}
...
}
@Configuration
@ConditionalOnProperty(name = "myapp.featureX.enabled", havingValue = "true")
public class MyAppFeatureXConfiguration {
// here I will define all the beans relevant for feature X:
@Bean
public ServiceForFeatureX1 serviceForFeatureX1() {}
@Bean
public ServiceForFeatureX2 serviceForFeatureX2() {}
}
通过这种方法,我的服务根本没有任何spring注释,并且我不使用
@Autowired
注释,因为所有内容都是通过@Configuration
类中的构造函数注入的:// no @Service / @Component annotation
public class ServiceForFeatureX1 {}
现在,我的问题是关于使用
@RestContoller
注释进行注释的类。假设我有2个这样的控制器:@RestController
public class FeatureXRestController1 {
...
}
@RestController
public class FeatureXRestController2 {
...
}
理想情况下,我也想在Java配置中定义它们,以便在禁用该功能时,这两个控制器甚至都不会加载:
@ConditionalOnProperty(name = "myapp.featureX.enabled", havingValue = "true", matchIfMissing=true)
public class MyAppFeatureXConfiguration {
@Bean
@RestController // this doesn't work because the @RestController has target Type and can't be applied
// to methods
public FeatureXRestController1 featureXRestController1() {
}
所以问题基本上是可以做到的吗?
RestController是一个Controller,它又是一个组件,因此需要对其进行组件扫描。因此,如果功能X被禁用,则功能X的其余控制器仍将开始加载并发生故障
因为不会有“服务”-配置中排除了bean,所以spring boot无法插入。
我考虑过的一种方法是定义一个特殊的注释,例如
@FeatureXRestController
并将其设置为@RestController
并将@ConditionalOnProperty
放在此处,但它仍然有两个位置,是我能想到的最佳解决方案:@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RestController
@ConditionalOnProperty(name = "myapp.featureX.enabled", havingValue = "true", matchIfMissing=true)
public @interface FeatureXRestController {
}
...
@FeatureXRestController
public class FeatureXRestController1 {...}
@FeatureXRestController
public class FeatureXRestController2 {...}
最佳答案
我发现了一个相对优雅的解决方法,可能会对社区有所帮助:
我没有使用我在问题中建议的专用元注释,而是使用常规的@RestController
注释对Feature X的控制器进行注释:
@RestController
public class FeatureXController {
...
}
可以“指示” Spring Boot应用程序类在组件扫描排除过滤器期间不加载RestControllers。为了便于回答,我将使用内置的注释过滤器,但通常可以针对更复杂(实际)的情况创建自定义过滤器:
// Note the annotation - component scanning process won't recognize classes annotated with RestController, so from now on all the rest controllers in the application must be defined in `@Configuration` classes.
@ComponentScan(excludeFilters = @Filter(RestController.class))
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
现在,由于我希望仅在启用功能X时才加载rest控制器,因此我在FeatureXConfiguration中创建相应的方法:
@Configuration
@ConditionalOnProperty(value = "mayapp.featureX.enabled", havingValue = "true", matchIfMissing = false)
public class FeatureXConfiguration {
@Bean
public FeatureXService featureXService () {
return new FeatureXService();
}
@Bean
public FeatureXRestController featureXRestController () {
return new FeatureXRestController(featureXService());
}
}
尽管组件扫描过程不会加载其余的控制器,但是显式的bean定义“覆盖”了此行为,其余的控制器的bean定义是在启动期间创建的。然后,Spring MVC引擎对其进行分析,并且由于存在
@RestController
批注,因此它像通常那样公开了相应的端点。