【问题标题】:Why is PostConstruct not called?为什么不调用 PostConstruct?
【发布时间】:2013-08-12 06:29:46
【问题描述】:

我正在开发一个简单的 Java EE 应用程序。

我有这样的课:

import javax.annotation.PostConstruct;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

@Stateless
public class BlogEntryDao {

    EntityManager em;

    @PostConstruct
    public void initialize(){
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Persistence");
        em = emf.createEntityManager();
    }

    public void addNewEntry(){
        Blogentry blogentry = new Blogentry();

        blogentry.setTitle("Test");
        blogentry.setContent("asdfasfas");

        em.persist(blogentry);

    }
}

所以我的托管 bean 调用了这个方法。直到这里没有问题。但是由于没有调用初始化方法,我在 em.persist 中得到了一个 NPE。

为什么没有调用初始化方法?我在 Glassfish 服务器上运行它。

问候。

【问题讨论】:

  • 对于初学者,您不应该在全局范围内打开您的EntityManagerEntityManager 大致对应一个会话。如果您确实需要处理自己的会话管理(注入@PersistenceContext 更好),您应该在每次调用addNewEntry 时创建和关闭EntityManager
  • 您是否确认您的容器将对象视为需要依赖注入的东西?不是每个对象都被钩住了,如果你只是在某个地方调用new BlogEntryDao(),容器可能不知道将它初始化为一个 bean。
  • @chrylis 是的,谢谢,我实际上正在调用新的 BlogEntryDao。
  • @chrylis 谢谢,将其更改为 EJB BlogEntryDao 修复了它。能否请您提供一个答案,以便我接受?
  • 我觉得可以在bean配置文件中定义init-method="initialize"。

标签: java jakarta-ee ejb


【解决方案1】:

@PostConstruct 等 Java EE bean 注释仅适用于容器管理的 bean。如果您只是自己调用new BlogEntryDao,容器不会拦截创建并调用@PostConstruct 方法。

(此外,您最好使用@PersistenceContext@PersistenceUnit 而不是在initialize() 方法中手动获取EntityManagerFactory,并且您应该为每次调用@987654329 创建一个EntityManager @,因为它们是短暂的。进行这些更改将完全消除对 initialize() 的需要。)

【讨论】:

    【解决方案2】:

    我在我的应用程序中遇到了同样的问题。 您没有发布您的 bean 上下文配置 xml 文件(所以我不确定它是否是同一个问题)但在我的情况下添加此行:

    <context:annotation-config/>
    

    解决了我的问题。 您需要&lt;context:annotation-config/&gt;&lt;context:component-scan/&gt; 来启用@PostConstruct 注解。

    【讨论】:

    • 注意:这对于 Spring 应用程序是正确的,但问题是关于一般 JavaEE 应用程序。
    【解决方案3】:

    由于这个问题首先出现在 Google 上的“postconstruct not called”,除了使用 new 关键字而不是将 @PostConstruct 放入 Spring bean 之外,可能不会调用 @PostConstruct 方法的另一个原因是如果你有循环依赖。

    如果这个 bean 依赖于另一个依赖于这个 bean 的 bean,那么你的另一个 bean 可能会在 BlogEntryDao 被初始化之前调用 addNewEntry(),即使 BlogEntryDao 是那个另一个 bean 的依赖项。

    这是因为由于循环引用,Spring 不知道您要先加载哪个 bean。在这种情况下,可以删除循环引用或使用@AutoWired/@Value构造函数参数而不是成员值或setter,或者如果使用xml配置,也许您可​​以交换bean定义的顺序。

    【讨论】:

      【解决方案4】:

      在我的例子中,@PostConstruct 没有被调用,因为我的 initialize() 方法是静态的并且也抛出了异常。在任何一种情况下,该方法都会被忽略。我希望它可以帮助其他犯同样错误的人。 这可以在控制台中找到:

      WARNING: JSF1044: Method '<XXX>' marked with the 'javax.annotation.PostConstruct' annotation cannot be static.  This method will be ignored.
      WARNING: JSF1047: Method '<XXX>' marked with the 'javax.annotation.PostConstruct' annotation cannot declare any checked exceptions.  This method will be ignored.
      

      【讨论】:

        【解决方案5】:

        在我的例子中,@PostConstruct 方法没有被调用,因为我直接在其他服务 bean(即 myService.myProperty)中引用了 spring 服务 bean 的 public instance variable。当我为该属性(即getMyProperty())公开getter method 并使用它来获取该属性时,再次调用@PostConstruct 方法。我还制作了myPropertyprivate,以防止将来发生任何意外的直接引用。

        另外请注意,如果您没有在 @Configuration 注释类中显式注册类并使用 @Bean 并单独依赖 @Autowired,则 @PostConstruct 方法可能不会在启动时立即执行。只有当自动装配类的其中一个方法被另一个类引用和调用时,才会加载该类,并且只有在那个时候才会调用@PostConstruct 方法。换句话说,只使用@Autowired,你实际上是在延迟加载一个类。如果你想在启动时加载它,注册到@Bean

        这是关于@Bean@Autowired Difference between @Bean and @Autowired 之间区别的一个很好的SO 线程

        编辑:最后一句话。当你有一个 web 应用程序并决定用 @RequestScope 注释你的类时,每次有新请求进来时都会调用 @Postconstruct 注释方法。这是因为 @RequestScope 指示 spring 创建一个新的 instance每次有新请求进入时,该类。如果您希望所有请求使用相同的实例,那么您可以使用上面提到的@Bean,但您也可以使用您的类上方的注释@Singleton。这将导致类在启动时立即加载。

        【讨论】:

          【解决方案6】:

          使用 Spring 时,请确保您使用了来自正确包的正确 PostConstruct 注释。

          javax.annotation.PostConstruct
          

          应该是那个。不是例如:

          jakarta.annotation.PostConstruct
          

          我花了一点时间才弄明白为什么我的 PostConstruct 中只有一个不起作用。

          【讨论】:

            【解决方案7】:

            确保具有@Postconstruct 方法的类位于同一个包中。我将类文件移动到主包,它工作。

            【讨论】:

              【解决方案8】:

              在我的例子中,我在类路径中有两个 javax.annotation.PostConstruct 实例。一个与war包捆绑在一起,另一个由tomcat提供。当 Spring 扫描 @PostConstruct 注释时,它会比较这两个不同的实例。因此扫描时没有选择@PostConstruct注解的方法。

              只提供javax.annotation-api 库的一个实例解决了这个问题。

              【讨论】:

                【解决方案9】:

                因为大多数方式都已经提到了。但是也可以在类的配置文件中创建一个 bean

                org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

                ;

                这将启用 PostConstructPreDestroy 注释。

                <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"></bean>
                

                对于 Predestroy 也需要调用 context.registerShutDownHook()

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2011-03-25
                  • 2017-06-26
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-11-24
                  • 2014-02-14
                  • 2016-02-10
                  相关资源
                  最近更新 更多