Singleton class with several different classloaders
If you want a true Singleton
across classloaders, then you need a common parent to load the class in question, or you need to specify the classloader yourself.
Update: From the comment from @Pshemo below a fair bit of the content in the blog below might come directly from a JavaWorld Article. I've left the blog entry in as it may still help someone, but its worth knowing where the content originally came from.
Original:
There is a blog entry that gives you a way to do this" (although I havent tried it!), and it looks fairly reasonable
As requested below here a code snippet from my link above - I do suggest you visit the blog though for the full context:
private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
ClassLoader in Singleton Pattern
Multiple ClassLoaders are commonly encountered in Java EE. The Java EE application servers loads classes from deployed War/EAR by a tree of classloaders. The reason why they do it in this way is to isolate one application from other applications but still sharing classes between deployed modules. If you want your class to be truly singleton then you need to make sure that same classloader loads your singleton. You can achieve it like this
private static Class getClass(String clazz) throws ClassNotFoundException {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if(loader == null)
loader = YourSingleton.class.getClassLoader();
return (loader.loadClass(clazz));
}
}
Note Enum in java already implement singleton pattern.
Updated Can you please explain what is multiple class Loader and how it resolve the issue.
Let say you have a library Foo
. Some part of your project requires Foo_version1.jar
and some other part requires Foo_version2.jar
. So in your classpath
you have Foo_version1.jar
and Foo_version2.jar
. Now the class loader need to load a class Bar
from Foo
, it will load it from first Foo_versionX
that it finds on the classpath
. In order to resolve this issue you need another class loader because remember that some part of your project require Bar
class from different jar
then the one loaded by classloader.
By using the code mentioned above you are making sure that if more than 1 classloader try to load your class then always the same instance will be used.
Impact of changes on singleton class loaded by different classloaders
Singleton is per classloader. so changes wont impact other instances loaded using other classloaders.
there are couple of good references:
- on Singleton and different classloaders
- on Static members and different classloaders
Isolating a static singleton class using class loaders
Does plain simple reflection work at specific points in time, where you want the output of getValue
to change?
Field f = LegacySingleton.class.getDeclaredField("value");
f.setAccessible(true);
f.set(null, true|false);
If not, For the Classloader approach, you can follow a Plugin architecture. But as others have noted, this may boil down to the whole dependencies being loaded on 2 different Classloader hierarchies. Also, you may face LinkageError
issues, depending on how the dependencies work in your codebase.
Inspired by this post:
- Isolate the codebase, primarily the jar having
LegacyRequestHandler
class and do not include in the application/main classpath. - Have a custom classloader that looks inwards first and then checks parent. A complete example of such a classloader is here.
Have a wrapper invoker that will initialise the class loader with the jar path providing
LegacySingleton
class, e.g.new ParentLastURLClassLoader(Arrays.asList(new URL[] {new URL("path/to/jar")}));
Post that, you can load the singleton in it's class loader space and obtain copies.
//2 different classloaders
ClassLoader cl1 = new ParentLastURLClassLoader(urls);
ClassLoader cl2 = new ParentLastURLClassLoader(urls);
//LegacySingleton with value = true in Classloader space of cl1
cl1.loadClass("LegacySingleton").getMethod("setup", boolean.class).invoke(null, true);
//LegacySingleton with value = false in Classloader space of cl1
cl2.loadClass("LegacySingleton").getMethod("setup", boolean.class).invoke(null, false);
- Next, you may obtain the driver class for your legacy code using reflection(through
cl1/2
) and trigger execution.
NOTE that you shouldn't refer to the classes in the Legacy code directly in the main class, as then they will be loaded using Java primordial/application class loader.
Related Topics
Can Not Find the Tag Library Descriptor for "Http://Java.Sun.Com/Jsp/Jstl/Core"
How Does Facebook Add Badge Numbers on App Icon in Android
Android Studio Error: "Manifest Merger Failed: Apps Targeting Android 12"
Android Post Picture to Facebook Wall
Creating Hashmap from a JSON String
Classnotfoundexception: Didn't Find Class "Com.Google.Android.Gms.Ads.Adview"
How to Set Seekbar Min and Max Value
How to Create Your Own Library for Android Development to Be Used in Every Program You Write
Java Output Formatting for Strings
Math.Random, Only Generating a 0
Java Equivalent to PHP's Hmac-Sha1
Rxjava Android How to Use the Zip Operator
How to Know Whether I am in a Call on Android
How to Use Interceptor to Add Headers in Retrofit 2.0
Android: Textview Automatically Truncate and Replace Last 3 Char of String
Android Unable to Instantiate Activity: Didn't Find Class on Path