简体   繁体   English

如何编写Java EE / EJB Singleton?

[英]How do I write a Java EE/EJB Singleton?

A day ago my application was one EAR, containing one WAR, one EJB JAR, and a couple of utility JAR files. 一天前,我的应用程序是一个EAR,其中包含一个WAR,一个EJB JAR和几个实用程序JAR文件。 I had a POJO singleton class in one of those utility files, it worked, and all was well with the world: 在其中一个实用程序文件中,我有一个POJO单例类,它可以正常工作,并且一切都很好:

EAR
 |--- WAR
 |--- EJB JAR
 |--- Util 1 JAR
 |--- Util 2 JAR
 |--- etc.

Then I created a second WAR and found out (the hard way) that each WAR has its own ClassLoader, so each WAR sees a different singleton, and things break down from there. 然后,我创建了第二个WAR,并发现(困难的方式)每个WAR都有自己的ClassLoader,因此每个WAR看到一个不同的单例,然后事情就从那里分解了。 This is not so good. 这不是很好。

EAR
 |--- WAR 1
 |--- WAR 2
 |--- EJB JAR
 |--- Util 1 JAR
 |--- Util 2 JAR
 |--- etc.

So, I'm looking for a way to create a Java singleton object that will work across WARs (across ClassLoaders?). 因此,我正在寻找一种创建Java单例对象的方法,该对象将跨WAR(跨ClassLoader?)工作。 The @Singleton EJB annotation seemed pretty promising until I found that JBoss 5.1 doesn't seem to support that annotation (which was added as part of EJB 3.1). 在我发现JBoss 5.1似乎不支持该注释(它是作为EJB 3.1的一部分添加)之前,@ @Singleton EJB注释似乎很有希望。 Did I miss something - can I use @Singleton with JBoss 5.1? 我是否错过了某些事情-我可以在JBoss 5.1中使用@Singleton吗? Upgrading to JBoss AS 6 is not an option right now. 现在不能升级到JBoss AS 6。

Alternately, I'd be just as happy to not have to use EJB to implement my singleton. 另外,我也很高兴不必使用EJB来实现我的单例。 What else can I do to solve this problem? 我还能做些什么来解决这个问题? Basically, I need a semi-application-wide* hook into a whole bunch of other objects, like various cached data, and app config info. 基本上,我需要一个半应用程序范围的*钩子,连接到一大堆其他对象中,例如各种缓存的数据和应用程序配置信息。 As a last resort, I've already considered merging my two WARs into one, but that would be pretty hellish. 作为最后的选择,我已经考虑过将两个WAR合并为一个,但这简直太可怕了。

*Meaning: available basically anywhere above a certain layer; *含义:基本上可以在某个层以上的任何位置使用; for now, mostly in my WARs - the View and Controller (in a loose sense). 现在,主要是在我的WAR中-视图和控制器(广义上)。

Edit: I should really be calling it Java EE rather than J2EE, shouldn't I? 编辑:我真的应该称它为Java EE,而不是J2EE,不是吗?


Edit 2: Many thanks again to @Yishai for all the help. 编辑2:非常感谢@Yishai的所有帮助。 After some trial-and-error it looks like I've figured out how to use a single ClassLoader across WARs under JBoss 5. I'm detailing this below for my own sake, and hopefully others will find this useful as well. 经过反复试验,似乎我已经弄清楚了如何在JBoss 5下的WAR中使用单个ClassLoader。我为自己着想在下面对此进行了详细说明,希望其他人也会发现它也很有用。

NB this is rather different from doing this under JBoss 4 (see Yishai's answer or my links below). 注意,这与在JBoss 4下执行此操作大不相同(请参见下面的Yishai答案或我的链接)。

Instead of writing a jboss-web.xml for each WAR, and a jboss.xml for ear EJB-JAR, put a jboss-classloading.xml file in each WAR, in the same location as the DD ( web.xml ). 不要为每个WAR编写一个jboss-web.xml为每个EJB-JAR编写一个jboss.xml ,而是在每个WAR中将一个jboss-classloading.xml文件放在与DD( web.xml )相同的位置。 The contents of jboss-classloading.xml should be: jboss-classloading.xml的内容应为:

<?xml version="1.0" encoding="UTF-8"?>
<classloading
    xmlns="urn:jboss:classloading:1.0"
    name="mywar.war"
    domain="DefaultDomain"
    parent-domain="Ignored"
    export-all="NON_EMPTY"
    import-all="true">
</classloading>

This follows from the JBoss CW here , whereas what (I think) works for JBoss 4.x is described here . 这从JBoss CW如下这里 ,而对于被描述的JBoss 4.x的东西(我认为)的作品在这里 More general info on JBoss classload(ing/ers): 有关JBoss classload(ing / ers)的更多常规信息:

As best I can tell, the JBoss community wiki docs are pretty lacking for JBoss 5 in comparison to JBoss 4. 据我所知,与JBoss 4相比,JBoss 5缺少JBoss社区Wiki文档。

Although the EJB3.1 spec introduces singleton and your version of JBoss doesn't support it, you can use the JBoss @Service annotation to create a singleton. 尽管EJB3.1规范引入了单例,并且您的JBoss版本不支持单例,但是您可以使用JBoss @Service批注创建单例。 Instructions here . 说明在这里 Also, it seems that you have JBoss configured to isolate your ejb jars and wars from each other. 另外,似乎您已配置JBoss来隔离ejb jar和war。 You don't have to do that. 您不必这样做。 You can look at the loader-repository tag in the jboss specific xml files so that your whole ear shares one classloader (or perhaps that at least the two wars share one classloader). 您可以查看jboss特定xml文件中的loader-repository标记,以便您的整个耳朵共享一个类加载器(或者也许至少两次战争共享一个类加载器)。

All that being said, I agree with @duffymo, that a singleton which shares state between the two wars is an idea that you should be walking, if not running away from. 话虽如此,我同意@duffymo的观点,即在两次战争之间共享状态的单身人士是一个想法,即使不逃避也应该走。

Edit: Regarding singletons, I suggest you look at questions like this one (which also has some nice balance in the comments). 编辑:对于单身,我建议你看一下这样的问题这一个 (其中也有在评论一些很好的平衡)。

The idea of having an object hold cached state in and of itself is ok, especially with EJB3 where you can inject your state instead of statically referencing it (if you use the @Service annotation, then you want the @Depends JBoss specific annotation). 让对象本身保持缓存状态的想法是可以的,尤其是对于EJB3,您可以在其中注入状态而不是静态引用状态(如果使用@Service批注,则需要@Depends JBoss特定批注)。 That being said, if you were using a "proper" singleton here, then I would expect that your only problem with the fact that your WARs have two separate classloaders is the extra memory footprint. 话虽如此,如果您在这里使用“适当的”单例,那么我希望您的WAR具有两个单独的类加载器这一事实的唯一问题是额外的内存占用。 Otherwise you are into the problematic area of singletons (where they have to be initialized to be used, everything that uses them has to ensure they are initialized first, and of course all code gets highly coupled with their being initialized). 否则,您将陷入单例的问题区域(必须对其进行初始化才能使用,使用它们的所有对象都必须确保首先对其进行初始化,并且当然所有代码都与它们的初始化高度相关)。

Where Singletons are really really bad is where they store state so that one class can change state and another class picks it up. Singleton真正真正糟糕的地方是它们存储状态,以便一个类可以更改状态,而另一类可以获取状态。 It is basically a no-no in EJBs until 3.1, and even then it makes a lot of concurrency issues. 在3.1之前,它在EJB中基本上是禁止使用的,即使那样,它也会带来很多并发问题。

Edit (further): So you want to go with the classloader repository. 编辑(进一步):因此,您想使用classloader存储库。 I use JBoss 4.2.3, so I don't necessarily know all of the ins and outs of JBoss5 (which did rewrite its classloader although they say it is almost fully backwards compatable), however in 4.2.x by default your configuration causes no problems because all the ears deployed on the server share the same classloader (the "unified classloader"). 我使用的是JBoss 4.2.3,所以我不一定了解JBoss5的所有内容(确实重写了它的类加载器,尽管他们说它几乎是完全向后兼容的),但是默认情况下在4.2.x中,您的配置不会导致之所以出现问题,是因为部署在服务器上的所有耳朵都共享同一个类加载器(“统一的类加载器”)。 What I suspect is that the server you are deploying to has the configuration differently, so I'm not quote sure how to interact with it, but what you have to do is add a file called jboss-app.xml in your ear (in the same location as the application.xml) that looks something like this: 我怀疑您要部署到的服务器的配置有所不同,因此我没有提供确定如何与之交互的信息,但是您要做的是在您的耳朵中添加一个名为jboss-app.xml的文件(在与application.xml相同的位置)看起来像这样:

 <?xml version="1.0"?>
 <!DOCTYPE jboss-app PUBLIC "-//JBoss//DTD J2EE Application 4.2//EN"
        "http://www.jboss.org/j2ee/dtd/jboss-app_4_2.dtd">
 <jboss-app>
      <loader-repository>
      com.yourcomany:archive=yourear
      </loader-repository>
 </jboss-app>

That is for JBoss 4.2. 那是针对JBoss 4.2的。 5.1 has the same type of tag, here is the xsd . 5.1具有相同类型的标记,这是xsd It has the same loader-repository concept. 它具有相同的加载器存储库概念。

That should be it. 应该是这样。 That is, as long as your ejb-jar, war, etc. don't have it, then they don't need it. 也就是说,只要您的ejb-jar,war等没有它,那么他们就不需要它。 However, your wars (in jboss-web.xml - same location as the web.xml) may need the same thing. 但是,您的战争(在jboss-web.xml中-与web.xml相同的位置)可能需要相同的东西。 In this case as long as you name the repository exactly the same way (if I understand correctly - never tried it myself) they will share the same classloader. 在这种情况下,只要您以完全相同的方式命名存储库(如果我理解正确-从未尝试过),它们将共享相同的类加载器。 The same goes for the EJB as configured in the jboss.xml that goes in the same location as the ejb.xml. EJB与在jboss.xml中配置的(与ejb.xml位于相同位置)相同。

This might make it a bit clearer. 可能会使它更加清晰。

I'd configure a separate object pool on your app server so it only contained the single instance. 我会在您的应用服务器上配置一个单独的对象池,以便它仅包含单个实例。

Why you would want to do this is the real question. 为什么要这样做是一个真正的问题。 Sounds like all your apps will be coupled this way. 听起来您所有的应用程序都将以这种方式耦合。 And Google is eradicating singleton from its apps. 谷歌正在从其应用程序中消除单例。 Why are you seeing fit to bring it back? 您为什么认为适合将其带回来?

You can use a MBean and bind it to JNDI, then retrieve it wherever you want to use it. 您可以使用MBean并将其绑定到JNDI,然后在想要使用它的任何地方检索它。

The MBean might be deployed in a .sar file MBean可能部署在.sar文件中

如果使用的是Java EE 6,则它支持单例EJB。

If practical, simply take the class that has the singleton, put it in a JAR, take the JAR OUT of the the EAR, and add the JAR to the JBoss classloader (via the system classpath, or a some lib directory). 如果可行,只需将具有单例的类放入JAR中,从EAR中取出JAR,然后将JAR添加到JBoss类加载器中(通过系统类路径或某个lib目录)。 This puts the class in a single classloader shared by both WARs. 这会将类放在两个WAR共享的单个类加载器中。

The singleton will not be able to "see" anything in your applications WARs etc, as they're in a lower classloader. 单例将无法“看到”您的应用程序WAR等中的任何内容,因为它们位于较低的类加载器中。

However, there's nothing stopping you from injecting into the singleton (at server startup) a factory class, that originates from the WARs et al, and THAT class has access to all of the apps classes. 但是,没有什么能阻止您(在服务器启动时)将单个工厂类注入到WARs等人的工厂类中,并且THAT类可以访问所有应用程序类。 That makes the singleton more of a simple container. 这使单例更像一个简单的容器。

But this is straightforward to do. 但这很简单。

Also, if you do this, make sure when you shut down you application, that any instances held by this singleton are freed. 另外,如果执行此操作,请确保在关闭应用程序时释放此单例所拥有的所有实例。 Any class reference by the singleton will not be GC'd when you undeploy the application. 取消部署应用程序时,不会对单例的任何类引用进行GC。 So, if you have a reference to your app stored in the singleton, then the server holds a reference to the singletons classloader, that classloader holds a reference to your singleton class, which holds reference to you apps class, which holds a reference to the apps CLASSLOADER, and THAT holds a reference to all of the classes in your app. 因此,如果您在单例中存储了对应用程序的引用,则服务器将保留对单例类加载器的引用,该类加载器将包含对您的单例类的引用,该引用包含对您的apps类的引用,对应用程序类的引用也包含对单例类的引用。 apps CLASSLOADER,其中包含对应用程序中所有类的引用。 Not a nice mess to leave behind. 留下来的不是一团糟。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM