1. 在看ehcache源码时,发现加载工具类ClassLoaderUtil有两个获取ClassLoaderde的方法,getStandardClassLoader和getFallbackClassLoader,一时间不明白为什么要这样做。且先看源码:
public static ClassLoader getStandardClassLoader()
{
return Thread.currentThread().getContextClassLoader();
}
public static ClassLoader getFallbackClassLoader()
{
return net/sf/ehcache/util/ClassLoaderUtil.getClassLoader();
}
创建类实例的方法createNewInstance:
public static Object createNewInstance(String className)
throws CacheException
{
Class clazz;
try
{
clazz = Class.forName(className, true, getStandardClassLoader());
}
catch(ClassNotFoundException e)
{
try
{
clazz = Class.forName(className, true, getFallbackClassLoader());
}
catch(ClassNotFoundException ex)
{
throw new CacheException((new StringBuilder()).append("Unable to load class ").append(className).append(". Initial cause was ").append(e.getMessage()).toString(), e);
}
}
Object newInstance;
try
{
newInstance = clazz.newInstance();
}
catch(IllegalAccessException e)
{
throw new CacheException((new StringBuilder()).append("Unable to load class ").append(className).append(". Initial cause was ").append(e.getMessage()).toString(), e);
}
catch(InstantiationException e)
{
throw new CacheException((new StringBuilder()).append("Unable to load class ").append(className).append(". Initial cause was ").append(e.getMessage()).toString(), e);
}
return newInstance;
}
可以看到,程序首先通过Class.forName(className, true, getStandardClassLoader())得到具有指定类名的class对象,如果抛出ClassNotFoundException异常,则再次用Class.forName(className, true, getFallbackClassLoader())初始化名为className的类,随后得到该对象的实例并返回。
在getStandardClassLoader中获得当前线程类加载器,而getFallbackClassLoader直接获得该类的加载器。
我们知道,java的类加载机制(jvm规范)是委托模型,简单的说,如果一个类加载器想要加载一个类,首先它会委托给它的parent去加载,如果它的所有parent都没有成功的加载那么它才会自己亲自来。如果你使用class.getClassLoader(),可能会导致和当前线程所运行的类加载器不一致 :Class.getClassLoader() returns the class loader for the class. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader.
所以首先用当前线程的类加载器加载类,如果抛出异常再使用类自身的加载器是比较安全合理的做法。
2. 在cacheManager进行初始化的时候,有一句代码addShutdownHookIfRequired();即添加关闭钩子。
什么是关闭钩子呢?为什么要添加这个?
关闭应用程序有很多种方式,其中最常见的是用户按照程序设定的步骤来关闭,这是最安全理想的关闭方式。除此之外,有些用户喜欢手动强制关闭应用,比如在windows任务管理器中直接结束该进程,那么对于程序来说这是不可控的。而如果我们需要在关闭程序时顺便清理临时文件,或者其他操作,那么就会出现问题。幸而java对此有较好的处理方法:添加关闭钩子!
使用java.lang.Runtime 类中的方法:addShutdownHook(Thread hook);//hook一个已初始化但尚未启动的 Thread 对象
关闭钩子,只是一个已初始化但尚未启动的线程。虚拟机开始启用其关闭序列时,它会以某种未指定的顺序启动所有已注册的关闭钩子,并让它们同时运行。运行完所有的钩子后,如果已启用退出终结,那么虚拟机接着会运行所有未调用的终结方法。最后,虚拟机会暂停
Java 虚拟机会为了响应以下两类事件而关闭:
a. 程序正常退出,这发生在最后的非守护线程退出时,或者在调用 exit(等同于 System.exit)方法时。或者,
b. 为响应用户中断而终止 虚拟机,如键入 ^C;或发生系统事件,比如用户注销或系统关闭。
回到源程序中,我们先看看这段代码:
private void addShutdownHookIfRequired()
{
String shutdownHookProperty = System.getProperty("net.sf.ehcache.enableShutdownHook");
boolean enabled = PropertyUtil.parseBoolean(shutdownHookProperty);
if(!enabled)
{
return;
} else
{
LOG.log(Level.INFO, "The CacheManager shutdown hook is enabled because net.sf.ehcache.enableShutdownHook is set to true.");
Thread localShutdownHook = new Thread() {
public void run()
{
synchronized(this)
{
if(status.equals(Status.STATUS_ALIVE))
{
shutdownHook = null;
if(CacheManager.LOG.isLoggable(Level.INFO))
CacheManager.LOG.log(Level.INFO, "VM shutting down with the CacheManager still active. Calling shutdown.");
shutdown();
}
}
}
final CacheManager this$0;
{
this$0 = CacheManager.this;
super();
}
}
;
Runtime.getRuntime().addShutdownHook(localShutdownHook);
shutdownHook = localShutdownHook;
return;
}
}
如果系统属性中有设置net.sf.ehcache.enableShutdownHook = true,那么就会注册关闭钩子来正常关闭cacheManager。
创建钩子的步骤:首先创建一个关闭manager的线程,然后Runtime.getRuntime().addShutdownHook(localShutdownHook);注册关闭钩子!
一旦满足触发的条件,虚拟机就会自动执行相关关闭代码。