@Inject 在 AttributeConverter 中不起作用
@Inject not working in AttributeConverter
我有一个简单的 AttributeConverter
实现,其中我尝试注入一个必须提供转换逻辑的对象,但 @Inject
似乎不适用于这种情况。转换器 class 如下所示:
@Converter(autoApply=false)
public class String2ByteArrayConverter implements AttributeConverter<String, byte[]>
{
@Inject
private Crypto crypto;
@Override
public byte[] convertToDatabaseColumn(String usrReadable)
{
return crypto.pg_encrypt(usrReadable);
}
@Override
public String convertToEntityAttribute(byte[] dbType)
{
return crypto.pg_decrypt(dbType);
}
}
当 @Converter
被触发时它会抛出一个 NullPointerException
因为 属性 crypto
没有从容器中初始化。这是为什么?
我使用的是 Glassfish 4,在所有其他情况下 @Inject
工作正常。
不能在转换器上使用 CDI 吗?
任何帮助将不胜感激:)
我的问题的重点更多是 AttributeConverter 部分。我知道要使 CDI 工作,bean 必须满足此处描述的条件 http://docs.oracle.com/javaee/6/tutorial/doc/gjfzi.html。
我还尝试通过实现以下构造函数来强制 CDI 工作:
@Inject
public String2ByteArrayConverter(Crypto crypto)
{
this.crypto = crypto;
}
现在我得到了以下没有给我任何线索的异常:
2015-07-23T01:03:24.835+0200|Severe: Exception during life cycle processing
org.glassfish.deployment.common.DeploymentException: Exception [EclipseLink-28019] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Deployment of PersistenceUnit [PU_VMA] failed. Close all factories for this PersistenceUnit.
Internal Exception: Exception [EclipseLink-7172] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Error encountered when instantiating the class [class model.converter.String2ByteArrayConverter].
Internal Exception: java.lang.InstantiationException: model.converter.String2ByteArrayConverter
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.createDeployFailedPersistenceException(EntityManagerSetupImpl.java:820)
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:760)
...
我什至尝试使用@Producer 或@Decorator 以使CDI 在那个地方工作,但我仍然认为AttributeConverter 有一些特定的东西不允许CDI。所以问题还没有解决。
不幸的是,您不能将 CDI bean 注入 JPA 转换器,但是在 CDI 1.1 中,您可以通过编程方式注入您的 Crypto:
Crypto crypto = javax.enterprise.inject.spi.CDI.current().select(Crypto.class).get()
嗯,CDI 仍然不适用于 AttributeConverter,这将是最优雅的解决方案,但我找到了一个令人满意的解决方案解决方法。解决方法是使用 @FacesConverter
。不幸的是,默认 CDI 在 faces converters 和 validators 中也不起作用,但是由于 Apache MyFaces CODI API 你可以让它正常工作@Advaced
注释 :) 所以我想出了一个这样的实现:
@Advanced
@FacesConverter("cryptoConverter")
public class CryptoJSFConverter implements Converter
{
private CryptoController crypto = new CryptoController();
@Inject
PatientController ptCtrl;
public Object getAsObject(FacesContext fc, UIComponent uic, String value)
{
if(value != null)
return crypto.pg_encrypt(value, ptCtrl.getSecretKey());
else
return null;
}
public String getAsString(FacesContext fc, UIComponent uic, Object object)
{
String res = crypto.pg_decrypt((byte[]) object, ptCtrl.getSecretKey());
return res;
}
}
注入的托管 bean 必须用 @Named
和一些范围定义显式注释。 faces-config.xml
中的声明不 起作用!在我的解决方案中,它看起来像这样:
@Named
@SessionScoped
public class PatientController extends PersistanceManager
{
...
}
现在转换器中有一个上下文信息。在我的例子中,它是 session/user 特定的密码配置。
当然,在这样的解决方案中,很可能还需要自定义 @FacesValidator
,但多亏了 CODI,这里也有可能使用 CDI (模拟转换器)。
您正在尝试结合两个不同的世界,因为 CDI 不知道 JPA Stuff,反之亦然。 (一个注释解析器当然不知道另一个)
你能做什么,是这样的:
/**
* @author Jakob Galbavy <code>jg@chex.at</code>
*/
@Converter
@Singleton
@Startup
public class UserConverter implements AttributeConverter<User, Long> {
@Inject
private UserRepository userRepository;
private static UserRepository staticUserRepository;
@PostConstruct
public void init() {
staticUserRepository = this.userRepository;
}
@Override
public Long convertToDatabaseColumn(User attribute) {
if (null == attribute) {
return null;
}
return attribute.getId();
}
@Override
public User convertToEntityAttribute(Long dbData) {
if (null == dbData) {
return null;
}
return staticUserRepository.findById(dbData);
}
}
这样,您将创建一个单例 EJB,它是在容器启动时创建的,在 PostConstruct 阶段设置静态 class 属性。然后,您只需使用静态存储库而不是注入的字段(当用作 JPA 转换器时,它仍然是 NULL)。
作为参考,JPA 2.2 将允许 CDI 与 AttributeConverter 一起使用,一些供应商已经支持这一点(据我所知,EclipseLink、DataNucleus JPA 就是这样做的)。
我有一个简单的 AttributeConverter
实现,其中我尝试注入一个必须提供转换逻辑的对象,但 @Inject
似乎不适用于这种情况。转换器 class 如下所示:
@Converter(autoApply=false)
public class String2ByteArrayConverter implements AttributeConverter<String, byte[]>
{
@Inject
private Crypto crypto;
@Override
public byte[] convertToDatabaseColumn(String usrReadable)
{
return crypto.pg_encrypt(usrReadable);
}
@Override
public String convertToEntityAttribute(byte[] dbType)
{
return crypto.pg_decrypt(dbType);
}
}
当 @Converter
被触发时它会抛出一个 NullPointerException
因为 属性 crypto
没有从容器中初始化。这是为什么?
我使用的是 Glassfish 4,在所有其他情况下 @Inject
工作正常。
不能在转换器上使用 CDI 吗?
任何帮助将不胜感激:)
我的问题的重点更多是 AttributeConverter 部分。我知道要使 CDI 工作,bean 必须满足此处描述的条件 http://docs.oracle.com/javaee/6/tutorial/doc/gjfzi.html。 我还尝试通过实现以下构造函数来强制 CDI 工作:
@Inject
public String2ByteArrayConverter(Crypto crypto)
{
this.crypto = crypto;
}
现在我得到了以下没有给我任何线索的异常:
2015-07-23T01:03:24.835+0200|Severe: Exception during life cycle processing
org.glassfish.deployment.common.DeploymentException: Exception [EclipseLink-28019] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Deployment of PersistenceUnit [PU_VMA] failed. Close all factories for this PersistenceUnit.
Internal Exception: Exception [EclipseLink-7172] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Error encountered when instantiating the class [class model.converter.String2ByteArrayConverter].
Internal Exception: java.lang.InstantiationException: model.converter.String2ByteArrayConverter
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.createDeployFailedPersistenceException(EntityManagerSetupImpl.java:820)
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:760)
...
我什至尝试使用@Producer 或@Decorator 以使CDI 在那个地方工作,但我仍然认为AttributeConverter 有一些特定的东西不允许CDI。所以问题还没有解决。
不幸的是,您不能将 CDI bean 注入 JPA 转换器,但是在 CDI 1.1 中,您可以通过编程方式注入您的 Crypto:
Crypto crypto = javax.enterprise.inject.spi.CDI.current().select(Crypto.class).get()
嗯,CDI 仍然不适用于 AttributeConverter,这将是最优雅的解决方案,但我找到了一个令人满意的解决方案解决方法。解决方法是使用 @FacesConverter
。不幸的是,默认 CDI 在 faces converters 和 validators 中也不起作用,但是由于 Apache MyFaces CODI API 你可以让它正常工作@Advaced
注释 :) 所以我想出了一个这样的实现:
@Advanced
@FacesConverter("cryptoConverter")
public class CryptoJSFConverter implements Converter
{
private CryptoController crypto = new CryptoController();
@Inject
PatientController ptCtrl;
public Object getAsObject(FacesContext fc, UIComponent uic, String value)
{
if(value != null)
return crypto.pg_encrypt(value, ptCtrl.getSecretKey());
else
return null;
}
public String getAsString(FacesContext fc, UIComponent uic, Object object)
{
String res = crypto.pg_decrypt((byte[]) object, ptCtrl.getSecretKey());
return res;
}
}
注入的托管 bean 必须用 @Named
和一些范围定义显式注释。 faces-config.xml
中的声明不 起作用!在我的解决方案中,它看起来像这样:
@Named
@SessionScoped
public class PatientController extends PersistanceManager
{
...
}
现在转换器中有一个上下文信息。在我的例子中,它是 session/user 特定的密码配置。
当然,在这样的解决方案中,很可能还需要自定义 @FacesValidator
,但多亏了 CODI,这里也有可能使用 CDI (模拟转换器)。
您正在尝试结合两个不同的世界,因为 CDI 不知道 JPA Stuff,反之亦然。 (一个注释解析器当然不知道另一个) 你能做什么,是这样的:
/**
* @author Jakob Galbavy <code>jg@chex.at</code>
*/
@Converter
@Singleton
@Startup
public class UserConverter implements AttributeConverter<User, Long> {
@Inject
private UserRepository userRepository;
private static UserRepository staticUserRepository;
@PostConstruct
public void init() {
staticUserRepository = this.userRepository;
}
@Override
public Long convertToDatabaseColumn(User attribute) {
if (null == attribute) {
return null;
}
return attribute.getId();
}
@Override
public User convertToEntityAttribute(Long dbData) {
if (null == dbData) {
return null;
}
return staticUserRepository.findById(dbData);
}
}
这样,您将创建一个单例 EJB,它是在容器启动时创建的,在 PostConstruct 阶段设置静态 class 属性。然后,您只需使用静态存储库而不是注入的字段(当用作 JPA 转换器时,它仍然是 NULL)。
作为参考,JPA 2.2 将允许 CDI 与 AttributeConverter 一起使用,一些供应商已经支持这一点(据我所知,EclipseLink、DataNucleus JPA 就是这样做的)。