【问题标题】:@Inject not working in AttributeConverter@Inject 在 AttributeConverter 中不起作用
【发布时间】:2015-10-11 12:58:21
【问题描述】:

我有一个简单的AttributeConverter 实现,我尝试注入一个必须提供转换逻辑的对象,但@Inject 似乎不适用于这种情况。转换器类如下所示:

@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。所以问题还没有解决。

【问题讨论】:

  • 可能与以下内容重复:stackoverflow.com/questions/12080317/…
  • 部分是重复的,但是提供了一个特定于 Spring 的解决方案,它对我不起作用,因为我没有使用 Spring。我用一些新的经验扩展了我的案例描述,但问题仍然没有解决。

标签: jpa glassfish converter cdi


【解决方案1】:

很遗憾,您无法将 CDI bean 注入 JPA 转换器,但在 CDI 1.1 中,您可以通过编程方式注入 Crypto:

Crypto crypto  = javax.enterprise.inject.spi.CDI.current().select(Crypto.class).get()

【讨论】:

    【解决方案2】:

    好吧,CDI 仍然不适用于 AttributeConverter,这将是最优雅的解决方案,但我找到了一个令人满意的解决方法。解决方法是使用@FacesConverter。不幸的是,默认 CDI 在 faces 转换器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
    {
       ...
    }
    

    现在转换器中有一个上下文信息。在我的情况下,它是会话/用户特定的加密配置。

    当然,在这样的解决方案中,很可能还需要自定义 @FacesValidator,但由于 CODI,人们也可以在这里使用 CDI(模拟到转换器)。

    【讨论】:

      【解决方案3】:

      您正在尝试结合两个不同的世界,因为 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 阶段设置静态类属性。然后,您只需使用静态存储库而不是注入的字段(当用作 JPA 转换器时,它仍然是 NULL)。

      【讨论】:

        【解决方案4】:

        作为参考,JPA 2.2 将允许 CDI 与 AttributeConverter 一起使用,并且一些供应商已经支持这一点(EclipseLink、DataNucleus JPA 是我所知道的这样做的)。

        【讨论】:

        • Hibernate 从 5.3.0+ 开始也支持此功能。
        猜你喜欢
        • 2014-07-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-15
        相关资源
        最近更新 更多