英文:
NPE on a @Inject field for a @Singleton class
问题
我在一个奇怪的场景中遇到了一个问题,无论我尝试什么解决方案,使用CDI来管理一个单例都导致失败,现在我甚至不确定这是否可行/可以完成,所以我在这里发布帖子。
我将尽力解释当前情况,并想知道我的意图是否可行,如果是,以何种方式进行,如果不行,是否有一些替代方法来“升级”该jar中的单例实例。如果我遗漏了代码的细节/部分,请告诉我,因为这有点不寻常,我不确定我是否提供了所有所需的信息。
背景:
- 使用JBoss 7.2 EAP作为服务器;
- 一个大的Maven项目,父POM文件仅充当POM,为所有其他子项目(主要是ejb/ear/war类型)提供Maven的依赖项/配置;
- 在JBoss中使用的自定义模块为一些项目提供了许多依赖项,因此它只是一个包含jar文件的文件夹,包括在其他项目中使用的自定义jar;
- 部署到服务器的testing-purposes-ear.ear,其中包含testing-ejb和testing-war项目;
- war项目对自定义模块中的一个jar文件具有provided依赖关系(实际上,该jar实际上只是主项目的另一个子项目),在编译和运行时,它能够正常解析,没有问题(与使用Maven依赖项完全相同,在我看来);
- 到目前为止,war项目通过getInstance()方法调用来自该jar的单例实例(纯粹作为业务逻辑),一切正常。
意图:
- 删除jar中单例类的所有私有构造函数/getInstance(),并使用**@Singleton进行注释,希望通过使用@Inject**的方式改变war项目中的所有调用,而不是使用getInstance()方法。
已采取的步骤:
-
对于一个单一的类,删除了私有构造函数,getInstance(),为该类创建了一个新接口,仅公开某些方法,单例类实现了新接口,并使用javax.inject的**@Singleton**注解进行了标记。
-
在war项目中,删除了getInstance()的调用,并在使用它的类中将变量设置为**@Inject**作为字段,如下所示:
@Inject private Management managementInterface; ... void someMethod(){ managementInterface.performBusinessLogic(); }
-
这导致调用performSomeBusinessLogic()时抛出NPE,对此我找到了几个答案,因此我在该jar中添加了一个beans.xml,其中设置了discovery-mode=all,尝试了javax.ejb中的**@Singleton**,但无法使其可能进行注入。
-
我已经搜索了相当长的时间,看看如何完成这个操作,但这里/谷歌上描述的大多数场景都是关于在同一个容器中部署的应用程序之间的inter-ejb通信,但这不一样:我们正试图使用CDI从一个不是ejb/war项目的jar文件中注入一个单例,而是一个简单的Java库。
需要考虑的事项:
-
该单例类以前在多个ejb/war项目中使用过,没有任何问题,因此将其放入JBoss中作为自定义模块和独立的jar意味着对于所有调用的ejb/war项目,实际上只有一个该类的实例,并且我们避免了与此类问题相关的整个上下文/类加载问题。
-
该单例暴露了许多方法,其中一些最好不要暴露,因此我想在它之前放一个接口,以便调用的客户端只能访问某些方法,而不是所有方法。
-
由于项目的结构和依赖关系的管理方式,该jar文件可以访问许多库/依赖项,因此打算放弃旧的创建和使用单例的方法,并将其迁移到更现代的方法。
-
IDE(Intellij)能够解析依赖关系,没有问题,如下图所示,单击蓝色标记图标会导航到标有**@Singleton**并且实现了该接口的类。
问题:
- 是否可能将该jar项目中的单例从经典方式更改为使用服务器上的CDI?在这方面,最好是通过使用注解的方式,但如果有其他可用的解决方案,我也可以考虑。
1: https://i.stack.imgur.com/3rimV.png
英文:
I have come across a curious scenario in which any attempt of using CDI for managing a singleton has resulted in failure no matter the solutions I tried, and I am not even sure at this point if this is something feasable/that can be done, hence the reason I am posting here.
I'll try to explain to my best the current scenario and I would like to know whether my intention can be done, and if yes, by what way, and if not, is there some alternative to "upgrading" the singleton instances in that jar. If I have missed details/parts of the code, please let me know, as this is something a little bit unusual and I do not if I provided everything needed.
Background:
- JBoss 7.2 EAP being used as a server;
- One big Maven project, the parent pom file only acts as a pom to provide dependencies/configurations in maven for all the other sub-projects, which are of type ejb/ear/war mainly.
- Custom module being used in the JBoss that provides a number of the dependencies for some projects, so it is just a folder full of jar files, including custom jars that are being used throughout the other projects.
- testing-purposes-ear.ear being deployed to the server, that contains an testing-ejb and testing-war projects.
- The war project has a provided dependency to one of the jar files from the custom module (the jar is actually just another sub-project of the main project), and at compile and runtime it was resolving okay and there no were issues (exactly the same as using a maven dependency in my opinion);
- Until now, the war project was calling the singleton instances from that jar (acting purely as business logic) by the getInstance() method, and everything was working fine.
Intention:
- Removing all the private constructors/getInstance() from the jar for the singleton classes and annotating them with @Singleton in the hopes of changing all the calls in the war project to another way, by using the @Inject, instead of using the getInstance() method.
Steps taken:
-
For one single class, removed the private constructor, getInstance(), created a new interface for the class to expose only certain methods, and the singleton class was implementing the new interface and was marked with the @Singleton annotation from javax.inject.
-
In the war project, removed the getInstance() call, and set the variable with @Inject as a field in the class that was using it, like so:
@Inject private Management managementInterface; ... void someMethod(){ managementInterface.performBusinessLogic(); }
-
This resulted in a NPE being thrown when calling performSomeBusinessLogic(), for which I found several answers, so I have added a beans.xml to that jar with discovery-mode=all, tried the @Singleton
from javax.ejb, but nothing worked to make it possible to inject that singleton. -
I have searched quite some time to see how this could be done, but most of the scenarios depicted here/on google are about inter-ejb communication between application deployed in the same container, but this is not the same : we are trying to use CDI to inject a singleton from a jar file that is not an ejb/war project, but rather a simple java library.
Things to consider:
-
That singleton class was previously used in multiple ejb/war projects with no issues, so the fact that it was put as a custom module in JBoss and a standalone jar means there really is one only instance of that class for all the calling ejb/war projects, and we avoid the whole context/class-loading typical issues that come with this stuff.
-
That singleton exposed quite a lot of methods, some of which were actually better not exposed, so I would like to put an interface before it so the calling clients will only have access to some methods, but not all of them.
-
The jar file has access to a lot of libraries/dependencies due to the structure of the project and the way the dependencies are managed, so the intention was to drop the old way of creating and using singletons and migrating it to something more modern.
-
The IDE (Intellij) is able to resolve the dependency without issues, as can be seen in the following attachment, on clicking the blue mark icon it leads to the class that is marked with @Singleton and that's implementing that interface.
Question:
- Is it possible to change the singletons from that jar project from the classic way to a way that uses CDI from the server? In this regards, something by the use of annotations would be best, but if there are any other solutions available, I'm open to them.
答案1
得分: 1
如果 Management 类位于一个 JAR 依赖项中(由 JBoss 提供或打包在 EAR 文件内),则不会被视为 CDI Bean。
它必须位于与 testing-ejb 同级打包的 EJB/CDI 模块内。
另一种方法是使用 @Remote EJB 注解,并将 JAR 文件部署为库,而不是简单的依赖项。
我并不清楚这次重构的目的,但如果目的是避免在每个需要它的 Bean 中手动实例化该类,你可以编写一个简单的生产者:
@Singleton
public class ManagementProvider {
@Produces
public Management getInstance() {
return Management.getInstance();
}
}
然后你可以在任何需要的地方使用 @Inject!
英文:
If the Management class is in a jar dependency (provided from JBoss or packaged inside the ear) is not viewed as a CDI bean.
It must be insiede an EJB/CDI module packaged at the same level as testing-ejb.
Another way is to use @Remote EJB annotation and deploy the jar as a library, not a simple dependecy.
It's not clear (for me) the purpose of this refactoring but if is to avoid to instantiate the class manually on every bean that need it you could write a simple producer:
@Singleton
public class ManagementProvider() {
@Produces
public Management getInstance() {
return Management.getInstance();
}
}
Then you can use @Inject anytime you want!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论