Java 可以使用属性启用/禁用 spring boot @RestController 吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29958231/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can a spring boot @RestController be enabled/disabled using properties?
提问by Bohemian
Given a "standard" spring boot application with a @RestController
, eg
给定一个带有 的“标准”spring boot 应用程序@RestController
,例如
@RestController
@RequestMapping(value = "foo", produces = "application/json;charset=UTF-8")
public class MyController {
@RequestMapping(value = "bar")
public ResponseEntity<String> bar(
return new ResponseEntity<>("Hello world", HttpStatus.OK);
}
}
Is there an annotation or technique that prevents the endpoint from starting at allif/unless a certain application property exists/doesn't exist.
是否有一个注释或技术,防止启动端点在所有如果/除非某个应用程序属性存在/不存在。
Note: Testing a property inside the method and exploding is not a solution, because the endpoint will exist.
注意:在方法内部测试属性并爆炸不是解决方案,因为端点将存在。
I don't care about the granularity: ie enabling/disabling just a method or the whole class are both fine.
我不关心粒度:即仅启用/禁用一个方法或整个类都可以。
Because a profile is not a property, control via profiles does not solve my problem.
由于配置文件不是属性,因此通过配置文件进行控制并不能解决我的问题。
采纳答案by Bohemian
I found a simple solution using @ConditionalOnExpression
:
我找到了一个简单的解决方案@ConditionalOnExpression
:
@RestController
@ConditionalOnExpression("${my.controller.enabled:false}")
@RequestMapping(value = "foo", produces = "application/json;charset=UTF-8")
public class MyController {
@RequestMapping(value = "bar")
public ResponseEntity<String> bar(
return new ResponseEntity<>("Hello world", HttpStatus.OK);
}
}
With this annotation added, unless I have
添加此注释后,除非我有
my.controller.enabled=true
in my application.properties
file, the controller won't start at all.
在我的application.properties
文件中,控制器根本不会启动。
You can also use the more convenient:
您还可以使用更方便的:
@ConditionalOnProperty("my.property")
Which behaves exactly as above; if the property is present and "true"
, the component starts, otherwise it doesn't.
其行为与上述完全相同;如果该属性存在并且"true"
,则组件启动,否则不启动。
回答by David Latief Budiman
Adding to this question and another question here.
This is my answer:
这是我的回答:
I would actually used the @RefreshScope Bean and then when you want to stop the Rest Controller at runtime, you only need to change the property of said controller to false.
我实际上会使用@RefreshScope Bean,然后当您想在运行时停止 Rest Controller 时,您只需要将所述控制器的属性更改为 false。
SO's linkreferencing to changing property at runtime.
SO 的链接引用在运行时更改属性。
Here are my snippets of working code:
这是我的工作代码片段:
@RefreshScope
@RestController
class MessageRestController(
@Value("${message.get.enabled}") val getEnabled: Boolean,
@Value("${message:Hello default}") val message: String
) {
@GetMapping("/message")
fun get(): String {
if (!getEnabled) {
throw NoHandlerFoundException("GET", "/message", null)
}
return message
}
}
And there are other alternatives of using Filter:
还有其他使用过滤器的替代方法:
@Component
class EndpointsAvailabilityFilter @Autowired constructor(
private val env: Environment
): OncePerRequestFilter() {
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain
) {
val requestURI = request.requestURI
val requestMethod = request.method
val property = "${requestURI.substring(1).replace("/", ".")}." +
"${requestMethod.toLowerCase()}.enabled"
val enabled = env.getProperty(property, "true")
if (!enabled.toBoolean()) {
throw NoHandlerFoundException(requestMethod, requestURI, ServletServerHttpRequest(request).headers)
}
filterChain.doFilter(request, response)
}
}
回答by Akka Jaworek
I assume this question comes from the fact that you are using different application.properties files for your different enviroments. In this case you can use spring profiles and separate configurations into different files with profile name suffix for example:
我认为这个问题来自这样一个事实,即您为不同的环境使用不同的 application.properties 文件。在这种情况下,您可以使用 spring 配置文件并将配置分离到具有配置文件名称后缀的不同文件中,例如:
application.properties:
应用程序属性:
spring.profiles.active=@activatedProperties@
application-local.properties:
应用程序local.properties:
//some config
application-prod.properties:
应用程序-prod.properties:
//some config
then in your build paramethers you can specify which enviroment are you building by adding option:
然后在您的构建参数中,您可以通过添加选项来指定要构建的环境:
-Dspring.profiles.active= //<-put here profile local or prod
then in your application you can enable/disable any spring bean by adding
然后在您的应用程序中,您可以通过添加启用/禁用任何 spring bean
@Profile("put here profile name")
for example:
例如:
@RestController
@Profile("local")
@RequestMapping("/testApi")
public class RestForTesting{
//do some stuff
}
now my RestForTesting will be created only if im running a build created with
现在我的 RestForTesting 只会在我运行一个用
-Dspring.profiles.active=local
-Dspring.profiles.active=local
回答by wangf
In some case, the @ConditionalOnXXX cannot work, for example, depends on another bean instance to check condition. (XXXCondition class cannot invoke a bean).
在某些情况下,@ConditionalOnXXX 不起作用,例如,依赖另一个 bean 实例来检查条件。(XXXCondition 类不能调用 bean)。
In such case, register controller in Java configuration file.
在这种情况下,请在 Java 配置文件中注册控制器。
See source code(Spring webmvc 5.1.6):
查看源代码(Spring webmvc 5.1.6):
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.isHandler(Class)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.isHandler(Class)
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
Should add @RequestMapping annotation on type level for the controller bean. See:
应该在控制器 bean 的类型级别添加 @RequestMapping 注释。看:
@RequestMapping // Make Spring treat the bean as request hanlder
public class MyControllerA {
@RequestMapping(path = { "/path1" })
public .. restMethod1(...) {
........
}
}
@RequestMapping // Make Spring treat the bean as request hanlder
public class MyControllerB {
@RequestMapping(path = { "/path1" })
public .. restMethod1(...) {
........
}
}
@Configuration
public class ControllerConfiguration {
/**
*
* Programmingly register Controller based on certain condition.
*
*/
@Bean
public IMyController myController() {
IMyController controller;
if (conditionA) {
cntroller = new MyControllerA();
} else {
controller = new MyControllerB();
}
return controller;
}
}