春季条件注释的顺序评估 [英] Ordering evaluation of Conditional annotations in spring
问题描述
我有三个类,所有这些类都需要基于@ConditionalOnExpression注释启用/禁用.所有这些都在单独的.java文件中.
I have three classes all of which needs to be enabled/disabled based on the @ConditionalOnExpression annotaion. All of these are in separate .java files.
EX:
@ConditionalOnExpression("#T(com.xxx.xxx.xxx.xxx.CxProperties).isAEnabled()}")
Class A{
}
@ConditionalOnExpression("#T(com.xxx.xxx.xxx.xxx.CxProperties).isBEnabled()}")
Class B{
}
@ConditionalOnExpression("#T(com.xxx.xxx.xxx.xxx.CxProperties).isCEnabled()}")
Class C{
}
现在,我在不同的类中拥有一个初始化函数,该类首先被执行,并且我将启用/禁用值设置为类CxProperties. 注意:假设所有设置器都是静态的
Now i have an init funtion in a different class which gets executed first and i set the enable/disable value to the class CxProperties. Note: Assume all setters are static
class setvalues{
public void init(){
/*Read config values from a file*/
CxProperties.setAEnabled(true/false);
CxProperties.setBEnabled(true/false);
CxProperties.setCEnabled(true/false);
}
}
现在,这些条件的评估发生在程序的开始(甚至在执行init之前),而根本没有设置enable/disable.
Now the evaluation of these conditions are happening at the beginning of the program (even before the execution of init) when the enable/disable is not set at all.
春季是否有可能命令对这些条件进行评估,例如说要在执行某点之后对此进行评估?
Is there any possible way in spring to order the evaluation of these conditions like say evaluate this after a certain point of execuation?
任何指针都将受到高度赞赏.
Any pointers are highly appreciated.
推荐答案
我建议您不要为此使用@ConditionalOnExpression
注释.
I would suggest you not to use @ConditionalOnExpression
annotation for this.
考虑改用@PreAuthorize
.是的,它来自spring-security.
Consider using @PreAuthorize
instead. Yes, it's from spring-security.
如果没有启用,您可以保护每个服务免于使用,并动态切换其启用/禁用状态:
With that you can protect each service from usage if it isn't enabled and dynamically toggle enabled/disabled state for it:
@SpringBootApplication
public class So44462763Application {
public static void main(String[] args) {
SpringApplication.run(So44462763Application.class, args);
}
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // <-- this is required for PreAuthorize annotation to work
public static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
interface CxProperties {
boolean isServiceAEnabled();
boolean isServiceBEnabled();
boolean isServiceCEnabled();
boolean enableService(String service);
boolean disableService(String service);
}
@Component("cx")
public static class CxPropertiesImpl implements CxProperties {
private static final ConcurrentHashMap<String, Boolean> services = new ConcurrentHashMap<>(); //could be database/redis/property file/etc
@PostConstruct
private void init() {
//services.put("serviceA", true); //initial population from property file/network resource/whatever
}
public boolean isServiceAEnabled() {
return services.getOrDefault("serviceA", false);
}
public boolean isServiceBEnabled() {
return services.getOrDefault("serviceB", false);
}
public boolean isServiceCEnabled() {
return services.getOrDefault("serviceC", false);
}
//just a sample how you can dynamically control availability for each service
@Override
public boolean enableService(String service) {
services.put(service, true);
return services.getOrDefault(service, false);
}
@Override
public boolean disableService(String service) {
services.put(service, false);
return services.getOrDefault(service, false);
}
}
interface BusinessService {
String doSomething();
}
@Service("serviceA")
@PreAuthorize("@cx.serviceAEnabled")
public static class ServiceA implements BusinessService {
@Override
public String doSomething() {
return this.getClass().getSimpleName() + " doing some work";
}
}
@Service("serviceB")
@PreAuthorize("@cx.serviceBEnabled")
public static class ServiceB implements BusinessService {
@Override
public String doSomething() {
return this.getClass().getSimpleName() + " doing some work";
}
}
@Service("serviceC")
@PreAuthorize("@cx.serviceCEnabled")
public static class ServiceC implements BusinessService {
@Override
public String doSomething() {
return this.getClass().getSimpleName() + " doing some work";
}
}
@RestController
@RequestMapping("/api/work")
public static class WorkApi {
private static final Logger log = LoggerFactory.getLogger(WorkApi.class);
private final List<BusinessService> businessServices;
@Autowired
public WorkApi(final List<BusinessService> businessServices) {
this.businessServices = businessServices;
}
@GetMapping
public String doWork() {
final StringJoiner joiner = new StringJoiner(",");
for (BusinessService service : businessServices) {
try {
joiner.add(service.doSomething());
} catch (AccessDeniedException e) {
log.warn("Service {} is disabled.", service);
}
}
return joiner.toString();
}
}
@RestController
@RequestMapping("/api/control")
public static class ControlApi {
private final CxProperties cxProperties;
@Autowired
public ControlApi(final CxProperties cxProperties) {
this.cxProperties = cxProperties;
}
@PostMapping("{service}/enable")
public boolean enable(@PathVariable("service") String serviceName) {
return cxProperties.enableService(serviceName);
}
@PostMapping("{service}/disable")
public boolean disable(@PathVariable("service") String serviceName) {
return cxProperties.disableService(serviceName);
}
}
}
这是一个示例用法:
$ curl -u user:123 -XGET 'localhost:8080/api/work'
$ curl -u user:123 -XPOST 'localhost:8080/api/control/serviceC/enable'
true%
$ curl -u user:123 -XGET 'localhost:8080/api/work'
ServiceC doing some work%
$ curl -u user:123 -XPOST 'localhost:8080/api/control/serviceA/enable'
true%
$ curl -u user:123 -XGET 'localhost:8080/api/work'
ServiceA doing some work,ServiceC doing some work%
使用这种方法,即使不重新启动也可以控制服务的可访问性.
Using this approach you can control accessibility of services even without restart.
所有这些都可以在没有spring-security的情况下完成,但是需要更多的人工工作,并且可能会稍微降低代码的整体可读性.
And all of this can be done without spring-security as well but involves a little bit more of manual work and might slightly decrease overall readability of your code.
这篇关于春季条件注释的顺序评估的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!