英文:
Validating proxied Java Beans doesn't compare against correct values
问题
我正在尝试创建一个CDI扩展,该扩展将验证与配置值绑定的Java对象。
public class ExampleConfig {
@Range(min = 1000, max = 9999)
private int value;
@Inject
public ExampleConfig(@ConfigProperty(name = "value") int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
当我使用@Singleton
注解制作上述类时,它可以正常工作。在启动时,CDI扩展会验证读取名为"value"的环境变量的类。
类: com.example.ExampleConfig
属性: value
数值: 22222
原因: 必须在1000和9999之间
但是,当我将@Singleton
替换为@ApplicationScoped
时,在自己注入和使用此类时,它按预期工作,但在CDI扩展中,javax.validation.Validator
似乎总是将"value"视为0。
类: com.example.ExampleConfig$Proxy$_$$_WeldClientProxy
属性: value
数值: 0
原因: 必须在1000和9999之间
我很难理解为什么会出现这种情况,有人能否提供如何正确读取值的指导?
我一直在尝试实现以下两个目标,但一直未能成功:
- 让扩展在启动时强制初始化相应的类。
- 让CDI扩展等待bean初始化完成。
以下是我调用#validate
的方式:
public void afterDeploymentValidation(@Observes AfterDeploymentValidation adv, BeanManager bm) {
Set<ConstraintViolation<?>> allViolations = new HashSet<>();
for (Class<?> type : types)
{
final Object typeImpl = BeanProvider.getContextualReference(bm, type, false);
Set<ConstraintViolation<?>> violations = (Set<ConstraintViolation<?>>)(Object)validator.validate(typeImpl);
allViolations.addAll(violations);
}
// 为了简洁起见,省略了一些内容。
}
英文:
I'm trying to make a CDI extension which will validate a Java object which is bound to configuration values.
public class ExampleConfig {
@Range(min = 1000, max = 9999)
private int value;
@Inject
public ExampleConfig(@ConfigProperty(name = "value") int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
When I make the above class with the @Singleton
annotation, it works correctly. On startup the CDI extension validates the class which reads an environment variable called "value".
Class: com.example.ExampleConfig
Property: value
Value: 22222
Reason: must be between 1000 and 9999
When I replace @Singleton
with @ApplicationScoped
instead, when injecting and using this class myself, it works as intended, but in the CDI extension, javax.validation.Validator
appears to always treat the value
as 0.
Class: com.example.ExampleConfig$Proxy$_$$_WeldClientProxy
Property: value
Value: 0
Reason: must be between 1000 and 9999
I'm struggling to see why this is the case, is anyone able to provide guidance on how to read the value correctly?
Two things I've been trying to achieve to no avail is:
- Have the extension enforce initialization on startup for respective classes.
- Make the CDI extension wait until the bean has initialized.
The following is how I'm calling #validate
:
public void afterDeploymentValidation(@Observes AfterDeploymentValidation adv, BeanManager bm) {
Set<ConstraintViolation<?>> allViolations = new HashSet<>();
for (Class<?> type : types)
{
final Object typeImpl = BeanProvider.getContextualReference(bm, type, false);
Set<ConstraintViolation<?>> violations = (Set<ConstraintViolation<?>>)(Object)validator.validate(typeImpl);
allViolations.addAll(violations);
}
// Omitted for brevity.
}
答案1
得分: 2
几点注意事项:
- 首先,如果你只是想让Bean Validation起作用,只需将Hibernate Validator CDI项目放在你的运行时类路径上。无需其他任何操作,就完成了。
- 如果你在做其他事情,你可能会遇到一个问题,普通范围内的Bean的上下文引用是一个客户端代理。换言之,它是一个代理、一个外壳、一个持有者,它的“内部”(它所代理的东西)直到在代理上调用某个方法(如
toString()
或业务方法)之前都不会被“填充”。我猜测在你的情况下,验证器正在直接在代理上寻找可验证的字段。 - “填充”上下文引用的一种方法是在执行其他操作之前立即调用
toString()
。因此,在对引用执行任何其他操作之前,只需调用typeImpl.toString()
来“填充”引用。 - 我认为没有任何保证代理会以某种方式神奇地使代理对象的字段对你可用。为此,你需要获取内部的代理对象。每个CDI实现的方法都有所不同,但在Weld中,你可以通过一些强制转换来在程序中获取它。
英文:
Several things:
- First of all, if all you're trying to do is get Bean Validation working, just put the Hibernate Validator CDI project on your runtime classpath. Nothing else needed; the end.
- If you're doing something else, you're probably running into the fact that a contextual reference to a bean in a normal scope is a client proxy. In less stuffy terms, that means it's a proxy, a shell, a holder—and its "innards" (its referent, the thing it is proxying) is not "inflated" until some method is called on the proxy, like
toString()
or a business method. I'm guessing that what's happening in your case is the validator is looking for validatable fields directly on the proxy. - One way to "inflate" a contextual reference is to just call
toString()
on it right away before doing something else. So just calltypeImpl.toString()
before you do anything else to "inflate" the reference. - I don't think there's any guarantee that the proxy will somehow magically make the proxied object's fields available to you. For that, you'll need to get the inner proxied object. Each CDI implementation does this a little differently, but in Weld you can get this programmatically with some casting.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论