- 該類所有的實例都已經被GC,也就是JVM中不存在該Class的任何實例。
- 加載該類的ClassLoader已經被GC。
- 該類的java.lang.Class 對象沒有在任何地方被引用,如不能在任何地方通過反射訪問該類的方法
jsp->servlet 在web容器中,你的servlet是單例的,也是無狀態的,線程安全的。也就是隻有一個對象,
jsp改變以後,web容器只要把相應的servlet對象更新就好了。
而java呢?
可能這個類在你的應用中有n個實例,與這些實例單向,雙向關聯的又有n個實例。如果你修改了,這些jvm存在的老的實例對象怎麼辦????
java這類靜態語言無法實現象asp,php,jsp的效果的。
weblogic熱部署原理
Weblogic允許在wls運行時部署組件的新版本。這個過程被稱作熱部署。因爲java classloader沒有任何一種機制來卸下一系列存在的類,也不能用類的新版本來替換老版本,爲了在一個運行的虛擬機中更新相關的類,classloader必須被替換掉。當它被替換時,它所裝載的所有類以及衍生的子classloader也要被重新裝載。這些類的所有實例也必需被重新裝載。在wls中,每一個應用組件都有一個層次化的classloaders,它們都是system classloader的子類,這種結構有助於每個應用或應用的一部分能被單獨重新加載,而不會影響其它的組件。
-
Bootstrap ClassLoader/啓動類加載器
主要負責jdk_home/lib目錄下的核心 api 或 -Xbootclasspath 選項指定的jar包裝入工作。 -
Extension ClassLoader/擴展類加載器
主要負責jdk_home/lib/ext目錄下的jar包或 -Djava.ext.dirs 指定目錄下的jar包裝入工作。 -
System ClassLoader/系統類加載器
主要負責java -classpath/-Djava.class.path所指的目錄下的類與jar包裝入工作。 -
User Custom ClassLoader/用戶自定義類加載器(java.lang.ClassLoader的子類)
在程序運行期間, 通過java.lang.ClassLoader的子類動態加載class文件, 體現java動態實時類裝入特性。
- 每個ClassLoader都維護了一份自己的名稱空間, 同一個名稱空間裏不能出現兩個同名的類。
- 爲了實現java安全沙箱模型頂層的類加載器安全機制, java默認採用了 " 雙親委派的加載鏈 " 結構。
Class c = findLoadedClass(name);
if (c == null ) {
// 指定類未被裝載過
try {
if (parent != null ) {
// 如果父類加載器不爲空, 則委派給父類加載
c = parent.loadClass(name, false );
} else {
// 如果父類加載器爲空, 則委派給啓動類加載加載
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// 啓動類加載器或父類加載器拋出異常後, 當前類加載器將其
// 捕獲, 並通過findClass方法, 由自身加載
c = findClass(name);
}
}
java默認的線程上下文類加載器是 系統類加載器(AppClassLoader)。
try {
loader = AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
throw new InternalError(
"Could not create application class loader" );
}
// Also set the context class loader for the primordial thread.
Thread.currentThread().setContextClassLoader(loader);
典型的例子有, 通過線程上下文來加載第三方庫jndi實現, 而不依賴於雙親委派.
大部分java app服務器(jboss, tomcat..)也是採用contextClassLoader來處理web服務。
還有一些採用 hotswap 特性的框架, 也使用了線程上下文類加載器, 比如 seasar (full stack framework in japenese).
使java類加載體系顯得更靈活.
在編寫基礎設施時, 通過使用線程上下文來加載類, 應該是一個很好的選擇。
防止因爲不同的類加載器, 導致類型轉換異常(ClassCastException)。
三.命名空間及其作用
每個類裝載器有自己的命名空間,命名空間由所有以此裝載器爲創始類裝載器的類組成。不同命名空間的兩個類是不可見的,但只要得到類所對應的Class對象的reference,還是可以訪問另一命名空間的類。
例2演示了一個命名空間的類如何使用另一命名空間的類。在例子中,LoaderSample2由系統類裝載器裝載,LoaderSample3由自定義的裝載器loader負責裝載,兩個類不在同一命名空間,但LoaderSample2得到了LoaderSample3所對應的Class對象的reference,所以它可以訪問LoaderSampl3中公共的成員(如age)。
例2不同命名空間的類的訪問
/*LoaderSample2.java*/
import java.lang.reflect. * ;
public class LoaderSample2 {
public static void main(String[] args) {
try {
String path = System.getProperty( " user.dir " );
URL[] us = { new URL( " file:// " + path + " /sub/ " )};
ClassLoader loader = new URLClassLoader(us);
Class c = loader.loadClass( " LoaderSample3 " );
Object o = c.newInstance();
Field f = c.getField( " age " );
int age = f.getInt(o);
System.out.println( " age is " + age);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*sub/Loadersample3.java*/
static {
System.out.println( " LoaderSample3 loaded " );
}
public int age = 30 ;
}
運行:java LoaderSample2
LoaderSample3 loaded
age is 30
從運行結果中可以看出,在類LoaderSample2中可以創建處於另一命名空間的類LoaderSample3中的對象並可以訪問其公共成員age。
說明:如果LoaderSample3在classpath下能夠找到,則由URLClassLoader的parent loader AppClassLoader來加載,如果不在classpath下
運行時包(runtime package)
由同一類裝載器定義裝載的屬於相同包的類組成了運行時包,決定兩個類是不是屬於同一個運行時包,不僅要看它們的包名是否相同,還要看的定義類裝載器是否相同。只有屬於同一運行時包的類才能互相訪問包可見的類和成員。這樣的限制避免了用戶自己的代碼冒充核心類庫的類訪問核心類庫包可見成員的情況。假設用戶自己定義了一個類java.lang.Yes,並用用戶自定義的類裝載器裝載,由於java.lang.Yes和核心類庫java.lang.*由不同的裝載器裝載,它們屬於不同的運行時包,所以java.lang.Yes不能訪問核心類庫java.lang中類的包可見的成員。
總結
命名空間並沒有完全禁止屬於不同空間的類的互相訪問,雙親委託模型加強了Java的安全,運行時包增加了對包可見成員的保護。
二. 擴展ClassLoader方法
我們目的是從本地文件系統使用我們實現的類裝載器裝載一個類。爲了創建自己的類裝載器我們應該擴展ClassLoader類,這是一個抽象類。我們創建一個FileClassLoader extends ClassLoader。我們需要覆蓋ClassLoader中的findClass(String name)方法,這個方法通過類的名字而得到一個Class對象。
{
byte [] data = loadClassData(name);
return defineClass(name, data, 0 , data.length);
}
我們還應該提供一個方法loadClassData(String name),通過類的名稱返回class文件的字
節數組。然後使用ClassLoader提供的defineClass()方法我們就可以返回Class對象了。
{
FileInputStream fis = null ;
byte [] data = null ;
try
{
fis = new FileInputStream( new File(drive + name + fileType));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int ch = 0 ;
while ((ch = fis.read()) != - 1 )
{
baos.write(ch);
}
data = baos.toByteArray();
} catch (IOException e)
{
e.printStackTrace();
}
return data;
}
自定義ClassLoader實現java 熱替換:http://www.ibm.com/developerworks/cn/java/j-lo-hotswapcls/