简体   繁体   English

使用类加载器破解单例-Java Web应用中的Multiton

[英]Hacking a singleton with classloaders - Multiton in a Java web app

I am creating a web front end using an existing back end containing several singleton classes. 我正在使用包含几个单例类的现有后端创建Web前端。 The DataStore is initialized by passing a user object into it, which is fine in a desktop application environment where the app is launched once on each machine, but will not work in a server side application designed to cater for multiple users. 通过将用户对象传递给DataStore来初始化DataStore,这在桌面应用程序环境中很好,在桌面环境中,该应用程序在每台计算机上启动一次,但是在旨在满足多个用户的服务器端应用程序中无法使用。

The database guys are reluctant to change the service layer and remove these singletons to allow an instance per user, or allow a single instance of a service layer object to serve multiple users. 数据库专家不愿更改服务层并删除这些单例以允许每个用户一个实例,或允许一个服务层对象的单个实例为多个用户提供服务。 This is with good reason, the desktop app has been in use for 10 years and changes could have serious side effects for the desktop app. 这是有充分的理由的,桌面应用程序已经使用了10年,更改可能会对桌面应用程序产生严重的副作用。

I have been asked to investigate using classloaders to create multiple instances of the singletons. 我被要求调查使用类加载器来创建单例的多个实例。 I am not comfortable with this idea at all, hacking singletons seems like bad practice, but changing the service layer could take months of work. 我对这个想法一点都不满意,黑客单身似乎是一种不好的做法,但是更改服务层可能需要花费数月的时间。

I have tested this out already by putting two identical WAR files of my app (with different file names) into Tomcat. 我已经通过将我的应用程序的两个相同的WAR文件(具有不同的文件名)放入Tomcat中进行了测试。 Tomcat creates a classloader for each webapp and they worked just fine separately. Tomcat为每个Web应用程序都创建了一个类加载器,它们分别工作正常。 I only encountered problems when the singletons used System.setProperty/System.getProperty, which is to be expected as the System class comes from a classloader much higher up in the tree. 我仅在单例使用System.setProperty / System.getProperty时遇到问题,这是可以预期的,因为System类来自树中更高的类加载器。

To get this separation within a single webapp, it starts to get a bit complicated. 为了在单个web应用程序中实现这种分离,它开始变得有点复杂。 It seems I would have to create a different classloader for each session, and use the classloader to load either all the classes in the whole service layer or just the ones which are singletons and their dependencies. 似乎我必须为每个会话创建一个不同的类加载器,并使用该类加载器加载整个服务层中的所有类或仅加载单例及其依赖项的类。

The problem comes when I'm thinking about how to use these objects in a session in a servlet. 当我正在考虑如何在Servlet的会话中使用这些对象时,就会出现问题。 Because each Servlet has a single instance within a Tomcat, getting objects from the session and casting them will not be straightforward. 因为每个Servlet在Tomcat中都有一个实例,所以从会话中获取对象并强制转换对象并不是一件容易的事。 Eg, to get a DataStore object from the session, I would have to cast it to the correct DataStore class loaded by the correct ClassLoader, since a single class loaded by two different ClassLoaders counts as two completely separate classes. 例如,要从会话中获取DataStore对象,我将必须将其强制转换为由正确的ClassLoader加载的正确的DataStore类,因为由两个不同的ClassLoader加载的单个类算作两个完全独立的类。

I have read that using ClassLoaders can cause all sorts of problems with memory leaks if they are not used carefully, and from the sounds of this, if I have 500 users, that is a lot of classloaders and classes loaded by classloaders. 我已经读到,如果不小心使用ClassLoader,可能会导致各种各样的内存泄漏问题。从声音上看,如果我有500个用户,则是很多类加载器和类加载器加载的类。 Won't I then have also have issues with PermGen? 然后,我会不会对PermGen遇到问题?

I suppose from this large explanation, I really have 4 questions: 我想从这个大的解释中,我确实有四个问题:

  1. Hacking a singleton with classloaders in a webapp. 在Web应用程序中使用类加载器入侵单例。 Creating potentially hundreds of instances of the same classes designed to be singletons. 创建潜在的数百个相同类的实例,这些实例被设计为单例。 Is that a terrible idea? 那是一个可怕的主意吗? So terrible I shouldn't contemplate it? 太可怕了,我不应该考虑吗?
  2. What is the best way to implement this if I absolutely have to? 如果我绝对必须这样做,最好的方法是什么?
  3. How do I cater for casting in a Servlet, if I want to get and set objects into a session? 如果要获取对象并将其设置为会话,如何满足Servlet的转换要求?
  4. Will I end up with issues with memory leaks, and PermGen space? 我最终会遇到内存泄漏和PermGen空间的问题吗?

I would really appreciate any suggestions. 我真的很感谢任何建议。 Thanks :) 谢谢 :)

  1. It's not ideal, but I don't think it's terrible. 这不是理想的,但我认为这并不可怕。 In practice, it's not much worse than loading multiple versions of the same class (even without singletons), and that's becoming increasingly common in complex application server environments, particularly those with OSGi. 实际上,它并没有加载相同类的多个版本(甚至没有单例)一样糟糕,并且在复杂的应用程序服务器环境中,尤其是在具有OSGi的环境中,这种情况变得越来越普遍。

  2. It's hard to say which approach is best, but to begin with, I would start by creating child class loaders of your web application class loader. 很难说哪种方法最好,但是首先,我将从创建Web应用程序类加载器的子类加载器开始。 Arrange to load implementation classes in the child class loaders (ie, the ones with the singleton), and it possible, have the implementation class implement an interface that is loaded from the web application class loader. 安排将实现类加载到子类加载器(即具有单例的加载器)中,并可能使实现类实现从Web应用程序类加载器加载的接口。

  3. By loading the interface from the web application class loader. 通过从Web应用程序类加载器加载接口。 This is basically the same approach that the application server itself is using to invoke your HttpServlet: the interface is loaded by the server, so it can refer to it directly, but the implementation is in a child class loader for your application. 这基本上与应用程序服务器本身用来调用HttpServlet的方法相同:接口由服务器加载,因此它可以直接引用它,但是实现是在应用程序的子类加载器中进行的。 You're just creating a secondary layer of interface/impl split for your own convenience. 您只是为了自己的方便而创建了第二层接口/ Impl拆分。

  4. You'll end up with memory leaks if you store references to the child class loader (or its loaded classes, or instantiated objects from those classes) in a "parent" class loader (eg, if the singleton registers an MBean, which causes it to get referenced in a JVM-wide object), but that's no different from if you weren't creating child class loaders. 如果将对子类加载器(或其加载的类,或这些类中的实例化对象)的引用存储在“父”类加载器中(例如,如果单例注册了一个MBean,则导致其泄漏),最终将导致内存泄漏。以在JVM范围的对象中被引用),但这与您没有创建子类加载器的情况没有什么不同。 If you're dynamically creating/destroying these singletons (thus child class loaders), you'll have to take care that you don't retain references to those child class loaders (or classes/objects) longer than necessary. 如果要动态创建/销毁这些单例(因此是子类加载器),则必须注意不要将对这些子类加载器(或类/对象)的引用保留的时间不要超过必需的时间。 PermGen is probably more problematic. PermGen可能存在更多问题。 If you can run with Java 8, that's gone away; 如果您可以使用Java 8来运行,那就已经不复存在了。 otherwise, you might have to increase the default PermGen size depending on how many of these class loaders/singletons you need to create. 否则,您可能必须增加默认的PermGen大小,具体取决于您需要创建多少个此类装入器/单个装入器。

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

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