1、 java命令和 javaw命令是怎么回事?
我現(xiàn)在的理解:
java命令在執(zhí)行一個class文件的時候,
(1)首先要創(chuàng)建一個虛擬機實例
(2)虛擬機啟動用戶主線程 main()方法,這是非守護線程
(3)虛擬機(也可能是主線程)啟動守護線程。比如垃圾收集線程。
(4)main()方法結束,并且由main()方法創(chuàng)建的用戶線程也結束。也就是說系統(tǒng)中沒有用戶線程存在了,則守護線程也結束,最后虛擬機實例自動銷毀。
javaw命令在eclipse啟動后,也是代表了一個虛擬機實例。它一直存在應該是因為系統(tǒng)中有用戶線程一直在后臺運行。
當eclipse被關閉是,應該是調(diào)用了system的exist()方法,即虛擬機實例強行銷毀。
當用戶自己編寫的class文件在eclipse中執(zhí)行時,由javaw這個虛擬機實例解釋執(zhí)行。
2、 下面是網(wǎng)上資料總結如下:
Java有兩種Thread:“守護線程Daemon”與“用戶線程User”。
從字面上我們很容易將守護線程理解成是由虛擬機(virtual machine)在內(nèi)部創(chuàng)建的,而用戶線程則是自己所創(chuàng)建的。事實并不是這樣,任何線程都可以是“守護線程Daemon”或“用戶線程User”。他們在幾乎每個方面都是相同的,唯一的區(qū)別是判斷虛擬機何時離開:
用戶線程:Java虛擬機在它所有非守護線程已經(jīng)離開后自動離開。
守護線程:守護線程則是用來服務用戶線程的,如果沒有其他用戶線程在運行,那么就沒有可服務對象,也就沒有理由繼續(xù)下去。
setDaemon(boolean on)方法可以方便的設置線程的Daemon模式,true為Daemon模式,false為User模式。setDaemon(boolean on)方法必須在線程啟動之前調(diào)用,當線程正在運行時調(diào)用會產(chǎn)生異常。isDaemon方法將測試該線程是否為守護線程。值得一提的是,當你在一個守護線程中產(chǎn)生了其他線程,那么這些新產(chǎn)生的線程不用設置Daemon屬性,都將是守護線程,用戶線程同樣。
下面是演示程序:
----------------------------------------------------------------
import java.io.IOException;
/**
* 守護線程在沒有用戶線程可服務時自動離開
*/
public class TestMain4 extends Thread {
public TestMain4() {
}
public void run() {
for(int i = 1; i <= 50; i++){
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(i);
}
}
public static void main(String [] args){
TestMain4 test = new TestMain4();
test.setDaemon(false);
test.start();
System.out.println("isDaemon = " + test.isDaemon());
try {
System.in.read(); // 接受輸入,使程序在此停頓,一旦接收到用戶輸入,main線程結束,守護線程自動結束,如果test不是守護進程必須等到test運行完了以后才退出
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
----------------------------------------------------------------------------------
例:我們所熟悉的Java垃圾回收線程就是一個典型的守護線程,當我們的程序中不再有任何運行中的Thread,程序就不會再產(chǎn)生垃圾,垃圾回收器也就無事可做,所以當垃圾回收線程是Java虛擬機上僅剩的線程時,Java虛擬機會自動離開。
3、下面是一個論壇的帖子
http://topic.csdn.net/t/20060115/00/4517316.html
守護線程與普通線程的唯一區(qū)別是:當JVM中所有的線程都是守護線程的時候,JVM就可以退出了;如果還有一個或以上的非守護線程則不會退出。(以上是針對正常退出,調(diào)用System.exit則必定會退出)
所以setDeamon(true)的唯一意義就是告訴JVM不需要等待它退出,讓JVM喜歡什么退出就退出吧,不用管它。
posted @
2010-04-25 00:06 junly 閱讀(601) |
評論 (0) |
編輯 收藏
其他參考:
1 http://gzcj.javaeye.com/blog/394648
2 http://blog.sina.com.cn/s/blog_5f1fe33f0100d9ak.html
類加載器是 Java 語言流行的重要原因之一。它使得 Java 類可以被動態(tài)加載到 Java 虛擬機中并執(zhí)行。類加載器從 JDK 1.0 就出現(xiàn)了,最初是為了滿足 Java Applet 的需要而開發(fā)出來的。Java Applet 需要從遠程下載 Java 類文件到瀏覽器中并執(zhí)行。現(xiàn)在類加載器在 Web 容器和 OSGi 中得到了廣泛的使用。一般來說,Java 應用的開發(fā)人員不需要直接同類加載器進行交互。Java 虛擬機默認的行為就已經(jīng)足夠滿足大多數(shù)情況的需求了。不過如果遇到了需要與類加載器進行交互的情況,而對類加載器的機制又不是很了解的話,就很容易花大量的時間去調(diào)試
ClassNotFoundException
和
NoClassDefFoundError
等異常。本文將詳細介紹 Java 的類加載器,幫助讀者深刻理解 Java 語言中的這個重要概念。下面首先介紹一些相關的基本概念。
類加載器基本概念
顧名思義,類加載器(class loader)用來加載 Java 類到 Java 虛擬機中。一般來說,Java 虛擬機使用 Java 類的方式如下:Java 源程序(.java 文件)在經(jīng)過 Java 編譯器編譯之后就被轉(zhuǎn)換成 Java 字節(jié)代碼(.class 文件)。類加載器負責讀取 Java 字節(jié)代碼,并轉(zhuǎn)換成 java.lang.Class
類的一個實例。每個這樣的實例用來表示一個 Java 類。通過此實例的 newInstance()
方法就可以創(chuàng)建出該類的一個對象。實際的情況可能更加復雜,比如 Java 字節(jié)代碼可能是通過工具動態(tài)生成的,也可能是通過網(wǎng)絡下載的。
基本上所有的類加載器都是 java.lang.ClassLoader
類的一個實例。下面詳細介紹這個 Java 類。
java.lang.ClassLoader
類介紹
java.lang.ClassLoader
類的基本職責就是根據(jù)一個指定的類的名稱,找到或者生成其對應的字節(jié)代碼,然后從這些字節(jié)代碼中定義出一個 Java 類,即 java.lang.Class
類的一個實例。除此之外,ClassLoader
還負責加載 Java 應用所需的資源,如圖像文件和配置文件等。不過本文只討論其加載類的功能。為了完成加載類的這個職責,ClassLoader
提供了一系列的方法,比較重要的方法如 表 1 所示。關于這些方法的細節(jié)會在下面進行介紹。
表 1. ClassLoader 中與加載類相關的方法
方法 |
說明 |
getParent() |
返回該類加載器的父類加載器。 |
loadClass(String name) |
加載名稱為 name 的類,返回的結果是 java.lang.Class 類的實例。 |
findClass(String name) |
查找名稱為 name 的類,返回的結果是 java.lang.Class 類的實例。 |
findLoadedClass(String name) |
查找名稱為 name 的已經(jīng)被加載過的類,返回的結果是 java.lang.Class 類的實例。 |
defineClass(String name, byte[] b, int off, int len) |
把字節(jié)數(shù)組 b 中的內(nèi)容轉(zhuǎn)換成 Java 類,返回的結果是 java.lang.Class 類的實例。這個方法被聲明為 final 的。 |
resolveClass(Class<?> c) |
鏈接指定的 Java 類。 |
對于 表 1 中給出的方法,表示類名稱的 name
參數(shù)的值是類的二進制名稱。需要注意的是內(nèi)部類的表示,如 com.example.Sample$1
和 com.example.Sample$Inner
等表示方式。這些方法會在下面介紹類加載器的工作機制時,做進一步的說明。下面介紹類加載器的樹狀組織結構。
類加載器的樹狀組織結構
Java 中的類加載器大致可以分成兩類,一類是系統(tǒng)提供的,另外一類則是由 Java 應用開發(fā)人員編寫的。系統(tǒng)提供的類加載器主要有下面三個:
- 引導類加載器(bootstrap class loader):它用來加載 Java 的核心庫,是用原生代碼來實現(xiàn)的,并不繼承自
java.lang.ClassLoader
。
- 擴展類加載器(extensions class loader):它用來加載 Java 的擴展庫。Java 虛擬機的實現(xiàn)會提供一個擴展庫目錄。該類加載器在此目錄里面查找并加載 Java 類。
- 系統(tǒng)類加載器(system class loader):它根據(jù) Java 應用的類路徑(CLASSPATH)來加載 Java 類。一般來說,Java 應用的類都是由它來完成加載的??梢酝ㄟ^
ClassLoader.getSystemClassLoader()
來獲取它。
除了系統(tǒng)提供的類加載器以外,開發(fā)人員可以通過繼承 java.lang.ClassLoader
類的方式實現(xiàn)自己的類加載器,以滿足一些特殊的需求。
除了引導類加載器之外,所有的類加載器都有一個父類加載器。通過 表 1 中給出的 getParent()
方法可以得到。對于系統(tǒng)提供的類加載器來說,系統(tǒng)類加載器的父類加載器是擴展類加載器,而擴展類加載器的父類加載器是引導類加載器;對于開發(fā)人員編寫的類加載器來說,其父類加載器是加載此類加載器 Java 類的類加載器。因為類加載器 Java 類如同其它的 Java 類一樣,也是要由類加載器來加載的。一般來說,開發(fā)人員編寫的類加載器的父類加載器是系統(tǒng)類加載器。類加載器通過這種方式組織起來,形成樹狀結構。樹的根節(jié)點就是引導類加載器。圖 1 中給出了一個典型的類加載器樹狀組織結構示意圖,其中的箭頭指向的是父類加載器。
圖 1. 類加載器樹狀組織結構示意圖
代碼清單 1 演示了類加載器的樹狀組織結構。
清單 1. 演示類加載器的樹狀組織結構
public class ClassLoaderTree {
public static void main(String[] args) {
ClassLoader loader = ClassLoaderTree.class.getClassLoader();
while (loader != null) {
System.out.println(loader.toString());
loader = loader.getParent();
}
}
}
|
每個 Java 類都維護著一個指向定義它的類加載器的引用,通過 getClassLoader()
方法就可以獲取到此引用。代碼清單 1 中通過遞歸調(diào)用 getParent()
方法來輸出全部的父類加載器。代碼清單 1 的運行結果如 代碼清單 2 所示。
清單 2. 演示類加載器的樹狀組織結構的運行結果
sun.misc.Launcher$AppClassLoader@9304b1
sun.misc.Launcher$ExtClassLoader@190d11
|
如 代碼清單 2 所示,第一個輸出的是 ClassLoaderTree
類的類加載器,即系統(tǒng)類加載器。它是 sun.misc.Launcher$AppClassLoader
類的實例;第二個輸出的是擴展類加載器,是 sun.misc.Launcher$ExtClassLoader
類的實例。需要注意的是這里并沒有輸出引導類加載器,這是由于有些 JDK 的實現(xiàn)對于父類加載器是引導類加載器的情況,getParent()
方法返回 null
。
在了解了類加載器的樹狀組織結構之后,下面介紹類加載器的代理模式。
類加載器的代理模式
類加載器在嘗試自己去查找某個類的字節(jié)代碼并定義它時,會先代理給其父類加載器,由父類加載器先去嘗試加載這個類,依次類推。在介紹代理模式背后的動機之前,首先需要說明一下 Java 虛擬機是如何判定兩個 Java 類是相同的。Java 虛擬機不僅要看類的全名是否相同,還要看加載此類的類加載器是否一樣。只有兩者都相同的情況,才認為兩個類是相同的。即便是同樣的字節(jié)代碼,被不同的類加載器加載之后所得到的類,也是不同的。比如一個 Java 類 com.example.Sample
,編譯之后生成了字節(jié)代碼文件 Sample.class
。兩個不同的類加載器 ClassLoaderA
和 ClassLoaderB
分別讀取了這個 Sample.class
文件,并定義出兩個 java.lang.Class
類的實例來表示這個類。這兩個實例是不相同的。對于 Java 虛擬機來說,它們是不同的類。試圖對這兩個類的對象進行相互賦值,會拋出運行時異常 ClassCastException
。下面通過示例來具體說明。代碼清單 3 中給出了 Java 類 com.example.Sample
。
清單 3. com.example.Sample 類
package com.example;
public class Sample {
private Sample instance;
public void setSample(Object instance) {
this.instance = (Sample) instance;
}
}
|
如 代碼清單 3 所示,com.example.Sample
類的方法 setSample
接受一個 java.lang.Object
類型的參數(shù),并且會把該參數(shù)強制轉(zhuǎn)換成 com.example.Sample
類型。測試 Java 類是否相同的代碼如 代碼清單 4 所示。
清單 4. 測試 Java 類是否相同
public void testClassIdentity() {
String classDataRootPath = "C:\\workspace\\Classloader\\classData";
FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath);
FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath);
String className = "com.example.Sample";
try {
Class<?> class1 = fscl1.loadClass(className);
Object obj1 = class1.newInstance();
Class<?> class2 = fscl2.loadClass(className);
Object obj2 = class2.newInstance();
Method setSampleMethod = class1.getMethod("setSample", java.lang.Object.class);
setSampleMethod.invoke(obj1, obj2);
} catch (Exception e) {
e.printStackTrace();
}
}
|
代碼清單 4 中使用了類 FileSystemClassLoader
的兩個不同實例來分別加載類 com.example.Sample
,得到了兩個不同的 java.lang.Class
的實例,接著通過 newInstance()
方法分別生成了兩個類的對象 obj1
和 obj2
,最后通過 Java 的反射 API 在對象 obj1
上調(diào)用方法 setSample
,試圖把對象 obj2
賦值給 obj1
內(nèi)部的 instance
對象。代碼清單 4 的運行結果如 代碼清單 5 所示。
清單 5. 測試 Java 類是否相同的運行結果
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at classloader.ClassIdentity.testClassIdentity(ClassIdentity.java:26)
at classloader.ClassIdentity.main(ClassIdentity.java:9)
Caused by: java.lang.ClassCastException: com.example.Sample
cannot be cast to com.example.Sample
at com.example.Sample.setSample(Sample.java:7)
... 6 more
|
從 代碼清單 5 給出的運行結果可以看到,運行時拋出了 java.lang.ClassCastException
異常。雖然兩個對象 obj1
和 obj2
的類的名字相同,但是這兩個類是由不同的類加載器實例來加載的,因此不被 Java 虛擬機認為是相同的。
了解了這一點之后,就可以理解代理模式的設計動機了。代理模式是為了保證 Java 核心庫的類型安全。所有 Java 應用都至少需要引用 java.lang.Object
類,也就是說在運行的時候,java.lang.Object
這個類需要被加載到 Java 虛擬機中。如果這個加載過程由 Java 應用自己的類加載器來完成的話,很可能就存在多個版本的 java.lang.Object
類,而且這些類之間是不兼容的。通過代理模式,對于 Java 核心庫的類的加載工作由引導類加載器來統(tǒng)一完成,保證了 Java 應用所使用的都是同一個版本的 Java 核心庫的類,是互相兼容的。
不同的類加載器為相同名稱的類創(chuàng)建了額外的名稱空間。相同名稱的類可以并存在 Java 虛擬機中,只需要用不同的類加載器來加載它們即可。不同類加載器加載的類之間是不兼容的,這就相當于在 Java 虛擬機內(nèi)部創(chuàng)建了一個個相互隔離的 Java 類空間。這種技術在許多框架中都被用到,后面會詳細介紹。
下面具體介紹類加載器加載類的詳細過程。
加載類的過程
在前面介紹類加載器的代理模式的時候,提到過類加載器會首先代理給其它類加載器來嘗試加載某個類。這就意味著真正完成類的加載工作的類加載器和啟動這個加載過程的類加載器,有可能不是同一個。真正完成類的加載工作是通過調(diào)用 defineClass
來實現(xiàn)的;而啟動類的加載過程是通過調(diào)用 loadClass
來實現(xiàn)的。前者稱為一個類的定義加載器(defining loader),后者稱為初始加載器(initiating loader)。在 Java 虛擬機判斷兩個類是否相同的時候,使用的是類的定義加載器。也就是說,哪個類加載器啟動類的加載過程并不重要,重要的是最終定義這個類的加載器。兩種類加載器的關聯(lián)之處在于:一個類的定義加載器是它引用的其它類的初始加載器。如類 com.example.Outer
引用了類 com.example.Inner
,則由類 com.example.Outer
的定義加載器負責啟動類 com.example.Inner
的加載過程。
方法 loadClass()
拋出的是 java.lang.ClassNotFoundException
異常;方法 defineClass()
拋出的是 java.lang.NoClassDefFoundError
異常。
類加載器在成功加載某個類之后,會把得到的 java.lang.Class
類的實例緩存起來。下次再請求加載該類的時候,類加載器會直接使用緩存的類的實例,而不會嘗試再次加載。也就是說,對于一個類加載器實例來說,相同全名的類只加載一次,即 loadClass
方法不會被重復調(diào)用。
下面討論另外一種類加載器:線程上下文類加載器。
線程上下文類加載器
線程上下文類加載器(context class loader)是從 JDK 1.2 開始引入的。類 java.lang.Thread
中的方法 getContextClassLoader()
和 setContextClassLoader(ClassLoader cl)
用來獲取和設置線程的上下文類加載器。如果沒有通過 setContextClassLoader(ClassLoader cl)
方法進行設置的話,線程將繼承其父線程的上下文類加載器。Java 應用運行的初始線程的上下文類加載器是系統(tǒng)類加載器。在線程中運行的代碼可以通過此類加載器來加載類和資源。
前面提到的類加載器的代理模式并不能解決 Java 應用開發(fā)中會遇到的類加載器的全部問題。Java 提供了很多服務提供者接口(Service Provider Interface,SPI),允許第三方為這些接口提供實現(xiàn)。常見的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。這些 SPI 的接口由 Java 核心庫來提供,如 JAXP 的 SPI 接口定義包含在 javax.xml.parsers
包中。這些 SPI 的實現(xiàn)代碼很可能是作為 Java 應用所依賴的 jar 包被包含進來,可以通過類路徑(CLASSPATH)來找到,如實現(xiàn)了 JAXP SPI 的 Apache Xerces 所包含的 jar 包。SPI 接口中的代碼經(jīng)常需要加載具體的實現(xiàn)類。如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory
類中的 newInstance()
方法用來生成一個新的 DocumentBuilderFactory
的實例。這里的實例的真正的類是繼承自 javax.xml.parsers.DocumentBuilderFactory
,由 SPI 的實現(xiàn)所提供的。如在 Apache Xerces 中,實現(xiàn)的類是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
。而問題在于,SPI 的接口是 Java 核心庫的一部分,是由引導類加載器來加載的;SPI 實現(xiàn)的 Java 類一般是由系統(tǒng)類加載器來加載的。引導類加載器是無法找到 SPI 的實現(xiàn)類的,因為它只加載 Java 的核心庫。它也不能代理給系統(tǒng)類加載器,因為它是系統(tǒng)類加載器的祖先類加載器。也就是說,類加載器的代理模式無法解決這個問題。
線程上下文類加載器正好解決了這個問題。如果不做任何的設置,Java 應用的線程的上下文類加載器默認就是系統(tǒng)上下文類加載器。在 SPI 接口的代碼中使用線程上下文類加載器,就可以成功的加載到 SPI 實現(xiàn)的類。線程上下文類加載器在很多 SPI 的實現(xiàn)中都會用到。
下面介紹另外一種加載類的方法:Class.forName
。
Class.forName
Class.forName
是一個靜態(tài)方法,同樣可以用來加載類。該方法有兩種形式:Class.forName(String name, boolean initialize, ClassLoader loader)
和 Class.forName(String className)
。第一種形式的參數(shù) name
表示的是類的全名;initialize
表示是否初始化類;loader
表示加載時使用的類加載器。第二種形式則相當于設置了參數(shù) initialize
的值為 true
,loader
的值為當前類的類加載器。Class.forName
的一個很常見的用法是在加載數(shù)據(jù)庫驅(qū)動的時候。如 Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance()
用來加載 Apache Derby 數(shù)據(jù)庫的驅(qū)動。
在介紹完類加載器相關的基本概念之后,下面介紹如何開發(fā)自己的類加載器。
回頁首
開發(fā)自己的類加載器
雖然在絕大多數(shù)情況下,系統(tǒng)默認提供的類加載器實現(xiàn)已經(jīng)可以滿足需求。但是在某些情況下,您還是需要為應用開發(fā)出自己的類加載器。比如您的應用通過網(wǎng)絡來傳輸 Java 類的字節(jié)代碼,為了保證安全性,這些字節(jié)代碼經(jīng)過了加密處理。這個時候您就需要自己的類加載器來從某個網(wǎng)絡地址上讀取加密后的字節(jié)代碼,接著進行解密和驗證,最后定義出要在 Java 虛擬機中運行的類來。下面將通過兩個具體的實例來說明類加載器的開發(fā)。
文件系統(tǒng)類加載器
第一個類加載器用來加載存儲在文件系統(tǒng)上的 Java 字節(jié)代碼。完整的實現(xiàn)如 代碼清單 6 所示。
清單 6. 文件系統(tǒng)類加載器
public class FileSystemClassLoader extends ClassLoader {
private String rootDir;
public FileSystemClassLoader(String rootDir) {
this.rootDir = rootDir;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = getClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
}
else {
return defineClass(name, classData, 0, classData.length);
}
}
private byte[] getClassData(String className) {
String path = classNameToPath(className);
try {
InputStream ins = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private String classNameToPath(String className) {
return rootDir + File.separatorChar
+ className.replace('.', File.separatorChar) + ".class";
}
}
|
如 代碼清單 6 所示,類 FileSystemClassLoader
繼承自類 java.lang.ClassLoader
。在 表 1 中列出的 java.lang.ClassLoader
類的常用方法中,一般來說,自己開發(fā)的類加載器只需要覆寫 findClass(String name)
方法即可。java.lang.ClassLoader
類的方法 loadClass()
封裝了前面提到的代理模式的實現(xiàn)。該方法會首先調(diào)用 findLoadedClass()
方法來檢查該類是否已經(jīng)被加載過;如果沒有加載過的話,會調(diào)用父類加載器的 loadClass()
方法來嘗試加載該類;如果父類加載器無法加載該類的話,就調(diào)用 findClass()
方法來查找該類。因此,為了保證類加載器都正確實現(xiàn)代理模式,在開發(fā)自己的類加載器時,最好不要覆寫 loadClass()
方法,而是覆寫 findClass()
方法。
類 FileSystemClassLoader
的 findClass()
方法首先根據(jù)類的全名在硬盤上查找類的字節(jié)代碼文件(.class 文件),然后讀取該文件內(nèi)容,最后通過 defineClass()
方法來把這些字節(jié)代碼轉(zhuǎn)換成 java.lang.Class
類的實例。
網(wǎng)絡類加載器
下面將通過一個網(wǎng)絡類加載器來說明如何通過類加載器來實現(xiàn)組件的動態(tài)更新。即基本的場景是:Java 字節(jié)代碼(.class)文件存放在服務器上,客戶端通過網(wǎng)絡的方式獲取字節(jié)代碼并執(zhí)行。當有版本更新的時候,只需要替換掉服務器上保存的文件即可。通過類加載器可以比較簡單的實現(xiàn)這種需求。
類 NetworkClassLoader
負責通過網(wǎng)絡下載 Java 類字節(jié)代碼并定義出 Java 類。它的實現(xiàn)與 FileSystemClassLoader
類似。在通過 NetworkClassLoader
加載了某個版本的類之后,一般有兩種做法來使用它。第一種做法是使用 Java 反射 API。另外一種做法是使用接口。需要注意的是,并不能直接在客戶端代碼中引用從服務器上下載的類,因為客戶端代碼的類加載器找不到這些類。使用 Java 反射 API 可以直接調(diào)用 Java 類的方法。而使用接口的做法則是把接口的類放在客戶端中,從服務器上加載實現(xiàn)此接口的不同版本的類。在客戶端通過相同的接口來使用這些實現(xiàn)類。網(wǎng)絡類加載器的具體代碼見 下載。
在介紹完如何開發(fā)自己的類加載器之后,下面說明類加載器和 Web 容器的關系。
類加載器與 Web 容器
對于運行在 Java EE™ 容器中的 Web 應用來說,類加載器的實現(xiàn)方式與一般的 Java 應用有所不同。不同的 Web 容器的實現(xiàn)方式也會有所不同。以 Apache Tomcat 來說,每個 Web 應用都有一個對應的類加載器實例。該類加載器也使用代理模式,所不同的是它是首先嘗試去加載某個類,如果找不到再代理給父類加載器。這與一般類加載器的順序是相反的。這是 Java Servlet 規(guī)范中的推薦做法,其目的是使得 Web 應用自己的類的優(yōu)先級高于 Web 容器提供的類。這種代理模式的一個例外是:Java 核心庫的類是不在查找范圍之內(nèi)的。這也是為了保證 Java 核心庫的類型安全。
絕大多數(shù)情況下,Web 應用的開發(fā)人員不需要考慮與類加載器相關的細節(jié)。下面給出幾條簡單的原則:
- 每個 Web 應用自己的 Java 類文件和使用的庫的 jar 包,分別放在
WEB-INF/classes
和 WEB-INF/lib
目錄下面。
- 多個應用共享的 Java 類文件和 jar 包,分別放在 Web 容器指定的由所有 Web 應用共享的目錄下面。
- 當出現(xiàn)找不到類的錯誤時,檢查當前類的類加載器和當前線程的上下文類加載器是否正確。
在介紹完類加載器與 Web 容器的關系之后,下面介紹它與 OSGi 的關系。
類加載器與 OSGi
OSGi™ 是 Java 上的動態(tài)模塊系統(tǒng)。它為開發(fā)人員提供了面向服務和基于組件的運行環(huán)境,并提供標準的方式用來管理軟件的生命周期。OSGi 已經(jīng)被實現(xiàn)和部署在很多產(chǎn)品上,在開源社區(qū)也得到了廣泛的支持。Eclipse 就是基于 OSGi 技術來構建的。
OSGi 中的每個模塊(bundle)都包含 Java 包和類。模塊可以聲明它所依賴的需要導入(import)的其它模塊的 Java 包和類(通過 Import-Package
),也可以聲明導出(export)自己的包和類,供其它模塊使用(通過 Export-Package
)。也就是說需要能夠隱藏和共享一個模塊中的某些 Java 包和類。這是通過 OSGi 特有的類加載器機制來實現(xiàn)的。OSGi 中的每個模塊都有對應的一個類加載器。它負責加載模塊自己包含的 Java 包和類。當它需要加載 Java 核心庫的類時(以 java
開頭的包和類),它會代理給父類加載器(通常是啟動類加載器)來完成。當它需要加載所導入的 Java 類時,它會代理給導出此 Java 類的模塊來完成加載。模塊也可以顯式的聲明某些 Java 包和類,必須由父類加載器來加載。只需要設置系統(tǒng)屬性 org.osgi.framework.bootdelegation
的值即可。
假設有兩個模塊 bundleA 和 bundleB,它們都有自己對應的類加載器 classLoaderA 和 classLoaderB。在 bundleA 中包含類 com.bundleA.Sample
,并且該類被聲明為導出的,也就是說可以被其它模塊所使用的。bundleB 聲明了導入 bundleA 提供的類 com.bundleA.Sample
,并包含一個類 com.bundleB.NewSample
繼承自 com.bundleA.Sample
。在 bundleB 啟動的時候,其類加載器 classLoaderB 需要加載類 com.bundleB.NewSample
,進而需要加載類 com.bundleA.Sample
。由于 bundleB 聲明了類 com.bundleA.Sample
是導入的,classLoaderB 把加載類 com.bundleA.Sample
的工作代理給導出該類的 bundleA 的類加載器 classLoaderA。classLoaderA 在其模塊內(nèi)部查找類 com.bundleA.Sample
并定義它,所得到的類 com.bundleA.Sample
實例就可以被所有聲明導入了此類的模塊使用。對于以 java
開頭的類,都是由父類加載器來加載的。如果聲明了系統(tǒng)屬性 org.osgi.framework.bootdelegation=com.example.core.*
,那么對于包 com.example.core
中的類,都是由父類加載器來完成的。
OSGi 模塊的這種類加載器結構,使得一個類的不同版本可以共存在 Java 虛擬機中,帶來了很大的靈活性。不過它的這種不同,也會給開發(fā)人員帶來一些麻煩,尤其當模塊需要使用第三方提供的庫的時候。下面提供幾條比較好的建議:
- 如果一個類庫只有一個模塊使用,把該類庫的 jar 包放在模塊中,在
Bundle-ClassPath
中指明即可。
- 如果一個類庫被多個模塊共用,可以為這個類庫單獨的創(chuàng)建一個模塊,把其它模塊需要用到的 Java 包聲明為導出的。其它模塊聲明導入這些類。
- 如果類庫提供了 SPI 接口,并且利用線程上下文類加載器來加載 SPI 實現(xiàn)的 Java 類,有可能會找不到 Java 類。如果出現(xiàn)了
NoClassDefFoundError
異常,首先檢查當前線程的上下文類加載器是否正確。通過 Thread.currentThread().getContextClassLoader()
就可以得到該類加載器。該類加載器應該是該模塊對應的類加載器。如果不是的話,可以首先通過 class.getClassLoader()
來得到模塊對應的類加載器,再通過 Thread.currentThread().setContextClassLoader()
來設置當前線程的上下文類加載器。
總結
類加載器是 Java 語言的一個創(chuàng)新。它使得動態(tài)安裝和更新軟件組件成為可能。本文詳細介紹了類加載器的相關話題,包括基本概念、代理模式、線程上下文類加載器、與 Web 容器和 OSGi 的關系等。開發(fā)人員在遇到 ClassNotFoundException
和 NoClassDefFoundError
等異常的時候,應該檢查拋出異常的類的類加載器和當前線程的上下文類加載器,從中可以發(fā)現(xiàn)問題的所在。在開發(fā)自己的類加載器的時候,需要注意與已有的類加載器組織結構的協(xié)調(diào)。
posted @
2010-04-24 22:26 junly 閱讀(604) |
評論 (0) |
編輯 收藏
來自:http://www.pussor.com/?p=3
官網(wǎng):http://www.pushlets.com/
Ajax等Web 2.0技術的廣泛應用,推動了C/S向B/S的轉(zhuǎn)變,如今很多應用如監(jiān)控、即時通信等系統(tǒng)都需要實時同步服務器端和客戶端的數(shù)據(jù)更新。Comet在這種需求下應運而生,本文簡單介紹了基于Comet的開源框架Pushlet。
Comet基礎
Comet 是一個用于描述客戶端和服務器之間的交互的術語,即使用長期保持的 HTTP 連接來在連接保持暢通的情況下支持客戶端和服務器間的事件驅(qū)動的通信。
—引用自“Comet的誘惑”
傳統(tǒng)的web系統(tǒng)的工作流程是客戶端發(fā)出請求,服務器端進行響應,而Comet則是在現(xiàn)有技術的基礎上,實現(xiàn)服務器數(shù)據(jù)、事件等快速PUSH到客戶端,所以會出現(xiàn)一個術語”服務器推“技術。
PUSH實現(xiàn)方式
JSP/SERVLET PUSH
原理:
利用JSP/SERVEL技術,在不關閉HTTP流的情況下PUSH數(shù)據(jù)到客戶端瀏覽器;
實現(xiàn):
基于 AJAX 的長輪詢(long-polling)方式
AJAX 的出現(xiàn)使得 JavaScript 可以調(diào)用 XMLHttpRequest 對象發(fā)出 HTTP 請求,JavaScript 響應處理函數(shù)根據(jù)服務器返回的信息對 HTML 頁面的顯示進行更新。使用 AJAX 實現(xiàn)“服務器推”與傳統(tǒng)的 AJAX 應用不同之處在于:
- 服務器端會阻塞請求直到有數(shù)據(jù)傳遞或超時才返回。
- 客戶端 JavaScript 響應處理函數(shù)會在處理完服務器返回的信息后,再次發(fā)出請求,重新建立連接。
- 當客戶端處理接收的數(shù)據(jù)、重新建立連接時,服務器端可能有新的數(shù)據(jù)到達;這些信息會被服務器端保存直到客戶端重 新建立連接,客戶端會一次把當前服務器端所有的信息取回。
Pushlet實例
以Pushlet中的ping案例來進行分析:
- 新建一個Web項目取名ping,并導入Pushlet的jar包;
- 在src目錄下配置sources.properties、pushlet.properties文件;
- webroot目錄下導入js-pushlet-client.js,js-pushlet-net.html;
- 新建TestEventPullSources.java;
- 新建index.html,引入js-pushlet-client.js;
- 新建pingok.jsp;
- 修改web.xml加上pushlet的servlet
- 打包、部署ping項目;
實例1 Pushlet CookBook部分翻譯 + 注釋
pushlet 2.0.3 源碼分析(服務器端)
posted @
2010-04-23 13:38 junly 閱讀(6195) |
評論 (1) |
編輯 收藏
PermGen space的全稱是Permanent Generation space,是指內(nèi)存的永久保存區(qū)域,這塊內(nèi)存主要是存放Class和Meta信息的,Class在被Loader時就會被放到PermGen space中,它和存放類實例(Instance)的Heap區(qū)域不同,GC(Garbage Collection)不會在主程序運行期對PermGen space進行清理,所以如果APP會LOAD很多CLASS的話,就很可能出現(xiàn)PermGen space錯誤,這種錯誤常見在web服務器對JSP進行pre compile的時候。
在tomcat中redeploy時出現(xiàn)outofmemory的錯誤. 可以有以下幾個方面的原因:
1, 使用了proxool,因為proxool內(nèi)部包含了一個老版本的cglib.
2, log4j,最好不用,只用common-logging
3, 老版本的cglib,快點更新到最新版。
4, 更新到最新的hibernate3.2 3、
這里以tomcat環(huán)境為例,其它WEB服務器如jboss,weblogic等是同一個道理。
一、java.lang.OutOfMemoryError: PermGen space PermGen space的全稱是Permanent Generation space,是指內(nèi)存的永久保存區(qū)域, 這塊內(nèi)存主要是被JVM存放Class和Meta信息的,Class在被Loader時就會被放到PermGen space中, 它和存放類實例(Instance)的Heap區(qū)域不同,GC(Garbage Collection)不會在主程序運行期對 PermGen space進行清理,所以如果你的應用中有很多CLASS的話,就很可能出現(xiàn)PermGen space錯誤, 這種錯誤常見在web服務器對JSP進行pre compile的時候。如果你的WEB APP下都用了大量的第三方jar, 其大小超過了jvm默認的大小(4M)那么就會產(chǎn)生此錯誤信息了。
解決方法: 手動設置MaxPermSize大小修改TOMCAT_HOME/bin/catalina.sh 在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行: JAVA_OPTS="-server -XX:PermSize=64M -XX:MaxPermSize=128m
建議:將相同的第三方jar文件移置到tomcat/shared/lib目錄下,這樣可以達到減少jar 文檔重復占用內(nèi)存的目的。
二、java.lang.OutOfMemoryError: Java heap space Heap size 設置 JVM堆的設置是指java程序運行過程中JVM可以調(diào)配使用的內(nèi)存空間的設置.JVM在啟動的時候會自動設置Heap size的值,其初始空間(即-Xms)是物理內(nèi)存的1/64,最大空間(-Xmx)是物理內(nèi)存的1/4??梢岳肑VM提供的-Xmn -Xms -Xmx等選項可進行設置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。提示:在JVM中如果98%的時間是用于GC且可用的Heap size 不足2%的時候?qū)伋龃水惓P畔?。提示:Heap Size 最大不要超過可用物理內(nèi)存的80%,一般的要將-Xms和-Xmx選項設置為相同,而-Xmn為1/4的-Xmx值。
解決方法:手動設置Heap size 修改TOMCAT_HOME/bin/catalina.sh 在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行: JAVA_OPTS="-server -Xms800m -Xmx800m -XX:MaxNewSize=256m"
三、實例,以下給出1G內(nèi)存環(huán)境下java jvm 的參數(shù)設置參考:
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m -Djava.awt.headless=true "
內(nèi)存不足 (OutOfMemory) - 由于java 堆或本地內(nèi)存中的內(nèi)存耗盡,應用程序顯示“內(nèi)存不足”錯誤。
內(nèi)存泄漏-java 堆或本地內(nèi)存的持續(xù)內(nèi)存增長,最終將導致內(nèi)存不足狀態(tài)。
調(diào)試內(nèi)存泄漏狀態(tài)的技術與調(diào)試內(nèi)存不足狀態(tài)的技術相同。
Java 堆 - 這是 JVM 用來分配 java 對象的內(nèi)存。
如果JVM不能在java堆中獲得更多內(nèi)存來分配更多java對象,將會拋出java內(nèi)存不足(java.lang.OutOfMemoryError)錯誤。默認情況下,應用程序崩潰。
本地內(nèi)存 - 這是 JVM 用于其內(nèi)部操作的內(nèi)存。
如果 JVM 無法獲得更多本地內(nèi)存,它將拋出本地內(nèi)存不足(本地 OutOfMemoryError)錯誤。當進程到達操作系統(tǒng)的進程大小限值,或者當計算機用完 RAM 和交換空間時,通常會發(fā)生這種情況。
進程大小 - 進程大小將是 java 堆、本地內(nèi)存與加載的可執(zhí)行文件和庫所占用內(nèi)存的總和。在 32 位操作系統(tǒng)上,進程的虛擬地址空間最大可達到 4 GB。從這 4 GB 內(nèi)存中,操作系統(tǒng)內(nèi)核為自己保留一部分內(nèi)存(通常為 1 - 2 GB)。剩余內(nèi)存可用于應用程序。
2.
java虛擬機是遵照有關規(guī)范的一個軟件實現(xiàn),存在于內(nèi)存中。jvm是由安裝于機器上的jre(java運行環(huán)境)生成的。通常來說,每次運行一個application都會生成一個jvm,但是也可以有多個程序在同一個jvm里面。
可以使用命令java -X查看非標準(non-standard)的程序運行選項,以下3個是我所關心的:
-Xms set initial Java heap size
-Xmx set maximum Java heap size
-Xss set java thread stack size
-Xmx設置應用程序(不是jvm)能夠使用的最大內(nèi)存數(shù),這個值也不應該設置過大,超過機器內(nèi)存。
例如:java -Xmx50M testMemory
-Xms設置程序初始化的時候內(nèi)存棧的大小。有時可以用于改變程序運行的效率。
例如使用以下方式運行一個占用20M左右內(nèi)存的程序testMemory:
java -Xms50M testMemory
使用這個方法可以得到應用的空間使用量

/**//*
Returns the total amount of memory in the Java virtual machine. The value returned by this method may vary over time, depending on the host environment.
*/
System.out.println(Runtime.getRuntime().totalMemory());
3.
查看java進程的內(nèi)存使用量:
Windows任務管理器(Windows Task Manager)
增大運行應用的Heap的取值
//命令行執(zhí)行方式
java -Xms256 -Xmx512m app
//Tomcat執(zhí)行方式
Windows下,在文件{tomcat_home}/bin/catalina.bat
在文件開頭可增加如下設置:
set JAVA_OPTS=-Xms256m -Xmx512m
Unix下,在文件{tomcat_home}/bin/catalina.sh的前面,可增加如下設置:
JAVA_OPTS='-Xms256m -Xmx512m'
posted @
2010-04-22 10:18 junly 閱讀(330) |
評論 (0) |
編輯 收藏
摘要: <c3p0-config>
<default-config>
<!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數(shù)。Default: 3 -->
<property name="acquireIncrement">3</property&g...
閱讀全文
posted @
2010-04-20 09:20 junly 閱讀(859) |
評論 (0) |
編輯 收藏
1 配置Action的struts.xml ??????
<struts>
<!-- Struts 2的Action都必須配置在package里-->
<package name="default" extends="struts-default">
<!-- 定義一個Logon的Action實現(xiàn)類為lee.Logon -->
<action name="Logon" class="lee.Logon">
<!--配置Action返回input時轉(zhuǎn)入/pages/Logon.jsp頁面-->
<result name="input">/pages/Logon.jsp</result>
<!--配置Action返回cancel時重定向到Welcome的Action-->
<result name="cancel" type="redirect-action">Welcome</result>
<!--配置Action返回success時重定向到MainMenu的Action -->
<result type="redirect-action">MainMenu</result>
<!--配置Action返回expired時進入ChangePassword的Action連-->
<result name="expired" type="chain">ChangePassword</result>
</action>
<!--定義Logoff的Action實現(xiàn)類為lee.Logoff -->
<action name="Logoff" class=" lee.Logoff">
<!--配置Action返回success重定向到MainMenu的Action -->
<result type="redirect-action">Welcome</result>
</action>
</package>
</struts>
2 配置Struts 2 全局屬性的struts.properties ??????
#指定Struts 2處于開發(fā)狀態(tài) ????????????
struts.devMode = false
#指定當Struts 2配置文件改變后,Web框架是否重新加載Struts 2配置文件 ????????
struts.configuration.xml.reload=true
3 編輯Web 應用的web.xml配置文件,配置Struts 2 的核心Filter??
<?xml version="1.0" encoding="GBK"?>
<!-- web-app是Web應用配置文件的根元素,提定Web應用的Schema信息-->
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.
com/xml/ns/j2ee/web-app_2_4.xsd">
<!--定義Struts 2的FilterDispatcher的Filter -->
<filter>
<!-- 定義核心Filter的名字-->
<filter-name>struts2</filter-name>
<!--典定義核心Filter的實現(xiàn)類 -->
<filter-class>org.apache.Struts2.dispatcher.FilterDispatcher
</ filter-class>
<init-param>
<!--配置Struts 2框架默認加載的Action包結構-->
<param-name>actionPackages</param-name>
<param-value>org.apache.struts2.showcase.person</param-value>
</init-param>
<!--配置Struts 2框架的配置提共者類-->
<init-param>
<param-name>configProviders </param-name>
<param-value>lee.MyConfigurationProvider</param-value>
</init-param>
</filter>
<!-- FilterDispatcher用來初始化Struts 2并且處理所有的Web請求-->
<filter-mapping>
<filter-name>Struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
三個有特殊意義的初始化參數(shù):
(1) config:參數(shù)值是以(,)號隔開的字符串,每個字符串都是一個xml配置文件的位置。Struts2框架將自動加載這些配置文件。
(2) actionPackages:參數(shù)值是以(,)號隔開的字符串,每個字符串都是一個包空間,Struts2框架將掃描這些包空間下的Action類。
(3) configProviders:如果用戶需要實現(xiàn)自已的ConfigurationProvider類,用戶可以提供一個或多個實現(xiàn)了ConfigurationProvider接口的類,然后將這些類的類名設置成該屬性的值,多個類名這間以(,)隔開。
(4) 還可以在些配置常量,其中<param-name>子元素指定常量name,而<param-value>指定常量value.
4 在web.xml文件中配置加載Struts 2標簽庫
<!--手動配置Struts 2的標簽庫-->
<taglib>
<!--配置Struts 2標簽庫的URI -->
<taglib-uri>/s</taglib-uri>
<!--指定Struts 2標簽庫定義文件的位置-->
<taglib-location>/WEB-INF/struts-tags.tld</taglib-location>
</taglib>
注意:
Servlet2.4以上的規(guī)范,無需在web.xml文件中配置標簽庫定義,因為Servlet2.4規(guī)范會自動加載該標簽庫文件。
5 文件結構
Struts2qs
|-WEB-INF
| |-classes(struts.xml)
| |-lib(commons-logging.jar??freemarker.jar??ognl.jar??struts2-core.jar??xwork.jar)
| |-web.xml
|-login.jsp
6 將struts.xml配置文件分解成多個配置文件,模塊化管理
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 指定Struts 2 配置文件的DTD信息-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<!--下面是Struts 2配置文件的根元素-->
<struts>
<!--通過include元素導入其他配置文件-->
<include file="struts-part1.xml" />


</struts>
7 包空間的繼承
<!--指定Struts 2 配置文件的根元素 -->
<struts>
<!-- 配置名為lee的包空間,繼承struts-default包空間-->
<package name="lee" extends="struts-default">


</package>
</struts>
8 可插拔的方式來安裝插件
配置struts2與spring框架,只要將struts2-spring-plugin2.06.jar文件放在WEB-INF/lib路徑下,Struts2框架將自動加載該文件
posted @
2010-03-05 10:24 junly 閱讀(343) |
評論 (0) |
編輯 收藏
方法一:
conf/server.xml文件
Context path中間加上reloadable="true"
例如:<Context path="" docBase="" reloadable="true">
方法二:
刪除work目錄下的緩存文件
可以把Catalina目錄刪除;
注意:不能把work整個目錄刪除,不然重啟tomcat時,會把conf/web.xml刪除掉,這樣在啟動時,日志會提示:No Default web.xml,且訪問頁面會顯示404錯誤;
posted @
2010-02-28 15:22 junly 閱讀(896) |
評論 (0) |
編輯 收藏
什么是JNDI?為什么使用JNDI?
JNDI是Java 命名與目錄接口(Java Naming and Directory Interface)
要了解JNDI的作用,我們可以從“如果不用JNDI我們怎樣做?用了JNDI后我們又將怎樣做?”這個問題來探討。
沒有JNDI的做法:
程序員開發(fā)時,知道要開發(fā)訪問MySQL數(shù)據(jù)庫的應用,于是將一個對 MySQL JDBC 驅(qū)動程序類的引用進行了編碼,并通過使用適當?shù)?JDBC URL 連接到數(shù)據(jù)庫。
就像以下代碼這樣:
Connection conn=null;
try {
Class.forName("com.mysql.jdbc.Driver",true, Thread.currentThread().getContextClassLoader()); conn=DriverManager.getConnection("jdbc:mysql://MyDBServer?user=qingfeng&password=mingyue"); /* 使用conn并進行SQL操作 */ conn.close();
} catch(Exception e) {
e.printStackTrace();
} finally {
if(conn!=null) {
try { conn.close();
} catch(SQLException e) {
}
}}
這是傳統(tǒng)的做法,這種做法一般在小規(guī)模的開發(fā)過程中不會產(chǎn)生問題,只要程序員熟悉Java語言、了解JDBC技術和MySQL,可以很快開發(fā)出相應的應用程序。
沒有JNDI的做法存在的問題:
1、數(shù)據(jù)庫服務器名稱MyDBServer 、用戶名和口令都可能需要改變,由此引發(fā)JDBC URL需要修改;
2、數(shù)據(jù)庫可能改用別的產(chǎn)品,如改用DB2或者Oracle,引發(fā)JDBC驅(qū)動程序包和類名需要修改;
3、隨著實際使用終端的增加,原配置的連接池參數(shù)可能需要調(diào)整;
4、......
解決辦法:
程序員應該不需要關心“具體的數(shù)據(jù)庫后臺是什么?JDBC驅(qū)動程序是什么?JDBC URL格式是什么?訪問數(shù)據(jù)庫的用戶名和口令是什么?”等等這些問題,程序員編寫的程序應該沒有對 JDBC 驅(qū)動程序的引用,沒有服務器名稱,沒有用戶名稱或口令 —— 甚至沒有數(shù)據(jù)庫池或連接管理。而是把這些問題交給J2EE容器來配置和管理,程序員只需要對這些配置和管理進行引用即可。
由此,就有了JNDI。
用了JNDI之后的做法:
首先,在在J2EE容器中配置JNDI參數(shù),定義一個數(shù)據(jù)源,也就是JDBC引用參數(shù),給這個數(shù)據(jù)源設置一個名稱;然后,在程序中,通過數(shù)據(jù)源名稱引用數(shù)據(jù)源從而訪問后臺數(shù)據(jù)庫。
具體操作如下(以JBoss為例):
1、配置數(shù)據(jù)源
在JBoss的 D:\jboss420GA\docs\examples\jca 文件夾下面,有很多不同數(shù)據(jù)庫引用的數(shù)據(jù)源定義模板。將其中的 mysql-ds.xml 文件Copy到你使用的服務器下,如 D:\jboss420GA\server\default\deploy。
修改 mysql-ds.xml 文件的內(nèi)容,使之能通過JDBC正確訪問你的MySQL數(shù)據(jù)庫,如下:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>MySqlDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/lw</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>root</user-name>
<password>rootpassword</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>
這里,定義了一個名為MySqlDS的數(shù)據(jù)源,其參數(shù)包括JDBC的URL,驅(qū)動類名,用戶名及密碼等。
2、在程序中引用數(shù)據(jù)源:
Connection conn=null;
try {
Context ctx=new InitialContext();
Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用數(shù)據(jù)源
DataSource ds=(Datasource)datasourceRef; conn=ds.getConnection(); /* 使用conn進行數(shù)據(jù)庫SQL操作 */
......
c.close();
} catch(Exception e) {
e.printStackTrace();
} finally { if(conn!=null) {
try {
conn.close();
} catch(SQLException e) { } }}
直接使用JDBC或者通過JNDI引用數(shù)據(jù)源的編程代碼量相差無幾,但是現(xiàn)在的程序可以不用關心具體JDBC參數(shù)了。
在系統(tǒng)部署后,如果數(shù)據(jù)庫的相關參數(shù)變更,只需要重新配置 mysql-ds.xml 修改其中的JDBC參數(shù),只要保證數(shù)據(jù)源的名稱不變,那么程序源代碼就無需修改。
由此可見,JNDI避免了程序與數(shù)據(jù)庫之間的緊耦合,使應用更加易于配置、易于部署。
所以,在J2EE規(guī)范中,J2EE 中的資源并不局限于 JDBC 數(shù)據(jù)源。引用的類型有很多,其中包括資源引用(已經(jīng)討論過)、環(huán)境實體和 EJB 引用。特別是 EJB 引用,它暴露了 JNDI 在 J2EE 中的另外一項關鍵角色:查找其他應用程序組件。
JNDI原理
sun只是提供了JNDI的接口(即規(guī)范),IBM, Novell, Sun 和 WebLogic 和JBOSS已經(jīng)為 JNDI 提供了服務提供程序,
在JNDI中,在目錄結構中的每一個結點稱為context。每一個JNDI名字都是相對于context的。這里沒有絕對名字的概念存在。對一個應用來說,它可以通過使用 InitialContext 類來得到其第一個context:
Context ctx = new InitialContext();
ctx.bind("name", Object);
ctx.lookup("name");
Context:上下文,我的理解是相當與文件系統(tǒng)的中的目錄(JNDI的Naming Service是可以用操作系統(tǒng)的文件系統(tǒng)的,哈哈).
entry/object:一個節(jié)點,相當與文件系統(tǒng)中的目錄或文件.
filter:查詢/過濾條件是一個字符串表達式如:(&(objectClass=top)(cn=*))查詢出objectClass屬性為top,cn屬性為所有情況的entry.
Attribute:entry/object的屬性可以理解成JAVA對象的屬性,不同的是這個屬性可以多次賦值.
A.將接口分為Context 和 DirContext
JNDI有兩個核心接口Context和DirContext,Context中包含 了基本的名字操作,而DirContext則將這些操作擴展到目錄服務。DirContext 對Context進行了擴展,提供了基本的目錄服務操作, 對名字對象屬性的維護、基于屬性的名字查找等等。
B.上下文列表的多種方法
一般來說有兩種進行上下文列表的應用:上下文瀏覽應用和對上下文中的對象進行實際操作的應用。
上下文瀏覽應用一般只需要顯示上下文中包含內(nèi)容的名字,或者再獲取一些諸如對象的類型之類的信息。這種類型的應用一般都是交互式的,可以允許用戶在列舉的上下文列表中選擇一些進行進一步的顯示。
另外有一些應用需要對上下文中的對象進行實際的操作,比如,一個備份程序需要對目錄中所有文件的狀態(tài)進行操作,或者某打印機管理員可能需要對大樓中的所有打印機進行復位。為了進行這樣的操作,程序需要獲取上下文中的實際對象。
對于這樣兩種類型的應用,Context接口提供了兩種上下文列表方法list()和 listBindings()。其中l(wèi)ist()只返回一系列名字/類映射,而listBindings() 則返回名字、類和對象本身。顯然 list()用于上下文瀏覽應用而listBindings()用于那些需要對對象進行實際操作的應用。
例:
=================將以下代碼段添加到server.xml中的<Host>中============
<!-- configure DataSource. Add the following code into server.xml -->
<Context path="/bookstore" docBase="bookstore" debug="0"
reloadable="true" >
<!-- 數(shù)據(jù)源名稱 -->
<Resource name="jdbc/BookDB"
auth="Container"
type="javax.sql.DataSource" />
<ResourceParams name="jdbc/BookDB">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<!-- Maximum number of dB connections in pool. Make sure you
configure your mysqld max_connections large enough to handle
all of your db connections. Set to 0 for no limit.
-->
<!-- 活動狀態(tài)最大連接數(shù) -->
<parameter>
<name>maxActive</name>
<value>100</value>
</parameter>
<!-- Maximum number of idle dB connections to retain in pool.
Set to 0 for no limit.
-->
<!-- 空閑狀態(tài)數(shù)據(jù)庫連接最大數(shù) -->
<parameter>
<name>maxIdle</name>
<value>30</value>
</parameter>
<!-- Maximum time to wait for a dB connection to become available
in ms, in this example 10 seconds. An Exception is thrown if
this timeout is exceeded. Set to -1 to wait indefinitely.
Maximum time to wait for a dB connection to become available
in ms, in this example 10 seconds. An Exception is thrown if
this timeout is exceeded. Set to -1 to wait indefinitely.
-->
<!-- 數(shù)據(jù)庫處于空閑狀態(tài)的最長時間 -->
<parameter>
<name>maxWait</name>
<value>10000</value>
</parameter>
<!-- MySQL dB username and password for dB connections -->
<!-- 指定連接數(shù)據(jù)庫的用戶名及密碼 -->
<parameter>
<name>username</name>
<value>dbuser</value>
</parameter>
<parameter>
<name>password</name>
<value>1234</value>
</parameter>
<!-- Class name for mm.mysql JDBC driver -->
<!-- 指定JDBC驅(qū)動 -->
<parameter>
<name>driverClassName</name>
<value>com.mysql.jdbc.Driver</value>
</parameter>
<!-- The JDBC connection url for connecting to your MySQL dB.
The autoReconnect=true argument to the url makes sure that the
mm.mysql JDBC Driver will automatically reconnect if mysqld closed the
connection. mysqld by default closes idle connections after 8 hours.
-->
<!-- 指定連接數(shù)據(jù)庫的URL -->
<parameter>
<name>url</name>
<value>jdbc:mysql://localhost:3306/BookDB?autoReconnect=true</value>
</parameter>
</ResourceParams>
</Context>
運行機制:
1、 首先程序代碼獲取初始化的 JNDI 環(huán)境并且調(diào)用 Context.lookup() 方法從 JNDI 服務提供者那里獲一個 DataSource 對象
2、 中間層 JNDI 服務提供者返回一個 DataSource 對象給當前的 Java 應用程序這個 DataSource 對象代表了中間層服務上現(xiàn)存的緩沖數(shù)據(jù)源
3、 應用程序調(diào)用 DataSource 對象的 getConnection() 方法
4、 當 DataSource 對象的 getConnection() 方法被調(diào)用時,中間層服務器將查詢數(shù)據(jù)庫 連接緩沖池中有沒有 PooledConnection 接口的實例對象。這個 PooledConnection 對象將被用于與數(shù)據(jù)庫建立物理上的數(shù)據(jù)庫連接
5、 如果在緩沖池中命中了一個 PooledCoonection 對象那么連接緩沖池將簡單地更 新內(nèi)部的緩沖連接隊列并將該 PooledConnection 對象返回。如果在緩沖池內(nèi)沒 有找到現(xiàn)成的 PooledConnection 對象,那么 ConnectionPoolDataSource 接口將會被 用來產(chǎn)生一個新的 PooledConnection 對象并將它返回以便應用程序使用
6。 中間層服務器調(diào)用 PooledConnection 對象的 getConnection() 方法以便返還一個 java.sql.Connection 對象給當前的 Java 應用程序
7、 當中間層服務器調(diào)用 PooledConnection 對象的 getConnection() 方法時, JDBC 數(shù)據(jù) 庫驅(qū)動程序?qū)?chuàng)建一個 Connection 對象并且把它返回中間層服務器
8、 中間層服務器將 Connection 對象返回給應用程序 Java 應用程序,可以認為這個 Connection 對象是一個普通的 JDBC Connection 對象使用它可以和數(shù)據(jù)庫建立。事 實上的連接與數(shù)據(jù)庫引擎產(chǎn)生交互操作 。
9、 當應用程序不需要使用 Connection 對象時,可以調(diào)用 Connection 接口的 close() 方 法。請注意這種情況下 close() 方法并沒有關閉事實上的數(shù)據(jù)庫連接,僅僅是釋 放了被應用程序占用的數(shù)據(jù)庫連接,并將它還給數(shù)據(jù)庫連接緩沖池,數(shù)據(jù)庫連接 緩沖池會自動將這個數(shù)據(jù)庫連接交給請求隊列中下一個的應用程序使用。
posted @
2010-02-23 10:36 junly 閱讀(1374) |
評論 (0) |
編輯 收藏
JDBC-ODBC橋連接數(shù)據(jù)庫
不足:需要在客戶端安裝ODBC驅(qū)動程序,ODBC驅(qū)動程序還需要具有客戶端的控制權限。
方法:
1.創(chuàng)建數(shù)據(jù)源
2.裝載驅(qū)動并與DBMS建立連接
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con=DriverManager.getConnectio("jdbc:odbc:jia","sa","123");
3.查詢
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery(sql);
4.更新
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);
if(stmt.executeUpdate(sql)<=0){return false;}
else{return true;}
5.讀取數(shù)據(jù)
Statement 接口提供了3種執(zhí)行SQL語句的方法:
qexecuteQuery()
qexecuteUpdate()
qexecute()
6.tTransaction
Connection con=DriverManager.getConnectio("jdbc:odbc:jia","sa","123");
con.setAutoCommit(false);//關閉自動提交模式
Statement stmt = con.createStatement();
stmt.qexecute(sql);
stmt.qexecute(sql);
stmt.qexecute(sql);
con.commit(); //提交
con.setAutoCommit(true);//開啟自動提交模式
con.rollback(); //回滾
7.關閉連接對象
con.close();
con.isClosed();
JDBC連接數(shù)據(jù)庫
方法:
1.Oracle8/8i/9i數(shù)據(jù)庫(thin模式)
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
String url="jdbc:oracle:thin:@localhost:1521:orcl"; //orcl為數(shù)據(jù)庫SID
String user="test";
String password="test";
Connection conn= DriverManager.getConnection(url,user,password);
2.DB2數(shù)據(jù)庫
Class.forName("com.ibm.db2.jdbc.app.DB2Driver ").newInstance();
String url="jdbc:db2://localhost:5000/sample"; //sample為你的數(shù)據(jù)庫名
String user="admin";
String password="";
Connection conn= DriverManager.getConnection(url,user,password);
3.Sql Server7.0/2000數(shù)據(jù)庫
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();
String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=mydb"; //mydb為數(shù)據(jù)庫
String user="sa";
String password="";
Connection conn= DriverManager.getConnection(url,user,password);
4.Sybase數(shù)據(jù)庫
Class.forName("com.sybase.jdbc.SybDriver").newInstance();
String url =" jdbc:sybase:Tds:localhost:5007/myDB";//myDB為你的數(shù)據(jù)庫名
Properties sysProps = System.getProperties();
SysProps.put("user","userid");
SysProps.put("password","user_password");
Connection conn= DriverManager.getConnection(url, SysProps);
5.Informix數(shù)據(jù)庫
Class.forName("com.informix.jdbc.IfxDriver").newInstance();
String url = "jdbc:informix-sqli://123.45.67.89:1533/myDB:INFORMIXSERVER=myserver;
user=testuser;password=testpassword"; //myDB為數(shù)據(jù)庫名
Connection conn= DriverManager.getConnection(url);
6.MySQL數(shù)據(jù)庫
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
String url ="jdbc:mysql://localhost/myDB?user=soft&password=soft1234&useUnicode=true&characterEncoding=8859_1" //myDB為數(shù)據(jù)庫名
Connection conn= DriverManager.getConnection(url);
7.PostgreSQL數(shù)據(jù)庫
Class.forName("org.postgresql.Driver").newInstance();
String url ="jdbc:postgresql://localhost/myDB" //myDB為數(shù)據(jù)庫名
String user="myuser";
String password="mypassword";
Connection conn= DriverManager.getConnection(url,user,password);
8.access數(shù)據(jù)庫直連用ODBC的
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver") ;
String url="jdbc:odbc:Driver={MicroSoft Access Driver (*.mdb)};DBQ="+application.getRealPath("/Data/ReportDemo.mdb");
Connection conn = DriverManager.getConnection(url,"","");
Statement stmtNew=conn.createStatement() ;
posted @
2010-02-23 10:33 junly 閱讀(331) |
評論 (0) |
編輯 收藏
J2EE平臺由一整套服務(Services)、應用程序接口(APIs)和協(xié)議構成,它對開發(fā)基于Web的多層應用提供了功能支持。在本文中將解釋支撐J2EE的13種核心技術:
JDBC,JNDI,EJBs,RMI,JSP,Javaservlets,XML,JMS,JavaIDL,JTS, JTA,JavaMail和JAF,同時還將描述在何時、何處需要使用這些技術。當然,我還要介紹這些不同的技術之間是如何交互的。此外,為了讓您更好地 感受J2EE的真實應用,將在WebLogic應用服務器,來自BEA Systems公司的一種廣為應用的產(chǎn)品環(huán)境下來介紹這些技術。不論對于Web Logic應用服務器和J2EE的新手,還是那些想了解J2EE能帶來什么好處的項目管理者和系統(tǒng)分析員,相信本文一定很有參考價值。
一、宏觀印象:分布式結構和J2EE
過去,二層化應用--通常被稱為client/server應用--是大家談論的最多的。在很多情況下,服務器提供的惟一服務就是數(shù)據(jù)庫服務。在這 種解決方案中,客戶端程序負責數(shù)據(jù)訪問、實現(xiàn)業(yè)務邏輯、用合適的樣式顯示結果、彈出預設的用戶界面、接受用戶輸入等。client/server結構通常 在第一次部署的時候比較容易,但難于升級或改進,而且經(jīng)?;谀撤N專有的協(xié)議,通常是某種數(shù)據(jù)庫協(xié)議。它使得重用業(yè)務邏輯和界面邏輯非常困難。更重要的 是,在Web時代,二層化應用通常不能體現(xiàn)出很好的伸縮性,因而很難適應Internet的要求。
Sun設計J2EE的部分起因就是想解決二層化結構的缺陷。于是,J2EE定義了一套標準來簡化N層企業(yè)級應用的開發(fā)。它定義了一套標準化的組件,并為這些組件提供了完整的服務。J2EE還自動為應用程序處理了很多實現(xiàn)細節(jié),如安全、多線程等。
用J2EE開發(fā)N層應用包括將二層化結構中的不同層面切分成許多層。一個N層化應用A能夠為以下的每種服務提供一個分開的層:
顯示:在一個典型的Web應用中,客戶端機器上運行的瀏覽器負責實現(xiàn)用戶界面。 字串1
動態(tài)生成顯示:盡管瀏覽器可以完成某些動態(tài)內(nèi)容顯示,但為了兼容不同的瀏覽器,這些動態(tài)生成工作應該放在Web服務器端進行,使用JSP、Servlets,或者XML(可擴展標記語言)和(可擴展樣式表語言)。 字串6
業(yè)務邏輯:業(yè)務邏輯適合用SessionEJBs(后面將介紹)來實現(xiàn)。
數(shù)據(jù)訪問:數(shù)據(jù)訪問適合用EntityEJBs(后面將介紹)和JDBC來實現(xiàn)。
后臺系統(tǒng)集成:同后臺系統(tǒng)的集成可能需要用到許多不同的技術,至于何種最佳需要根據(jù)后臺系統(tǒng)的特征而定。
您可能開始詫異:為什么有這么多的層?事實上,多層方式可以使企業(yè)級應用具有很強的伸縮性,它允許每層專注于特定的角色。例如,讓Web服務器負責提供頁面,應用服務器處理應用邏輯,而數(shù)據(jù)庫服務器提供數(shù)據(jù)庫服務。
由于J2EE建立在Java2平臺標準版(J2SE)的基礎上,所以具備了J2SE的所有優(yōu)點和功能。包括“編寫一次,到處可用”的可移植性、通過 JDBC訪問數(shù)據(jù)庫、同原有企業(yè)資源進行交互的CORBA技術,以及一個經(jīng)過驗證的安全模型。在這些基礎上,J2EE又增加了對EJB(企業(yè)級Java組 件)、Javaservlets、Java服務器頁面(JSPs)和XML技術的支持。
二、分布式結構與WebLogic應用服務器
J2EE提供了一個框架--一套標準API--用于開發(fā)分布式結構的應用,這個框架的實際實現(xiàn)留給了第三方廠商。部分廠商只是專注于整個J2EE架 構中的的特定組件,例如Apache的Tomcat提供了對JSP和servlets的支持,BEA系統(tǒng)公司則通過其WebLogic應用服務器產(chǎn)品為整 個J2EE規(guī)范提供了一個較為完整的實現(xiàn)。
WebLogic服務器已使建立和部署伸縮性較好的分布式應用的過程大為簡化。WebLogic和J2EE代你處理了大量常規(guī)的編程任務,包括提供事務服務、安全領域、可靠的消息、名字和目錄服務、數(shù)據(jù)庫訪問和連接池、線程池、負載平衡和容錯處理等。
通過以一種標準、易用的方式提供這些公共服務,象WebLogic服務器這樣的產(chǎn)品造就了具有更好伸縮性和可維護性的應用系統(tǒng),使其為大量的用戶提供了增長的可用性。
J2EE技術
在接下來的部分里,我們將描述構成J2EE的各種技術,并且了解WebLogic服務器是如何在一個分布式應用中對它們進行支持的。最常用的J2EE技術應該是JDBC、JNDI、EJB、JSP和servlets,對這些我們將作更仔細的考察。
三、Java Database Connectivity(JDBC)
JDBCAPI以一種統(tǒng)一的方式來對各種各樣的數(shù)據(jù)庫進行存取。和ODBC一樣,JDBC為開發(fā)人員隱藏了不同數(shù)據(jù)庫的不同特性。另外,由于JDBC建立在Java的基礎上,因此還提供了數(shù)據(jù)庫存取的平臺獨立性。 字串5
JDBC定義了4種不同的驅(qū)動程序,現(xiàn)分述如下:
類型1:JDBC-ODBCBridge
在JDBC出現(xiàn)的初期,JDBC-ODBC橋顯然是非常有實用意義的,通過JDBC-ODBC橋,開發(fā)人員可以使用JDBC來存取ODBC數(shù)據(jù)源。 不足的是,他需要在客戶端安裝ODBC驅(qū)動程序,換句話說,必須安裝MicrosoftWindows的某個版本。使用這一類型你需要犧牲JDBC的平臺 獨立性。另外,ODBC驅(qū)動程序還需要具有客戶端的控制權限。
類型2:JDBC-nativedriverbridge
JDBC本地驅(qū)動程序橋提供了一種JDBC接口,它建立在本地數(shù)據(jù)庫驅(qū)動程序的頂層,而不需要使用ODBC。JDBC驅(qū)動程序?qū)?shù)據(jù)庫的API從標準的JDBC調(diào)用轉(zhuǎn)換為本地調(diào)用。使用此類型需要犧牲JDBC的平臺獨立性,還要求在客戶端安裝一些本地代碼。 字串3
類型3:JDBC-networkbridge
JDBC網(wǎng)絡橋驅(qū)動程序不再需要客戶端數(shù)據(jù)庫驅(qū)動程序。它使用網(wǎng)絡上的中間服務器來存取數(shù)據(jù)庫。這種應用使得以下技術的實現(xiàn)有了可能,這些技術包括 負載均衡、連接緩沖池和數(shù)據(jù)緩存等。由于第3種類型往往只需要相對更少的下載時間,具有平臺獨立性,而且不需要在客戶端安裝并取得控制權,所以很適合于 Internet上的應用。
類型4:PureJavadriver
第4種類型通過使用一個純Java數(shù)據(jù)庫驅(qū)動程序來執(zhí)行數(shù)據(jù)庫的直接訪問。此類型實際上在客戶端實現(xiàn)了2層結構。要在N-層結構中應用,一個更好的做法是編寫一個EJB,讓它包含存取代碼并提供一個對客戶端具有數(shù)據(jù)庫獨立性的服務。
WebLogic服務器為一些通常的數(shù)據(jù)庫提供了JDBC驅(qū)動程序,包括Oracle,Sybase,MicrosoftSQLServer以及 Informix。它也帶有一種JDBC驅(qū)動程序用于Cloudscape,這是一種純Java的DBMS,WebLogic服務器中帶有該數(shù)據(jù)庫的評估 版本。
以下讓我們看一個JDBC實例:在這個例子中我們假定你已經(jīng)在Cloudscape中建立了一個PhoneBook數(shù)據(jù)庫,并且包含一個表,名為 CONTACT_TABLE,它帶有2個字段:NAME和PHONE。開始的時候先裝載CloudscapeJDBCdriver,并請求 drivermanager得到一個對PhoneBookCloudscape數(shù)據(jù)庫的連接。通過這一連接,我們可以構造一個Statement對象并用 它來執(zhí)行一個簡單的SQL查詢。最后,用循環(huán)來遍歷結果集的所有數(shù)據(jù),并用標準輸出將NAME和PHONE字段的內(nèi)容進行輸出。
import java.sql.*; 字串5 public class JDBCExample{public static void main( String args[] ){try{Class.forName("COM.cloudscape.core.JDBCDriver");Connection conn = DriverManager.getConnection("jdbc:cloudscape:PhoneBook");Statement stmt = conn.createStatement();String sql = "SELECT name, phone FROM CONTACT_TABLE ORDER BYname";ResultSet resultSet = stmt.executeQuery( sql ); 字串8 String name;String phone;while ( resultSet.next() ){name = resultSet.getString(1).trim();phone = resultSet.getString(2).trim();System.out.println( name + ", " + phone ); }catch ( Exception e ){// Handle exception heree.printStackTrace();}}}
OK。接著來看一看JDBC是如何在企業(yè)應用中的進行使用。
JDBC在企業(yè)級應用中的應用。以上實例其實是很基本的,可能有些微不足道。它假定了一個2層結構。在一個多層的企業(yè)級應用中,更大的可能是在客戶 端和一個EJB進行通信,該EJB將建立數(shù)據(jù)庫連接。為了實現(xiàn)和改進可伸縮性和系統(tǒng)性能, WebLogic服務器提供了對連接緩沖池connection pool的支持。
Connection pool減少了建立和釋放數(shù)據(jù)庫連接的消耗。在系統(tǒng)啟動以后即可建立這樣的緩沖池,此后如故再有對數(shù)據(jù)庫的請求,WebLogic服務器可以很簡單地從緩 沖池中取出數(shù)據(jù)。數(shù)據(jù)緩沖池可以在WebLogic服務器的 weblogic.properties 文件中進行定義。
在企業(yè)級應用的另一個常見的數(shù)據(jù)庫特性是事務處理。事務是一組申明statement,它們必須做為同一個statement來處理以保證數(shù)據(jù)完整 性。缺省情況下JDBC使用 auto-commit 事務模式。這可以通過使用Connection類的setAutoCommit() 方法來實現(xiàn)。現(xiàn)在已經(jīng)對JDBC有了一些認識,下面該轉(zhuǎn)向JNDI了。
四、Java Naming and Directory Interface (JNDI) 字串3
JNDI API被用于執(zhí)行名字和目錄服務。它提供了一致的模型來存取和操作企業(yè)級的資源如DNS和LDAP,本地文件系統(tǒng),后者在應用服務器中的對象。在JNDI 中,在目錄結構中的每一個結點稱為context。每一個JNDI名字都是相對于context的。這里沒有絕對名字的概念存在。對一個應用來說,它可以 通過使用 InitialContext 類來得到其第一個context: 字串6
Context ctx = new InitialContext();
應用可以通過這個初始化的context經(jīng)有這個目錄樹 來定位它所需要的資源或?qū)ο?。例如,假設你在Weblogic服務器中展開了一個EJB并將home接口綁定到名字 myApp.myEJB ,那么該EJB的某個客戶在取得一個初始化context以后,可以通過以下語句定位home接口:
MyEJBHome home = ctx.lookup( "myApp.myEJB" );
在這個例子中,一旦你有了對被請求對象的參考,EJB的home接口就可以在它上面調(diào)用方法。我們將在下面的"Enterprise Java Beans"章節(jié)中做更多的介紹。
以上關于JNDI的討論只是冰山之一角而已。如果要更進一步地在context中查找對象,JNDI也提供了一些方法來進行以下操作:將一個對象插 入或綁定到context。這在你展開一個EJB的時候是很有效的。從context中移去對象。列出context中的所有對象。創(chuàng)建或刪除子一級的 context。接下來,要開始關注EJB了。
五、Enterprise Java Beans (EJB)
J2EE技術之所以贏得某體廣泛重視的原因之一就是EJB。它們提供了一個框架來開發(fā)和實施分布式商務邏輯,由此很顯著地簡化了具有可伸縮性和高度 復雜的企業(yè)級應用的開發(fā)。EJB規(guī)范定義了EJB組件在何時如何與它們的容器進行交互作用。容器負責提供公用的服務,例如目錄服務、事務管理、安全性、資 源緩沖池以及容錯性。 字串8
EJB規(guī)范定義了三種基本的bean類型:
Stateless session beans: 提供某種單一的服務,不維持任何狀態(tài),在服務器故障發(fā)生時無法繼續(xù)存在,生命期相對較短。例如,一個stateless sessionbean可能被用于執(zhí)行溫度轉(zhuǎn)換計算。
Stateful session bean: T提供了與客戶端的會話交互,可以存儲狀態(tài)從而代表一個客戶。典型例子是購物車。Stateful session bean在服務器故障時無法繼續(xù)生存,生命氣相對較短。每一個實例只用于一個單個的線程。
Entity beans: 提供了一致性數(shù)據(jù)的表示-- 通常存放在數(shù)據(jù)庫中 -- 在服務器故障發(fā)生后能繼續(xù)存在。多用戶情況下可以使用EJB來表示相同的數(shù)據(jù)。entity EJB的一個典型例子是客戶的帳號信息。 字串2
盡管有以上的區(qū)別,所有的EJB還是有許多的共同之處。它們都處理homeinterface。它定義了一個客戶端是如何創(chuàng)建與消亡EJB的??梢?在bean中對定義了客戶端方法的遠程接口進行調(diào)用;bean類則執(zhí)行了主要的商務邏輯。描述EJB的開發(fā)已經(jīng)超出了本文的范圍。但是,如果一個EJB已 經(jīng)被開發(fā)了或者從第三方進行了購買,它就必須在應用服務器中進行發(fā)布。WebLogic Server 5.1帶有一個EJB Deployer Tool來協(xié)助處理EJB的發(fā)布。當你使用EJB Deployer Tool的時候,你要定義客戶端所用的JNDI名字來定位EJB。Deployer Tool將生成wrapper類來處理和容器的通信以及在一個jar文件中把被請求的Java類綁定在一起。
一旦EJB被發(fā)布,客戶端就可以使用它的JNDI名字來定位EJB。首先,它必須得到一個到home接口的reference。然后,客戶端可以使 用該接口,調(diào)用一個create() 方法來得到服務器上運行的某個bean實例的句柄;最后,客戶端可以使用該句柄在bean中調(diào)用方法。了解 EJB后,讓我們再來看JSP。 字串4
六、JavaServer Pages (JSPs) 字串4
可能已經(jīng)有許多人已經(jīng)熟悉Microsoft的Active Server Pages(ASP)技術了。JSP和ASP相對應的,但更具有平臺對立性。他們被設計用以幫助Web內(nèi)容開發(fā)人員創(chuàng)建動態(tài)網(wǎng)頁,并且只需要相對較少的代 碼。 即使Web設計師不懂得如何編程也可以使用JSP,因為JSP應用是很方便的。 JSP頁面由HTML代碼和嵌入其中的Java代碼所組成。服務器在頁面被客戶端所請求以后對這些Java代碼進行處理,然后將生成的HTML頁面返回給 客戶端的瀏覽器。 字串5
下面來看一個JSP的簡單實例。它只顯示了服務器的當前日期和時間。雖然,對語法的具體解釋已經(jīng)超出了本文的范圍,但我們還是可以很直觀地看到,Java代碼被放在 符號的中間,而Java的表達式則放在符號之間。
<H1>Date JSP sample</H1><H2><% response.setHeader("Refresh", 5); %>The current date is <%= new Date() %>.</H2> 字串1
下面是: Java servlets
七、Java Servlets
Servlet 提供的功能大多與JSP類似,不過實現(xiàn)的方式不同。JSP通常是大多數(shù)HTML代碼中嵌入少量的Java代碼,而servlets全部由Java寫成并且 生成HTML。Servlet是一種小型的Java程序,它擴展了Web服務器的功能。作為一種服務器端的應用,當被請求時開始執(zhí)行,這和CGI Perl腳本很相似。Servlets和CGI腳本的一個很大的區(qū)別是:每一個CGI在開始的時候都要求開始一個新的進程 -- 而servlets是在servlet引擎中以分離的線程來運行的。因此servlets在可伸縮性上提供了很好的改進。
在開發(fā)servlets的時候,您常常需要擴展javax.servlet.http.HttpServlet類,并且override一些它的方法,其中包括:
service(): 作為dispatcher來實現(xiàn)命令-定義方法
doGet(): 處理客戶端的HTTP GET請求。
doPost(): 進行HTTP POST操作 字串7
其它的方法還包括處理不同類型的HTTP請求 -- 可以參考HttpServlet API文檔。以上描述的是標準J2EE Servlet API的各種方法。WebLogic服務器提供了一個該API完整的實現(xiàn)途徑。一旦你開發(fā)了一個servlet,你就可以在 weblogic.properties 中加以注冊并由此可以在WebLogic服務器中對它進行配置。 字串8
通過Java servlets,我們已經(jīng)到達了J2EE主要技術的末尾了。但J2EE所提供的并不止于這些。下面的段落中我們將簡要地看一下現(xiàn)存的一些技術,包括RMI,Java IDL和CORBA, JTA, 以及XML,等等。 字串1
八、Remote Method Invocation (RMI)
正如其名字所表示的那樣,RMI協(xié)議是在遠程對象上調(diào)用一些方法。它使用了連續(xù)序列方式在客戶端和服務器端傳遞數(shù)據(jù)。RMI是一種被EJB使用的更下層的協(xié)議。 字串5
九、Java IDL/CORBA 字串5
在Java IDL的支持下,開發(fā)人員可以將Java和CORBA集成在一起。 他們可以創(chuàng)建Java對象并使之可在CORBA ORB中展開, 或者他們還可以創(chuàng)建Java類并作為和其它ORB一起展開的CORBA對象的客戶。后一種方法提供了另外一種途徑,通過它Java可以被用于將你的新的應 用和legacy系統(tǒng)相集成。 字串4
十、Java Transaction Architecture (JTA)/Java Transaction Service (JTS)
JTA定義了一種標準的API,應用系統(tǒng)由此可以存取各種事務監(jiān)控。JTS是CORBA OTS事務監(jiān)控的基本的實現(xiàn)。JTS規(guī)定了事務管理器的實現(xiàn)方式。該事務管理器是在高層支持Java Transaction API (JTA)規(guī)范,并且在較底層實現(xiàn)OMG OTS specification的Java映像。JTS事務管理器為應用服務器、資源管理器、獨立的應用以及通信資源管理器提供了事務服務。
十一、JavaMail and JavaBeans Activation Framework
JavaMail是用于存取郵件服務器的API,它提供了一套郵件服務器的抽象類。僅支持SMTP服務器,也支持IMAP服務器。JavaMail 利用JavaBeans Activation Framework (JAF)來處理MIME-編碼的郵件附件。MIME的字節(jié)流可以被轉(zhuǎn)換成Java對象,或者轉(zhuǎn)換自Java對象。由此大多數(shù)應用都可以不需要直接使用 JAF。
十二、Java Messaging Service (JMS)
JMS是用于和面向消息的中間件相互通信的應用程序接口(API)。它既支持點對點的域,有支持發(fā)布/訂閱(publish/subscribe) 類型的域,并且提供對下列類型的支持:經(jīng)認可的消息傳遞,事務型消息的傳遞,一致性消息和具有持久性的訂閱者支持。JMS還提供了另一種方式來對您的應用 與legacy backend系統(tǒng)相集成。
十三、Extensible Markup Language (XML) 字串7
XML是一種可以用來定義其它標記語言的語言。它被用來在不同的商務過程中共享數(shù)據(jù)。XML的發(fā)展和Java是相互獨立的,但是,它和Java具有 的相同目標正是平臺獨立性。通過將Java和XML的組合,您可以得到一個完美的具有平臺獨立性的解決方案。目前正有許多不同的公司在為Java和XML 的組合而努力。如果要了解更多的這方面的信息,可以訪問Sun的Java-XML頁面,或者IBM developerWorks的XML Zone。
posted @
2010-02-21 16:28 junly 閱讀(341) |
評論 (0) |
編輯 收藏