为什么PostConstruct方法没有被调用?

37
我正在开发一个简单的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调用了这个方法。到这里没有问题。但是由于initialize方法没有被调用,我在em.persist中遇到了NPE。
为什么initialize方法没有被调用?我在Glassfish服务器上运行这个。

3
首先,你不应该在全局范围内打开你的 EntityManagerEntityManager 大致相当于一个会话。如果你真的需要处理自己的会话管理(注入 @PersistenceContext 更好),你应该在每次调用 addNewEntry 时创建和关闭一个 EntityManager - chrylis -cautiouslyoptimistic-
1
你确认你的容器将对象视为需要依赖注入的内容吗?并非所有对象都会被钩入,如果你只是在某个地方调用了 new BlogEntryDao(),那么容器可能不知道如何将其初始化为一个 bean。 - chrylis -cautiouslyoptimistic-
@chrylis 感谢,我正在调用新的BlogEntryDao。 - Koray Tugay
@chrylis 谢谢您,将它改成 EJB BlogEntryDao 就解决了。能否请您提供一个答案,这样我就可以接受它了吗? - Koray Tugay
我认为你可以在bean配置文件中定义init-method="initialize"。 - Weidian Huang
9个回答

29

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

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


19

我在我的应用程序中遇到了相同的问题。 您没有发布您的Bean上下文配置XML文件(因此我不确定是否是相同的问题),但在我的情况下,添加这行代码:

<context:annotation-config/>
解决了我的问题。 您需要使用<context:annotation-config /><context:component-scan />来启用@PostConstruct注释。

2
注意:这对于Spring应用程序是正确的,但问题是关于一般的JavaEE应用程序。 - stefan.m

19

由于在谷歌上搜索“postconstruct not called”,这个问题会首先出现,除了使用new关键字而不是将@PostConstruct放入Spring bean之外,@PostConstruct方法未被调用的另一个原因是存在循环依赖。

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

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


6

在我的情况下,@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.

4

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

javax.annotation.PostConstruct

应该是这个。而不是例如:

jakarta.annotation.PostConstruct

我花了一点时间才弄清楚为什么只有一个我的PostConstruct没有起作用。


2
在我的情况下,@PostConstruct方法没有被调用,因为我直接在其他服务bean中引用了Spring服务bean的公共实例变量(即myService.myProperty)。当我为该属性创建了一个公共的getter方法(即getMyProperty()),并使用它来获取该属性时,@PostConstruct方法再次被调用。此外,我将myProperty设置为private,以防止未来发生任何意外的直接引用。
还要注意,如果您不在@Configuration注释的类中显式注册类,并仅依赖于@Autowired,则@PostConstruct方法可能不会立即在启动时执行。只有当另一个类引用并调用自动连接类的某个方法时,才会加载该类,仅在那时@PostConstruct方法才会被调用。换句话说,仅使用@Autowired相当于惰性加载类。如果要在启动时加载它,请使用@Bean进行注册。
这是一个关于@Bean@Autowired之间区别的好的SO线程Difference between @Bean and @Autowired
编辑:最后一点说明。当您有一个Web应用程序并决定使用@RequestScope注释您的类时,@Postconstruct注释的方法将在每次新请求到达时被调用。这是因为@RequestScope指示Spring每次新请求到达时创建一个新的instance类。如果您希望所有请求使用同一个实例,则可以像上面提到的那样使用@Bean,但您也可以在类的上方使用注释@Singleton。这将导致该类在启动时被急切地加载。

1
确保具有@PostConstruct方法的类位于同一包中。我将类文件移动到主包中,它就起作用了。

1
在我的情况下,类路径中有两个javax.annotation.PostConstruct实例。一个是与war包捆绑的,另一个是由tomcat提供的。当Spring扫描@PostConstruct注释时,它会比较这两个不同的实例。因此,在扫描时未选择带有@PostConstruct注释的方法。
只提供一个javax.annotation-api库实例即可解决问题。

如何做到这一点?如何提供库的唯一实例? - andy489
怎么做到呢?如何只提供一个库的实例? - undefined
对我来说,我有一个多模块项目,其中包含主函数的模块正在扫描另一个模块,两个模块的pom文件都有这个依赖项。从包含主函数的pom文件中移除依赖项(因为它没有使用postconstruct)解决了这个问题。 - undefined

0

由于大多数方法已经被提到。但是,您也可以在配置文件中为类创建一个bean。

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

;

这将启用PostConstructPreDestroy注释。

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

同时,在使用Predestroy时,需要调用context.registerShutDownHook()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接