可以使用属性启用/禁用 Spring Boot @RestController 吗?
Posted
技术标签:
【中文标题】可以使用属性启用/禁用 Spring Boot @RestController 吗?【英文标题】:Can a spring boot @RestController be enabled/disabled using properties? 【发布时间】:2015-07-09 14:16:26 【问题描述】:给定一个带有@RestController
的“标准”spring boot 应用程序,例如
@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);
如果/除非某个应用程序属性存在/不存在,是否存在阻止端点启动的注释或技术根本。
注意:测试方法内部的属性并爆炸不是解决方案,因为端点将存在。
我不关心粒度:即启用/禁用一个方法或整个类都很好。
因为配置文件不是属性,所以通过配置文件进行控制并不能解决我的问题。
【问题讨论】:
【参考方案1】:我使用@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);
加上这个注释,除非我有
my.controller.enabled=true
在我的application.properties
文件中,控制器根本不会启动。
你也可以使用更方便的:
@ConditionalOnProperty("my.property")
其行为与上述完全相同;如果属性存在且"true"
,则组件启动,否则不启动。
【讨论】:
您可能需要考虑@ConditionalOnProperty
,因为它比 SpEL 评估略快。试试@ConditionalOnProperty(prefix="my.controller", name="enabled")
谢谢,关于可以应用此注释的级别的另一个说明:***.com/questions/30065945/…
在 RestController 之后使用 ConditionalOnProperty 或 ConditionalOnExpression 对我不起作用。正在创建 Bean 在 AdminController RestController 的日志中仍然可以访问 URL:DozerInitializer - Dozer JMX MBean [org.dozer.jmx:type=DozerAdminController] 自动注册到 Platform MBean 服务器有什么帮助吗?
这个解决方案的问题是,如果你改变了属性,除非你使用spring cloud进行配置,否则你将不得不重启服务器。
@user666 最佳实践将配置作为(系统测试)部署包的一部分,因此如果您遵循最佳实践,则需要重新启动。无论如何,这种控制通常是“功能切换”,因此激活将是有计划的更改,而不是临时的。对于 ad hoc,您可能会通过应用程序外部的网络来控制它,例如通过负载平衡器。【参考方案2】:
添加到这个问题和另一个问题here.
这是我的回答:
我实际上会使用@RefreshScope Bean,然后当您想在运行时停止 Rest Controller 时,只需将所述控制器的属性更改为 false。
SO 的 link 引用了在运行时更改属性。
这是我的工作代码的 sn-ps:
@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
还有其他使用过滤器的替代方法:
@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)
My Github explaining how to disable at runtime
【讨论】:
如果路径包含变量怎么办?【参考方案3】:在某些情况下,@ConditionalOnXXX 不能工作,例如,依赖另一个 bean 实例来检查条件。 (XXXCondition 类不能调用 bean)。
在这种情况下,请在 Java 配置文件中注册控制器。
查看源码(Spring webmvc 5.1.6):
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));
应该在控制器 bean 的类型级别上添加 @RequestMapping 注解。见:
@RequestMapping // Make Spring treat the bean as request handler
public class MyControllerA implements IMyController
@RequestMapping(path = "/path1" )
public .. restMethod1(...)
........
@RequestMapping // Make Spring treat the bean as request handler
public class MyControllerB implements IMyController
@RequestMapping(path = "/path1" )
public .. restMethod1(...)
........
@Configuration
public class ControllerConfiguration
/**
*
* Programmatically register Controller based on certain condition.
*
*/
@Bean
public IMyController myController()
IMyController controller;
if (conditionA)
controller = new MyControllerA();
else
controller = new MyControllerB();
return controller;
【讨论】:
【参考方案4】:我认为这个问题来自您为不同的环境使用不同的 application.properties 文件这一事实。在这种情况下,您可以使用 spring 配置文件并将配置分隔到具有配置文件名称后缀的不同文件中,例如:
application.properties:
spring.profiles.active=@activatedProperties@
应用程序本地属性:
//some config
application-prod.properties:
//some config
然后在您的构建参数中,您可以通过添加选项来指定您要构建的环境:
-Dspring.profiles.active= //<-put here profile local or prod
然后在您的应用程序中,您可以通过添加启用/禁用任何 Spring bean
@Profile("put here profile name")
例如:
@RestController
@Profile("local")
@RequestMapping("/testApi")
public class RestForTesting
//do some stuff
现在我的 RestForTesting 将仅在我运行使用
创建的构建时创建-Dspring.profiles.active=local
【讨论】:
没有。这个问题与配置文件无关,配置文件只是管理属性的众多方法之一。相反,我只想将端点部署到非生产环境 - 我不能让端点以任何形式存在于生产环境中。 我之前尝试过,向控制器添加@Profile
注释没有任何作用。以上是关于可以使用属性启用/禁用 Spring Boot @RestController 吗?的主要内容,如果未能解决你的问题,请参考以下文章
Spring MVC Web 应用程序 - 从属性启用/禁用控制器