我刚刚在GlassFish服务器下发生了臭名昭着的JavaEE CDI错误:
org.glassfish.deployment.common.DeploymentException: CDI deployment failure:Exception List with 2 exceptions:
Exception 0 :
org.jboss.weld.exceptions.DeploymentException: WELD-001437 Normal scoped bean class ASController is not proxyable because the type is final or it contains a final method public final void ASController.selectPath(org.primefaces.event.NodeSelectEvent) - Managed Bean [class ASController] with qualifiers [@Default @Any @Named].
错误很明显,因为他不喜欢CDI bean中的最终方法,但是我无法理解为什么。
在此链接
http://docs.jboss.org/weld/reference/1.1.0.Final/en-US/html_single/#d0e1429
他们解释说它与序列化有关,但我不明白为什么用最终方法序列化一个类应该比使用非最终方法的类更难。
答案 0 :(得分:13)
嗯,有几种方法可以实现代理对象。但是,由于您希望代理与代理bean具有“相同”类型,因此您必须使用继承(或者您可以实现的需求接口,但这不是每个POJO都可以作为bean的方法) CDI)。
也就是说,它们在内部从您想要注入的类扩展,围绕它生成一些代理代码并为您提供该子类。
然后这个代理正在处理所有的魔法,以确保你总是有一个适合你的上下文的bean(这个bean的所有成员变量bean都指向恰到好处的bean)。
所以你并没有真正收到你想要注入的bean的类型,而是接受那个bean的代理子类。这对最终方法和类以及私有构造函数不起作用。
如果该类不是final,则代理可以扩展此类,但是它不能轻易覆盖您的最终方法。然而,这可能是需要的(例如,如果您的bean被序列化,代理需要对其进行反序列化)。
有更复杂的方法。可以通过代理操作类的字节代码来注入此功能(例如删除最终修饰符,注入默认构造函数,......)甚至可以将它与继承混合使用,但这只是没有实现,但是(和同样非常容易支持多个JVM实现)。
从链接的资源中注明一个说明,计划将来发布此版本:
请注意
未来的Weld版本可能会支持非标准的解决方法 对于此限制,使用非可移植JVM API: Sun,IcedTea,Mac:Unsafe.allocateInstance()(效率最高) IBM,JRockit:ReflectionFactory.newConstructorForSerialization()
但我们还没有实现这个目标。
答案 1 :(得分:10)
容器为注入的类创建代理对象。因此,容器不使用您的类,但这些类扩展。 Java禁止扩展最终类,因此您不能在CDI中使用最终类。