是否可以在Java配置中 * 单独 * 定义Spring RestController(@RestController
注解类)(在标记为@Bean
的方法中注解了@Configuration
的类)?
我有一个由spring Boot 管理的应用程序(为了回答这个问题,版本并不重要,即使是最后一个可用的版本)。这个应用程序使用REST公开了一些端点,所以有几个rest控制器,它们依次调用服务(像往常一样)。
现在,根据配置(application.yml
中的属性),我希望避免启动一些服务,比如说,使用@RestController
注解进行注解的2个类,因为它们处理我希望排除的“特性X”。
我想通过 Java configuration 来配置我所有的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
标注的类。
@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是一个控制器,它又是一个组件,因此它要接受组件扫描。因此,如果功能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 {...}
3条答案
按热度按时间rdlzhqv91#
我发现了一个相对优雅的解决方案,可能对社区有帮助:我没有像问题中建议的那样使用专门的 meta注解,而是使用常规的
@RestController
注解来注解Feature X的控制器:Sping Boot 应用程序类可以被“指示”在组件扫描排除过滤器期间不加载RestControllers。为了示例起见,我将在答案中使用内置的注解过滤器,但通常可以为更复杂(真实的)的情况创建自定义过滤器:
现在,由于我希望仅在启用Feature X时才加载rest控制器,因此我在FeatureXConfiguration中创建了相应的方法:
虽然组件扫描过程不加载其余控制器,但显式bean定义“覆盖”了此行为,并且在启动过程中创建了其余控制器的bean定义。然后Spring MVC引擎对其进行分析,由于存在
@RestController
注解,它像往常一样公开了相应的端点。c0vxltue2#
您可以在java config. E. e中使用本地类。
然后,您可以省略“真实的”实现上的
@RestController
注解,但照常使用其他注解,如@RequestMapping
。由于
FeatureXRestController1
没有@RestController
注解,因此它不再是@Component
,因此将不会通过组件扫描获得。MyAppFeatureXConfiguration
会传回@RestController
的Bean。此FeatureXRestController1Bean
会扩充FeatureXRestController1
,因此具有超类别的所有方法和请求对映。因为
FeatureXRestController1Bean
是一个本地类,所以它不包含在组件扫描中。)a9wyjsp73#
我喜欢上面提出的两个解决方案。但是,我想出了另一个对我有效的解决方案,而且相当干净。
因此,我决定只使用@Configuration类创建bean,并完全放弃@RestController注解。我将Web控制器的@Configuration类放在一个单独的包中,以便在需要创建控制器bean时,可以将类描述符传递给@ComponentScan或@ContextConfiguration注解。所有控制器类的ResponseBody注解,在类名上面,以保留REST控制器属性。非常干净。
缺点是控制器类不能被@WebMvcTest注解识别,并且每次我对一个控制器进行MockMvc测试时,我都需要为我所有的控制器创建bean。