将 EL 解析器与 WELD 结合使用时,如何解决类型差异?
我有一个通用的 EL 生成器,我编写它是为了利用 WELD 的能力,在我需要它时“使其工作”,甚至将类型强制写入函数中,以确保返回类型与焊接注射点。
这是我的问题: WELD 从注入点的可分配类型解析,即,如果您的注入点是 String,它只会查找具有 String 返回类型的生成器。
这是有问题的,因为我想要一个生产者来处理类型强制,并交回一个正确类型的对象。
作为一个拼凑,我有一个 String 生成器方法,它是真正的生成器的别名,并且只进行类型拼凑。
这......至少有效,直到我遇到具有对象类型注入点的情况,此时我的所有拼凑方法和通用生产者都匹配,即使我使用@Typed,也会出现不明确的依赖异常关于生产者。
有没有一个明智的方法可以解决这个问题,或者我应该放弃让 WELD 为我做所有艰苦工作的想法?
下面是使用此生成器的示例,来自具有 Request 范围的错误处理 bean。 RequestURI 是这种情况下比较麻烦的一个,另外两个需要键入“kludge”方法才能工作。这个特定 bean(不包括代码)的主要功能是捕获未处理的异常,并通过电子邮件将其报告给我们,以便在将来的修订中进行更具体的错误处理。这里的基本用例是简化对 EL 的编程访问,并可能允许使用值绑定写回 EL,尽管在此特定代码中这是不可能的。
我知道我可以使用其他方法执行以下操作,但这不是重点。实际上,在 IMO 中以编程方式访问 EL 变得更容易是一件积极的事情,特别是在处理 JSF 2.0 引入的一些更奇特的作用域(尤其是 Flash 作用域)时。我的大多数用例都与 Flash 范围有关,但在这里公开并不安全,它们也不是可预测的类型,也不是应该为它们编写的拼凑的类型,因此为什么我想要这种更通用的方法。
@Inject
@ELResource("#{requestScope['javax.servlet.error.exception']}")
protected Exception exception;
@Inject
@ELResource("#{requestScope['javax.servlet.error.status_code']}")
protected String statusCode;
@Inject
@ELResource("#{requestScope['javax.servlet.error.request_uri']}")
protected Object requestUri;
这是我的限定符:
@Target(value = {ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Qualifier
public @interface ELResource {
@Nonbinding
String value();
}
生产者:
@Dependent
public class ELProducer {
@Inject
FacesContext facesContext;
@Inject
Logger log;
@Produces
@ELResource("")
public Object getELResource(InjectionPoint ip) {
log.entering(getClass().getName(), "getELResource()",new Object[] {ip});
ExpressionFactory expFactory = facesContext.getApplication().getExpressionFactory();
String elString = ip.getAnnotated().getAnnotation(ELResource.class).value();
Class coercionType = resolveClass(ip);
log.log(Level.INFO, "EL String: {0} of type: {1}", new Object[] {elString, coercionType.getName()});
if (elString == null || elString.length() <= 0) {
log.log(Level.SEVERE,"No EL String specified for injection");
log.exiting(getClass().getName(), "getELResource()");
return null;
}
ValueExpression ve = expFactory.createValueExpression(facesContext.getELContext(), elString, coercionType);
if (ve != null) {
Object retval = ve.getValue(facesContext.getELContext());
log.log(Level.INFO,"EL Result: {0} of type: {1}",new Object[] { retval, ((retval != null) ? retval.getClass().getName() : "NULL") } );
log.exiting(getClass().getName(), "getELResource()",new Object[] {retval} );
return retval;
} else {
log.log(Level.WARNING,"Null EL Result");
log.exiting(getClass().getName(), "getELResource()");
return null;
}
}
// TODO: There should be a better way of accomplishing the below
@Produces
@ELResource("")
public String getELStringResource(InjectionPoint ip) {
return (String)getELResource(ip);
}
@Produces
@ELResource("")
public Exception getELExceptionResource(InjectionPoint ip) {
return (Exception)getELResource(ip);
}
private Class resolveClass(InjectionPoint ip) {
Annotated annotated = ip.getAnnotated();
Member member = ip.getMember();
if (member instanceof Field) {
Field field = (Field)member;
return field.getType();
} else if (member instanceof Constructor) {
Constructor con = (Constructor)member;
AnnotatedParameter ap = (AnnotatedParameter)annotated;
return con.getParameterTypes()[ap.getPosition()];
} else if (member instanceof Method) {
Method method = (Method)member;
AnnotatedParameter ap = (AnnotatedParameter)annotated;
return method.getParameterTypes()[ap.getPosition()];
} else {
return null;
}
}
}
以及错误:
org.jboss.weld.exceptions.DeploymentException: WELD-001409 Ambiguous dependencies for type [Object] with qualifiers [@ELResource] at injection point [[field] @Inject @ELResource protected xxx.backing.ErrorHandler.requestUri]. Possible dependencies [[Producer Method [Exception] with qualifiers [@Any @ELResource] declared as [[method] @Produces @Typed @ELResource public xxx.ELProducer.getELExceptionResource(InjectionPoint)], Producer Method [String] with qualifiers [@Any @ELResource] declared as [[method] @Produces @Typed @ELResource public xxx.ELProducer.getELStringResource(InjectionPoint)], Producer Method [Object] with qualifiers [@Any @ELResource] declared as [[method] @Produces @Dependent @ELResource public xxx.ELProducer.getELResource(InjectionPoint)]]]
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:309)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:139)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:162)
...
I have a generic EL producer that I've written to take advantage of WELD's ability to just 'make it work' when I need it done, and even have the type coercion written into the function so that it makes sure the return type matches the weld injection point.
Here's my problem:
WELD resolves from the injection point's assignable types, ie, if your injection point is a String, it will only look for producers with a String return type.
This is problematic, as I want one producer that will take care of the type coercion, and hand back a correctly typed object.
As a kludge, I have a String producer method that aliases to the real producer, and only does the type kludging.
This... at least works, until I get to the situation of having an Object typed injection point, at which point ALL of my kludge methods AND the generic producer ALL are matched, giving an ambiguous dependency exception, even if I use @Typed on the producers.
Is there a sane way around this, or should I give up on this idea of making WELD do all the hard work for me?
Here's an example of the use of this producer, from an error handling bean with a Request scope. RequestURI is the one that is troublesome in this case, the other two require typed "kludge" methods to work. The main function of this particular bean (code not included) is to catch unhandled exceptions and report them via email to us for more specific error handling in future revisions. The basic use-case here is to simplify programmatic access to EL, and potentially to allow writing back to EL using the value binding, though that's not possible in this particular code.
I know I can do the below using other methods, that's not the point. Realistically, it's a positive thing to make it easier to access EL programmatically IMO, especially when dealing with some of the more exotic scopes (especially Flash scope) introduced by JSF 2.0. Most of my use cases have to do with Flash scope, but aren't safe to disclose here, nor are they predictable types, or types that should have kludges written for them, hence why I want this more generalized method.
@Inject
@ELResource("#{requestScope['javax.servlet.error.exception']}")
protected Exception exception;
@Inject
@ELResource("#{requestScope['javax.servlet.error.status_code']}")
protected String statusCode;
@Inject
@ELResource("#{requestScope['javax.servlet.error.request_uri']}")
protected Object requestUri;
Here's my qualifier:
@Target(value = {ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Qualifier
public @interface ELResource {
@Nonbinding
String value();
}
The producer:
@Dependent
public class ELProducer {
@Inject
FacesContext facesContext;
@Inject
Logger log;
@Produces
@ELResource("")
public Object getELResource(InjectionPoint ip) {
log.entering(getClass().getName(), "getELResource()",new Object[] {ip});
ExpressionFactory expFactory = facesContext.getApplication().getExpressionFactory();
String elString = ip.getAnnotated().getAnnotation(ELResource.class).value();
Class coercionType = resolveClass(ip);
log.log(Level.INFO, "EL String: {0} of type: {1}", new Object[] {elString, coercionType.getName()});
if (elString == null || elString.length() <= 0) {
log.log(Level.SEVERE,"No EL String specified for injection");
log.exiting(getClass().getName(), "getELResource()");
return null;
}
ValueExpression ve = expFactory.createValueExpression(facesContext.getELContext(), elString, coercionType);
if (ve != null) {
Object retval = ve.getValue(facesContext.getELContext());
log.log(Level.INFO,"EL Result: {0} of type: {1}",new Object[] { retval, ((retval != null) ? retval.getClass().getName() : "NULL") } );
log.exiting(getClass().getName(), "getELResource()",new Object[] {retval} );
return retval;
} else {
log.log(Level.WARNING,"Null EL Result");
log.exiting(getClass().getName(), "getELResource()");
return null;
}
}
// TODO: There should be a better way of accomplishing the below
@Produces
@ELResource("")
public String getELStringResource(InjectionPoint ip) {
return (String)getELResource(ip);
}
@Produces
@ELResource("")
public Exception getELExceptionResource(InjectionPoint ip) {
return (Exception)getELResource(ip);
}
private Class resolveClass(InjectionPoint ip) {
Annotated annotated = ip.getAnnotated();
Member member = ip.getMember();
if (member instanceof Field) {
Field field = (Field)member;
return field.getType();
} else if (member instanceof Constructor) {
Constructor con = (Constructor)member;
AnnotatedParameter ap = (AnnotatedParameter)annotated;
return con.getParameterTypes()[ap.getPosition()];
} else if (member instanceof Method) {
Method method = (Method)member;
AnnotatedParameter ap = (AnnotatedParameter)annotated;
return method.getParameterTypes()[ap.getPosition()];
} else {
return null;
}
}
}
And the error:
org.jboss.weld.exceptions.DeploymentException: WELD-001409 Ambiguous dependencies for type [Object] with qualifiers [@ELResource] at injection point [[field] @Inject @ELResource protected xxx.backing.ErrorHandler.requestUri]. Possible dependencies [[Producer Method [Exception] with qualifiers [@Any @ELResource] declared as [[method] @Produces @Typed @ELResource public xxx.ELProducer.getELExceptionResource(InjectionPoint)], Producer Method [String] with qualifiers [@Any @ELResource] declared as [[method] @Produces @Typed @ELResource public xxx.ELProducer.getELStringResource(InjectionPoint)], Producer Method [Object] with qualifiers [@Any @ELResource] declared as [[method] @Produces @Dependent @ELResource public xxx.ELProducer.getELResource(InjectionPoint)]]]
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:309)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:139)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:162)
...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我真的很努力,但我没有设法没有评论你正在失去类型安全(这是 CDI 的主要设计目标之一),并且 EL评估是性能杀手...;-)
无论如何,(几乎没有)这样说:
没有真正的 CDI 选项可以克服这个问题。我建议您为这些 EL 表达式使用特定类型,例如
ElObject
等,它由生产者构造,本身为客户端提供类型安全访问器。编辑:您可能想查看Seam Solder,以一种简洁的方式提供 EL 功能...
I tried really hard, but I did not manage not to comment that you are loosing type safety, (which is one of the main design goals of CDI), and that EL evaluation is performance killer ... ;-)
Anyhow, having (almost not) said this:
There is no real CDI option to overcome this. What I'd recommend you is to use a specific type for those EL-expressions, like
ElObject
or so, which is constructed by the producer and which itself provides type safe accessors for the client.Edit: You might want to have a look at Seam Solder, which provides EL-Functionality in a neat way...