![](/img/trans.png)
[英]Mockito + Spring + @PostConstruct, mock initialization error, why is @PostConstruct called?
[英]Why is PostConstruct not called?
我正在开发一个简单的 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 服务器上运行它。
问候。
诸如@PostConstruct
类的 Java EE bean 注释仅适用于容器管理的 bean。 如果您只是自己调用new BlogEntryDao
,则容器不会拦截创建并调用@PostConstruct
方法。
(此外,最好使用@PersistenceContext
或@PersistenceUnit
而不是在initialize()
方法中手动获取EntityManagerFactory
,并且您应该为每次调用addNewEntry()
创建一个EntityManager
,因为它们是短暂的。进行这些更改将完全消除对initialize()
的需要。)
我在我的应用程序中遇到了同样的问题。 你没有发布你的 bean 上下文配置 xml 文件(所以我不确定它是否是同样的问题)但在我的情况下添加这一行:
<context:annotation-config/>
解决了我的问题。 您需要<context:annotation-config/>
或<context:component-scan/>
来启用 @PostConstruct 注释。
由于这个问题首先出现在 Google 上,因为“未调用 postconstruct”,除了使用new
关键字而不是将@PostConstruct
放入 Spring bean 之外,可能不会调用@PostConstruct
方法的另一个原因是,如果您有循环依赖。
如果这个 bean 依赖于另一个依赖这个 bean 的 bean,那么您的另一个 bean 可能会在BlogEntryDao
初始化之前调用addNewEntry()
,即使 BlogEntryDao 是另一个 bean 的依赖项。
这是因为由于循环引用,Spring 不知道您要首先加载哪个 bean。 在这种情况下,可以删除循环引用或使用@AutoWired
/ @Value
构造函数参数代替成员值或 setter,或者如果使用 xml 配置,也许您可以交换定义 bean 的顺序。
在我的例子中,@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.
在我的例子中,@ @PostConstruct
方法没有被调用,因为我直接在其他服务 bean(即 myService.myProperty)中引用了 spring 服务 bean 的public instance variable
。 当我为属性创建一个公共getter method
(即getMyProperty()
)并使用它来获取属性时,@ @PostConstruct
方法再次被调用。 此外,我将myProperty
设为private
以防止将来发生任何意外的直接引用。
另请注意,如果您没有在带有@Configuration
注释的类中使用@Bean
显式注册该类, @Bean
依赖于@Autowired
,则@PostConstruct
方法可能不会在启动时立即执行。 只有当自动装配类的一个方法被另一个类引用和调用时,该类才会被加载,并且只有在那个时候@PostConstruct
方法才会被调用。 换句话说,仅使用@Autowired
本质上就是延迟加载类。 如果你想在启动时加载它,用@Bean
注册它
这是一个关于@Bean
和@Autowired
之间区别的很好的 SO 线程@Bean
和@Autowired
之间的区别
编辑:最后一句话。 当你有一个 web 应用程序并决定用@RequestScope
注释你的类时,每次有新请求进来时都会调用@Postconstruct
注释的方法。这是因为@RequestScope
指示 spring 每次创建类的新instance
新的请求进来了。如果你想让所有的请求都使用同一个实例,那么你可以使用@Bean
提到的@Bean
,但你也可以在你的类上面使用注释@Singleton
。 这将导致类在启动时急切地加载。
使用 Spring 时,请确保使用来自正确包的正确 PostConstruct 注释。
javax.annotation.PostConstruct
应该是那个。 不是例如:
jakarta.annotation.PostConstruct
我花了一点时间才弄清楚为什么只有一个 PostConstruct 不起作用。
确保具有@Postconstruct
方法的类位于同一个包中。 我将类文件移动到主包并且它起作用了。
就我而言,我在类路径中有两个javax.annotation.PostConstruct
实例。 一个与 war 包捆绑在一起,另一个由 tomcat 提供。 当 Spring 扫描@PostConstruct
注释时,它会比较这两个不同的实例。 因此在扫描时没有选择@PostConstruct
注释方法。
仅提供javax.annotation-api
库的一个实例解决了该问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.