想必用過(guò)
Spring的程序員們都有這樣的感覺(jué),Spring把邏輯層封裝的太完美了(個(gè)人感覺(jué)View層封裝的不是很好)。以至于有的初學(xué)者都不知道Spring配置文件的意思,就拿來(lái)用了。所以今天我給大家詳細(xì)解釋一下Spring的applicationContext.xml
文件。Ok,我還是通過(guò)代碼加注釋的方式為大家演示:
以下是詳解Spring的applicationContext.xml文件代碼:
<!-- 頭文件,主要注意一下編碼 -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- 建立數(shù)據(jù)源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<!-- 數(shù)據(jù)庫(kù)驅(qū)動(dòng),我這里使用的是Mysql數(shù)據(jù)庫(kù) -->
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<!-- 數(shù)據(jù)庫(kù)地址,這里也要注意一下編碼,不然亂碼可是很郁悶的哦! -->
<property name="url">
<value>
jdbc:mysql://localhost:3306/tie?useUnicode=true&characterEncoding=utf-8
</value>
</property>
<!-- 數(shù)據(jù)庫(kù)的用戶名 -->
<property name="username">
<value>root</value>
</property>
<!-- 數(shù)據(jù)庫(kù)的密碼 -->
<property name="password">
<value>123</value>
</property>
</bean>
<!-- 把數(shù)據(jù)源注入給Session工廠 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<!-- 配置映射文件 -->
<property name="mappingResources">
<list>
<value>com/alonely/vo/User.hbm.xml</value>
</list>
</property>
</bean>
<!-- 把Session工廠注入給hibernateTemplate -->
<!-- 解釋一下hibernateTemplate:hibernateTemplate提供了很多方便的方法,在執(zhí)行時(shí)自動(dòng)建立 HibernateCallback 對(duì)象,例如:load()、get()、save、delete()等方法。 -->
<bean id="hibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<constructor-arg>
<ref local="sessionFactory" />
</constructor-arg>
</bean>
<!-- 把DAO注入給Session工廠 -->
<bean id="userDAO" class="com.alonely.dao.UserDAO">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<!-- 把Service注入給DAO -->
<bean id="userService" class="com.alonely.service.UserService">
<property name="userDAO">
<ref local="userDAO" />
</property>
</bean>
<!-- 把Action注入給Service -->
<bean name="/user" class="com.alonely.struts.action.UserAction">
<property name="userService">
<ref bean="userService" />
</property>
</bean>
</beans> |
以上Spring的applicationContext.xml文件我是用的SSH架構(gòu),如果您用Spring的
MVC架構(gòu),其原理也是一樣的。
一:理解多線程
多線程是這樣一種機(jī)制,它允許在程序中并發(fā)執(zhí)行多個(gè)指令流,每個(gè)指令流都稱為一個(gè)線程,彼此間互相獨(dú)立。
線程又稱為輕量級(jí)進(jìn)程,它和進(jìn)程一樣擁有獨(dú)立的執(zhí)行控制,由操作系統(tǒng)負(fù)責(zé)調(diào)度,區(qū)別在于線程沒(méi)有獨(dú)立的存儲(chǔ)空間,而是和所屬進(jìn)程中的其它線程共享一個(gè)存儲(chǔ)空間,這使得線程間的通信遠(yuǎn)較進(jìn)程簡(jiǎn)單。
多個(gè)線程的執(zhí)行是并發(fā)的,也就是在邏輯上“同時(shí)”,而不管是否是物理上的“同時(shí)”。如果系統(tǒng)只有一個(gè)CPU,那么真正的“同時(shí)”是不可能的,但是由于CPU的速度非常快,用戶感覺(jué)不到其中的區(qū)別,因此我們也不用關(guān)心它,只需要設(shè)想各個(gè)線程是同時(shí)執(zhí)行即可。
多線程和傳統(tǒng)的單線程在程序設(shè)計(jì)上最大的區(qū)別在于,由于各個(gè)線程的控制流彼此獨(dú)立,使得各個(gè)線程之間的代碼是亂序執(zhí)行的,由此帶來(lái)的線程調(diào)度,同步等問(wèn)題,將在以后探討。
二:在Java中實(shí)現(xiàn)多線程
我們不妨設(shè)想,為了創(chuàng)建一個(gè)新的線程,我們需要做些什么?很顯然,我們必須指明這個(gè)線程所要執(zhí)行的代碼,而這就是在Java中實(shí)現(xiàn)多線程我們所需要做的一切!
真是神奇!Java是如何做到這一點(diǎn)的?通過(guò)類!作為一個(gè)完全面向?qū)ο蟮恼Z(yǔ)言,Java提供了類 java.lang.Thread 來(lái)方便多線程編程,這個(gè)類提供了大量的方法來(lái)方便我們控制自己的各個(gè)線程,我們以后的討論都將圍繞這個(gè)類進(jìn)行。
那么如何提供給 Java 我們要線程執(zhí)行的代碼呢?讓我們來(lái)看一看 Thread 類。Thread 類最重要的方法是 run() ,它為Thread 類的方法 start() 所調(diào)用,提供我們的線程所要執(zhí)行的代碼。為了指定我們自己的代碼,只需要覆蓋它!
方法一:繼承 Thread 類,覆蓋方法 run()
我們?cè)趧?chuàng)建的 Thread 類的子類中重寫 run() ,加入線程所要執(zhí)行的代碼即可。
下面是一個(gè)例子:
public class MyThread extends Thread {
int count= 1, number;
public MyThread(int num) {
number = num;
System.out.println("創(chuàng)建線程 " + number);
}
public void run() {
while(true) {
System.out.println("線程 " + number + ":計(jì)數(shù) " + count);
if(++count== 6) return;
}
}
public static void main(String args[]) {
for(int i = 0; i < 5; i++) new MyThread(i+1).start();
}
}
這種方法簡(jiǎn)單明了,符合大家的習(xí)慣,但是,它也有一個(gè)很大的缺點(diǎn),那就是如果我們的類已經(jīng)從一個(gè)類繼承(如小程序必須繼承自 Applet 類),則無(wú)法再繼承 Thread 類,這時(shí)如果我們又不想建立一個(gè)新的類,應(yīng)該怎么辦呢?
我們不妨來(lái)探索一種新的方法:我們不創(chuàng)建 Thread 類的子類,而是直接使用它,那么我們只能將我們的方法作為參數(shù)傳遞給 Thread 類的實(shí)例,有點(diǎn)類似回調(diào)函數(shù)。但是 Java 沒(méi)有指針,我們只能傳遞一個(gè)包含這個(gè)方法的類的實(shí)例。那么如何限制這個(gè)類必須包含這一方法呢?當(dāng)然是使用接口!(雖然抽象類也可滿足,但是需要繼承,而我們之所以要采用這種新方法,不就是為了避免繼承帶來(lái)的限制嗎?)
Java 提供了接口 java.lang.Runnable 來(lái)支持這種方法。
方法二:實(shí)現(xiàn) Runnable 接口
Runnable 接口只有一個(gè)方法 run(),我們聲明自己的類實(shí)現(xiàn) Runnable 接口并提供這一方法,將我們的線程代碼寫入其中,就完成了這一部分的任務(wù)。
但是 Runnable 接口并沒(méi)有任何對(duì)線程的支持,我們還必須創(chuàng)建 Thread 類的實(shí)例,這一點(diǎn)通過(guò) Thread 類的構(gòu)造函數(shù)
public Thread(Runnable target);來(lái)實(shí)現(xiàn)。
下面是一個(gè)例子:
public class MyThread implements Runnable {
int count= 1, number;
public MyThread(int num) {
number = num;
System.out.println("創(chuàng)建線程 " + number);
}
public void run() {
while(true) {
System.out.println("線程 " + number + ":計(jì)數(shù) " + count);
if(++count== 6) return;
}
}
public static void main(String args[]) {
for(int i = 0; i < 5; i++) new Thread(new MyThread(i+1)).start();
}
}
嚴(yán)格地說(shuō),創(chuàng)建 Thread 子類的實(shí)例也是可行的,但是必須注意的是,該子類必須沒(méi)有覆蓋 Thread 類的 run 方法,否則該線程執(zhí)行的將是子類的 run 方法,而不是我
們用以實(shí)現(xiàn)Runnable 接口的類的 run 方法,對(duì)此大家不妨試驗(yàn)一下。
使用 Runnable 接口來(lái)實(shí)現(xiàn)多線程使得我們能夠在一個(gè)類中包容所有的代碼,有利于封裝,它的缺點(diǎn)在于,我們只能使用一套代碼,若想創(chuàng)建多個(gè)線程并使各個(gè)線程執(zhí)行不同的代碼,則仍必須額外創(chuàng)建類,如果這樣的話,在大多數(shù)情況下也許還不如直接用多個(gè)類分別繼承 Thread 來(lái)得緊湊。
綜上所述,兩種方法各有千秋,大家可以靈活運(yùn)用。
下面讓我們一起來(lái)研究一下多線程使用中的一些問(wèn)題。
三:線程的四種狀態(tài)
1. 新?tīng)顟B(tài):線程已被創(chuàng)建但尚未執(zhí)行(start() 尚未被調(diào)用)。
2. 可執(zhí)行狀態(tài):線程可以執(zhí)行,雖然不一定正在執(zhí)行。CPU 時(shí)間隨時(shí)可能被分配給該線程,從而使得它執(zhí)行。
3. 死亡狀態(tài):正常情況下 run() 返回使得線程死亡。調(diào)用 stop()或 destroy() 亦有同樣效果,但是不被推薦,前者會(huì)產(chǎn)生異常,后者是強(qiáng)制終止,不會(huì)釋放鎖。
4. 阻塞狀態(tài):線程不會(huì)被分配 CPU 時(shí)間,無(wú)法執(zhí)行。
四:線程的優(yōu)先級(jí)
線程的優(yōu)先級(jí)代表該線程的重要程度,當(dāng)有多個(gè)線程同時(shí)處于可執(zhí)行狀態(tài)并等待獲得 CPU 時(shí)間時(shí),線程調(diào)度系統(tǒng)根據(jù)各個(gè)線程的優(yōu)先級(jí)來(lái)決定給誰(shuí)分配 CPU 時(shí)間,優(yōu)先級(jí)高的線程有更大的機(jī)會(huì)獲得 CPU 時(shí)間,優(yōu)先級(jí)低的線程也不是沒(méi)有機(jī)會(huì),只是機(jī)會(huì)要小一些罷了。
你可以調(diào)用 Thread 類的方法 getPriority() 和 setPriority()來(lái)存取線程的優(yōu)先級(jí),線程的優(yōu)先級(jí)界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之間,缺省是5(NORM_PRIORITY)。
五:線程的同步
由于同一進(jìn)程的多個(gè)線程共享同一片存儲(chǔ)空間,在帶來(lái)方便的同時(shí),也帶來(lái)了訪問(wèn)沖突這個(gè)嚴(yán)重的問(wèn)題。Java語(yǔ)言提供了專門機(jī)制以解決這種沖突,有效避免了同一個(gè)數(shù)據(jù)對(duì)象被多個(gè)線程同時(shí)訪問(wèn)。
由于我們可以通過(guò) private 關(guān)鍵字來(lái)保證數(shù)據(jù)對(duì)象只能被方法訪問(wèn),所以我們只需針對(duì)方法提出一套機(jī)制,這套機(jī)制就是 synchronized 關(guān)鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。
1. synchronized 方法:通過(guò)在方法聲明中加入 synchronized關(guān)鍵字來(lái)聲明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制對(duì)類成員變量的訪問(wèn):每個(gè)類實(shí)例對(duì)應(yīng)一把鎖,每個(gè) synchronized 方法都必須獲得調(diào)用該方法的類實(shí)例的鎖方能執(zhí)行,否則所屬線程阻塞,方
法一旦執(zhí)行,就獨(dú)占該鎖,直到從該方法返回時(shí)才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進(jìn)入可執(zhí)行狀態(tài)。這種機(jī)制確保了同一時(shí)刻對(duì)于每一個(gè)類實(shí)例,其所有聲明為 synchronized 的成員函數(shù)中至多只有一個(gè)處于可執(zhí)行狀態(tài)(因?yàn)橹炼嘀挥幸粋€(gè)能夠獲得該類實(shí)例對(duì)應(yīng)的鎖),從而有效避免了類成員變量的訪問(wèn)沖突(只要所有可能訪問(wèn)類成員變量的方法均被聲明為 synchronized)。
在 Java 中,不光是類實(shí)例,每一個(gè)類也對(duì)應(yīng)一把鎖,這樣我們也可將類的靜態(tài)成員函數(shù)聲明為 synchronized ,以控制其對(duì)類的靜態(tài)成員變量的訪問(wèn)。
synchronized 方法的缺陷:若將一個(gè)大的方法聲明為synchronized 將會(huì)大大影響效率,典型地,若將線程類的方法 run() 聲明為 synchronized ,由于在線程的整個(gè)生命期內(nèi)它一直在運(yùn)行,因此將導(dǎo)致它對(duì)本類任何 synchronized 方法的調(diào)用都永遠(yuǎn)不會(huì)成功。當(dāng)然我們可以通過(guò)將訪問(wèn)類成員變量的代碼放到專門的方法中,將其聲明為 synchronized ,并在主方法中調(diào)用來(lái)解決這一問(wèn)題,但是 Java 為我們提供了更好的解決辦法,那就是 synchronized 塊。
2. synchronized 塊:通過(guò) synchronized關(guān)鍵字來(lái)聲明synchronized 塊。語(yǔ)法如下:
synchronized(syncObject) {
//允許訪問(wèn)控制的代碼
}
synchronized 塊是這樣一個(gè)代碼塊,其中的代碼必須獲得對(duì)象 syncObject (如前所述,可以是類實(shí)例或類)的鎖方能執(zhí)行,具體機(jī)制同前所述。由于可以針對(duì)任意代碼塊,且可任意指定上鎖的對(duì)象,故靈活性較高。
六:線程的阻塞
為了解決對(duì)共享存儲(chǔ)區(qū)的訪問(wèn)沖突,Java 引入了同步機(jī)制,現(xiàn)在讓我們來(lái)考察多個(gè)線程對(duì)共享資源的訪問(wèn),顯然同步機(jī)制已經(jīng)不夠了,因?yàn)樵谌我鈺r(shí)刻所要求的資源不一定已經(jīng)準(zhǔn)備好了被訪問(wèn),反過(guò)來(lái),同一時(shí)刻準(zhǔn)備好了的資源也可能不止一個(gè)。為了解決這種情況下的訪問(wèn)控制問(wèn)題,Java 引入了對(duì)阻塞機(jī)制的支持。
阻塞指的是暫停一個(gè)線程的執(zhí)行以等待某個(gè)條件發(fā)生(如某資源就緒),學(xué)過(guò)操作系統(tǒng)的同學(xué)對(duì)它一定已經(jīng)很熟悉了。Java 提供了大量方法來(lái)支持阻塞,下面讓我們逐一分析。
1. sleep() 方法:sleep() 允許 指定以毫秒為單位的一段時(shí)間作為參數(shù),它使得線程在指定的時(shí)間內(nèi)進(jìn)入阻塞狀態(tài),不能得到CPU 時(shí)間,指定的時(shí)間一過(guò),線程重新進(jìn)入可執(zhí)行狀態(tài)。
典型地,sleep() 被用在等待某個(gè)資源就緒的情形:測(cè)試發(fā)現(xiàn)條件不滿足后,讓線程阻塞一段時(shí)間后重新測(cè)試,直到條件滿足為止。
2. suspend() 和 resume() 方法:兩個(gè)方法配套使用,suspend()使得線程進(jìn)入阻塞狀態(tài),并且不會(huì)自動(dòng)恢復(fù),必須其對(duì)應(yīng)的resume() 被調(diào)用,才能使得線程重新進(jìn)入可執(zhí)行狀態(tài)。典型地,suspend() 和 resume() 被用在等待另一個(gè)線程產(chǎn)生的結(jié)果的情形:測(cè)試發(fā)現(xiàn)結(jié)果還沒(méi)有產(chǎn)生后,讓線程阻塞,另一個(gè)線程產(chǎn)生了結(jié)果后,調(diào)用 resume() 使其恢復(fù)。
3. yield() 方法:yield() 使得線程放棄當(dāng)前分得的 CPU 時(shí)間,但是不使線程阻塞,即線程仍處于可執(zhí)行狀態(tài),隨時(shí)可能再次分得 CPU 時(shí)間。調(diào)用 yield() 的效果等價(jià)于調(diào)度程序認(rèn)為該線程已執(zhí)行了足夠的時(shí)間從而轉(zhuǎn)到另一個(gè)線程。
4. wait() 和 notify() 方法:兩個(gè)方法配套使用,wait() 使得線程進(jìn)入阻塞狀態(tài),它有兩種形式,一種允許 指定以毫秒為單位的一段時(shí)間作為參數(shù),另一種沒(méi)有參數(shù),前者當(dāng)對(duì)應(yīng)的 notify() 被調(diào)用或者超出指定時(shí)間時(shí)線程重新進(jìn)入可執(zhí)行狀態(tài),后者則必須對(duì)應(yīng)的 notify() 被調(diào)用。
初看起來(lái)它們與 suspend() 和 resume() 方法對(duì)沒(méi)有什么分別,但是事實(shí)上它們是截然不同的。區(qū)別的核心在于,前面敘述的所有方法,阻塞時(shí)都不會(huì)釋放占用的鎖(如果占用了的話),而這一對(duì)方法則相反。
上述的核心區(qū)別導(dǎo)致了一系列的細(xì)節(jié)上的區(qū)別。
首先,前面敘述的所有方法都隸屬于 Thread 類,但是這一對(duì)卻直接隸屬于 Object 類,也就是說(shuō),所有對(duì)象都擁有這一對(duì)方法。初看起來(lái)這十分不可思議,但是實(shí)際上卻是很自然的,因?yàn)檫@一對(duì)方法阻塞時(shí)要釋放占用的鎖,而鎖是任何對(duì)象都具有的,調(diào)用任意對(duì)象的 wait() 方法導(dǎo)致線程阻塞,并且該對(duì)象上的鎖被釋放。而調(diào)用 任意對(duì)象的notify()方法則導(dǎo)致因調(diào)用該對(duì)象的 wait() 方法而阻塞的線程中隨機(jī)選擇的一個(gè)解除阻塞(但要等到獲得鎖后才真正可執(zhí)行)。
其次,前面敘述的所有方法都可在任何位置調(diào)用,但是這一對(duì)方法卻必須在 synchronized 方法或塊中調(diào)用,理由也很簡(jiǎn)單,只有在synchronized 方法或塊中當(dāng)前線程才占有鎖,才有鎖可以釋放。同樣的道理,調(diào)用這一對(duì)方法的對(duì)象上的鎖必須為當(dāng)前線程所擁有,這樣才有鎖可以釋放。因此,這一對(duì)方法調(diào)用必須放置在這樣的 synchronized 方法或塊中,該方法或塊的上鎖對(duì)象就是調(diào)用這一對(duì)方法的對(duì)象。若不滿足這一條件,則程序雖然仍能編譯,但在運(yùn)行時(shí)會(huì)出現(xiàn)IllegalMonitorStateException 異常。
wait() 和 notify() 方法的上述特性決定了它們經(jīng)常和synchronized 方法或塊一起使用,將它們和操作系統(tǒng)的進(jìn)程間通信機(jī)制作一個(gè)比較就會(huì)發(fā)現(xiàn)它們的相似性:synchronized方法或塊提供了類似于操作系統(tǒng)原語(yǔ)的功能,它們的執(zhí)行不會(huì)受到多線程機(jī)制的干擾,而這一對(duì)方法則相當(dāng)于 block 和wakeup 原語(yǔ)(這一對(duì)方法均聲明為 synchronized)。它們的結(jié)合使得我們可以實(shí)現(xiàn)操作系統(tǒng)上一系列精妙的進(jìn)程間通信的算法(如信號(hào)量算法),并用于解決各種復(fù)雜的線程間通信問(wèn)題。
關(guān)于 wait() 和 notify() 方法最后再說(shuō)明兩點(diǎn):
第一:調(diào)用 notify() 方法導(dǎo)致解除阻塞的線程是從因調(diào)用該對(duì)象的 wait() 方法而阻塞的線程中隨機(jī)選取的,我們無(wú)法預(yù)料哪一個(gè)線程將會(huì)被選擇,所以編程時(shí)要特別小心,避免因這種不確定性而產(chǎn)生問(wèn)題。
第二:除了 notify(),還有一個(gè)方法 notifyAll() 也可起到類似作用,唯一的區(qū)別在于,調(diào)用 notifyAll() 方法將把因調(diào)用該對(duì)象的 wait() 方法而阻塞的所有線程一次性全部解除阻塞。當(dāng)然,只有獲得鎖的那一個(gè)線程才能進(jìn)入可執(zhí)行狀態(tài)。
談到阻塞,就不能不談一談死鎖,略一分析就能發(fā)現(xiàn),suspend() 方法和不指定超時(shí)期限的 wait() 方法的調(diào)用都可能產(chǎn)生死鎖。遺憾的是,Java 并不在語(yǔ)言級(jí)別上支持死鎖的避免,我們?cè)诰幊讨斜仨毿⌒牡乇苊馑梨i。
以上我們對(duì) Java 中實(shí)現(xiàn)線程阻塞的各種方法作了一番分析,我們重點(diǎn)分析了 wait() 和 notify() 方法,因?yàn)樗鼈兊墓δ茏顝?qiáng)大,使用也最靈活,但是這也導(dǎo)致了它們的效率較低,較容易出錯(cuò)。實(shí)際使用中我們應(yīng)該靈活使用各種方法,以便更好地達(dá)到我們的目的。
七:守護(hù)線程
守護(hù)線程是一類特殊的線程,它和普通線程的區(qū)別在于它并不是應(yīng)用程序的核心部分,當(dāng)一個(gè)應(yīng)用程序的所有非守護(hù)線程終止運(yùn)行時(shí),即使仍然有守護(hù)線程在運(yùn)行,應(yīng)用程序也將終止,反之,只要有一個(gè)非守護(hù)線程在運(yùn)行,應(yīng)用程序就不會(huì)終止。守護(hù)線程一般被用于在后臺(tái)為其它線程提供服務(wù)。
可以通過(guò)調(diào)用方法 isDaemon() 來(lái)判斷一個(gè)線程是否是守護(hù)線程,也可以調(diào)用方法 setDaemon() 來(lái)將一個(gè)線程設(shè)為守護(hù)線程。
八:線程組
線程組是一個(gè) Java 特有的概念,在 Java 中,線程組是類ThreadGroup 的對(duì)象,每個(gè)線程都隸屬于唯一一個(gè)線程組,這個(gè)線程組在線程創(chuàng)建時(shí)指定并在線程的整個(gè)生命期內(nèi)都不能更改。你可以通過(guò)調(diào)用包含 ThreadGroup 類型參數(shù)的 Thread 類構(gòu)造函數(shù)來(lái)指定線程屬的線程組,若沒(méi)有指定,則線程缺省地隸屬于名為 system 的系統(tǒng)線程組。
在 Java 中,除了預(yù)建的系統(tǒng)線程組外,所有線程組都必須顯式創(chuàng)建。
在 Java 中,除系統(tǒng)線程組外的每個(gè)線程組又隸屬于另一個(gè)線程組,你可以在創(chuàng)建線程組時(shí)指定其所隸屬的線程組,若沒(méi)有指定,則缺省地隸屬于系統(tǒng)線程組。這樣,所有線程組組成了一棵以系統(tǒng)線程組為根的樹。
Java 允許我們對(duì)一個(gè)線程組中的所有線程同時(shí)進(jìn)行操作,比如我們可以通過(guò)調(diào)用線程組的相應(yīng)方法來(lái)設(shè)置其中所有線程的優(yōu)先級(jí),也可以啟動(dòng)或阻塞其中的所有線程。
Java 的線程組機(jī)制的另一個(gè)重要作用是線程安全。線程組機(jī)制允許我們通過(guò)分組來(lái)區(qū)分有不同安全特性的線程,對(duì)不同組的線程進(jìn)行不同的處理,還可以通過(guò)線程組的分層結(jié)構(gòu)來(lái)支持不對(duì)等安全措施的采用。Java 的 ThreadGroup 類提供了大量的方法來(lái)方便我們對(duì)線程組樹中的每一個(gè)線程組以及線程組中的每一個(gè)線程進(jìn)行操作。
九:總結(jié)
在這一講中,我們一起學(xué)習(xí)了 Java 多線程編程的方方面面,包括創(chuàng)建線程,以及對(duì)多個(gè)線程進(jìn)行調(diào)度、管理。我們深刻認(rèn)識(shí)到了多線程編程的復(fù)雜性,以及線程切換開銷帶來(lái)的多線程程序的低效性,這也促使我們認(rèn)真地思考一個(gè)問(wèn)題:我們是否需要多線程?何時(shí)需要多線程?
多線程的核心在于多個(gè)代碼塊并發(fā)執(zhí)行,本質(zhì)特點(diǎn)在于各代碼塊之間的代碼是亂序執(zhí)行的。我們的程序是否需要多線程,就是要看這是否也是它的內(nèi)在特點(diǎn)。
假如我們的程序根本不要求多個(gè)代碼塊并發(fā)執(zhí)行,那自然不需要使用多線程;假如我們的程序雖然要求多個(gè)代碼塊并發(fā)執(zhí)行,但是卻不要求亂序,則我們完全可以用一個(gè)循環(huán)來(lái)簡(jiǎn)單高效地實(shí)現(xiàn),也不需要使用多線程;只有當(dāng)它完全符合多線程的特點(diǎn)時(shí),多線程機(jī)制對(duì)線程間通信和線程管理的強(qiáng)大支持才能有用武之地,這時(shí)使用多線程才是值得的。
1、使用JdbcTemplate的execute()方法執(zhí)行SQL語(yǔ)句
- jdbcTemplate.execute("CREATE TABLE USER (user_id integer, name varchar(100))");
jdbcTemplate.execute("CREATE TABLE USER (user_id integer, name varchar(100))");
2、如果是UPDATE或INSERT,可以用update()方法。
- jdbcTemplate.update("INSERT INTO USER VALUES('"
- + user.getId() + "', '"
- + user.getName() + "', '"
- + user.getSex() + "', '"
- + user.getAge() + "')");
jdbcTemplate.update("INSERT INTO USER VALUES('"
+ user.getId() + "', '"
+ user.getName() + "', '"
+ user.getSex() + "', '"
+ user.getAge() + "')");
3、帶參數(shù)的更新
- jdbcTemplate.update("UPDATE USER SET name = ? WHERE user_id = ?", new Object[] {name, id});
jdbcTemplate.update("UPDATE USER SET name = ? WHERE user_id = ?", new Object[] {name, id});
- jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)", new Object[] {user.getId(), user.getName(), user.getSex(), user.getAge()});
jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)", new Object[] {user.getId(), user.getName(), user.getSex(), user.getAge()});
4、使用JdbcTemplate進(jìn)行查詢時(shí),使用queryForXXX()等方法
- int count = jdbcTemplate.queryForInt("SELECT COUNT(*) FROM USER");
int count = jdbcTemplate.queryForInt("SELECT COUNT(*) FROM USER");
- String name = (String) jdbcTemplate.queryForObject("SELECT name FROM USER WHERE user_id = ?", new Object[] {id}, java.lang.String.class);
String name = (String) jdbcTemplate.queryForObject("SELECT name FROM USER WHERE user_id = ?", new Object[] {id}, java.lang.String.class);
- List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
- List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
- Iterator it = rows.iterator();
- while(it.hasNext()) {
- Map userMap = (Map) it.next();
- System.out.print(userMap.get("user_id") + "\t");
- System.out.print(userMap.get("name") + "\t");
- System.out.print(userMap.get("sex") + "\t");
- System.out.println(userMap.get("age") + "\t");
- }
List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
Iterator it = rows.iterator();
while(it.hasNext()) {
Map userMap = (Map) it.next();
System.out.print(userMap.get("user_id") + "\t");
System.out.print(userMap.get("name") + "\t");
System.out.print(userMap.get("sex") + "\t");
System.out.println(userMap.get("age") + "\t");
}
JdbcTemplate將我們使用的JDBC的流程封裝起來(lái),包括了異常的捕捉、SQL的執(zhí)行、查詢結(jié)果的轉(zhuǎn)換等等。spring大量使用Template Method模式來(lái)封裝固定流程的動(dòng)作,XXXTemplate等類別都是基于這種方式的實(shí)現(xiàn)。
除了大量使用Template Method來(lái)封裝一些底層的操作細(xì)節(jié),spring也大量使用callback方式類回調(diào)相關(guān)類別的方法以提供JDBC相關(guān)類別的功能,使傳統(tǒng)的JDBC的使用者也能清楚了解spring所提供的相關(guān)封裝類別方法的使用。
JDBC的PreparedStatement
- final String id = user.getId();
- final String name = user.getName();
- final String sex = user.getSex() + "";
- final int age = user.getAge();
-
- jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)",
- new PreparedStatementSetter() {
- public void setValues(PreparedStatement ps) throws SQLException {
- ps.setString(1, id);
- ps.setString(2, name);
- ps.setString(3, sex);
- ps.setInt(4, age);
- }
- });
final String id = user.getId();
final String name = user.getName();
final String sex = user.getSex() + "";
final int age = user.getAge();
jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)",
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, id);
ps.setString(2, name);
ps.setString(3, sex);
ps.setInt(4, age);
}
});
- final User user = new User();
- jdbcTemplate.query("SELECT * FROM USER WHERE user_id = ?",
- new Object[] {id},
- new RowCallbackHandler() {
- public void processRow(ResultSet rs) throws SQLException {
- user.setId(rs.getString("user_id"));
- user.setName(rs.getString("name"));
- user.setSex(rs.getString("sex").charAt(0));
- user.setAge(rs.getInt("age"));
- }
- });
final User user = new User();
jdbcTemplate.query("SELECT * FROM USER WHERE user_id = ?",
new Object[] {id},
new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
user.setId(rs.getString("user_id"));
user.setName(rs.getString("name"));
user.setSex(rs.getString("sex").charAt(0));
user.setAge(rs.getInt("age"));
}
});
- class UserRowMapper implements RowMapper {
- public Object mapRow(ResultSet rs, int index) throws SQLException {
- User user = new User();
-
- user.setId(rs.getString("user_id"));
- user.setName(rs.getString("name"));
- user.setSex(rs.getString("sex").charAt(0));
- user.setAge(rs.getInt("age"));
-
- return user;
- }
- }
-
- public List findAllByRowMapperResultReader() {
- String sql = "SELECT * FROM USER";
- return jdbcTemplate.query(sql, new RowMapperResultReader(new UserRowMapper()));
- }
class UserRowMapper implements RowMapper {
public Object mapRow(ResultSet rs, int index) throws SQLException {
User user = new User();
user.setId(rs.getString("user_id"));
user.setName(rs.getString("name"));
user.setSex(rs.getString("sex").charAt(0));
user.setAge(rs.getInt("age"));
return user;
}
}
public List findAllByRowMapperResultReader() {
String sql = "SELECT * FROM USER";
return jdbcTemplate.query(sql, new RowMapperResultReader(new UserRowMapper()));
}
在getUser(id)里面使用UserRowMapper
- public User getUser(final String id) throws DataAccessException {
- String sql = "SELECT * FROM USER WHERE user_id=?";
- final Object[] params = new Object[] { id };
- List list = jdbcTemplate.query(sql, params, new RowMapperResultReader(new UserRowMapper()));
-
- return (User) list.get(0);
- }
public User getUser(final String id) throws DataAccessException {
String sql = "SELECT * FROM USER WHERE user_id=?";
final Object[] params = new Object[] { id };
List list = jdbcTemplate.query(sql, params, new RowMapperResultReader(new UserRowMapper()));
return (User) list.get(0);
}
網(wǎng)上收集
org.springframework.jdbc.core.PreparedStatementCreator 返回預(yù)編譯SQL 不能于Object[]一起用
- public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
- return con.prepareStatement(sql);
- }
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
return con.prepareStatement(sql);
}
1.增刪改
org.springframework.jdbc.core.JdbcTemplate 類(必須指定數(shù)據(jù)源dataSource)
- template.update("insert into web_person values(?,?,?)",Object[]);
template.update("insert into web_person values(?,?,?)",Object[]);
或
- template.update("insert into web_person values(?,?,?)",new PreparedStatementSetter(){ 匿名內(nèi)部類 只能訪問(wèn)外部最終局部變量
-
- public void setValues(PreparedStatement ps) throws SQLException {
- ps.setInt(index++,3);
- });
template.update("insert into web_person values(?,?,?)",new PreparedStatementSetter(){ 匿名內(nèi)部類 只能訪問(wèn)外部最終局部變量
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(index++,3);
});
org.springframework.jdbc.core.PreparedStatementSetter 接口 處理預(yù)編譯SQL
- public void setValues(PreparedStatement ps) throws SQLException {
- ps.setInt(index++,3);
- }
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(index++,3);
}
2.查詢JdbcTemplate.query(String,[Object[]/PreparedStatementSetter],RowMapper/RowCallbackHandler)
org.springframework.jdbc.core.RowMapper 記錄映射接口 處理結(jié)果集
- public Object mapRow(ResultSet rs, int arg1) throws SQLException { int表當(dāng)前行數(shù)
- person.setId(rs.getInt("id"));
- }
- List template.query("select * from web_person where id=?",Object[],RowMapper);
public Object mapRow(ResultSet rs, int arg1) throws SQLException { int表當(dāng)前行數(shù)
person.setId(rs.getInt("id"));
}
List template.query("select * from web_person where id=?",Object[],RowMapper);
org.springframework.jdbc.core.RowCallbackHandler 記錄回調(diào)管理器接口 處理結(jié)果集
- template.query("select * from web_person where id=?",Object[],new RowCallbackHandler(){
- public void processRow(ResultSet rs) throws SQLException {
- person.setId(rs.getInt("id"));
- });
在現(xiàn)實(shí)工作中,我們常常需要保存一些系統(tǒng)配置信息,大家一般都會(huì)選擇配置文件來(lái)完成,本文根據(jù)筆者工作中用到的讀取配置文件的方法小小總結(jié)一下,主要敘述的是spring讀取配置文件的方法。
一.讀取xml配置文件
(一)新建一個(gè)java bean(HelloBean.java)
java 代碼
- package chb.demo.vo;
-
- public class HelloBean {
- private String helloWorld;
-
- public String getHelloWorld() {
- return helloWorld;
- }
-
- public void setHelloWorld(String helloWorld) {
- this.helloWorld = helloWorld;
- }
- }
-
(二)構(gòu)造一個(gè)配置文件(beanConfig.xml)
xml 代碼
- xml version="1.0" encoding="UTF-8"?>
- >
- <beans>
- <bean id="helloBean" class="chb.demo.vo.HelloBean">
- <property name="helloWorld">
- <value>Hello!chb!value>
- property>
- bean>
- beans>
(三)讀取xml文件
1.利用ClassPathXmlApplicationContext
java 代碼
- ApplicationContext context = new ClassPathXmlApplicationContext("beanConfig.xml");
- HelloBean helloBean = (HelloBean)context.getBean("helloBean");
- System.out.println(helloBean.getHelloWorld());
2.利用FileSystemResource讀取
java 代碼
- Resource rs = new FileSystemResource("D:/software/tomcat/webapps/springWebDemo/WEB-INF/classes/beanConfig.xml");
- BeanFactory factory = new XmlBeanFactory(rs);
- HelloBean helloBean = (HelloBean)factory.getBean("helloBean");\
- System.out.println(helloBean.getHelloWorld());
值得注意的是:利用FileSystemResource,則配置文件必須放在project直接目錄下,或者寫明絕對(duì)路徑,否則就會(huì)拋出找不到文件的異常
二.讀取properties配置文件
這里介紹兩種技術(shù):利用spring讀取properties 文件和利用java.util.Properties讀取
(一)利用spring讀取properties 文件
我們還利用上面的HelloBean.java文件,構(gòu)造如下beanConfig.properties文件:
properties 代碼
- helloBean.class=chb.demo.vo.HelloBean
- helloBean.helloWorld=Hello!chb!
屬性文件中的"helloBean"名稱即是Bean的別名設(shè)定,.class用于指定類來(lái)源。
然后利用org.springframework.beans.factory.support.PropertiesBeanDefinitionReader來(lái)讀取屬性文件
java 代碼
- BeanDefinitionRegistry reg = new DefaultListableBeanFactory();
- PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(reg);
- reader.loadBeanDefinitions(new ClassPathResource("beanConfig.properties"));
- BeanFactory factory = (BeanFactory)reg;
- HelloBean helloBean = (HelloBean)factory.getBean("helloBean");
- System.out.println(helloBean.getHelloWorld());
(二)利用java.util.Properties讀取屬性文件
比如,我們構(gòu)造一個(gè)ipConfig.properties來(lái)保存服務(wù)器ip地址和端口,如:
properties 代碼
則,我們可以用如下程序來(lái)獲得服務(wù)器配置信息:
java 代碼
- InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("ipConfig.properties");
- Properties p = new Properties();
- try {
- p.load(inputStream);
- } catch (IOException e1) {
- e1.printStackTrace();
- }
- System.out.println("ip:"+p.getProperty("ip")+",port:"+p.getProperty("port"));
本文只介紹了一些簡(jiǎn)單操作,不當(dāng)之處希望大家多多指教
一、數(shù)據(jù)庫(kù)名
數(shù)據(jù)庫(kù)名是數(shù)據(jù)庫(kù)的“身份證號(hào)碼”,用于標(biāo)示一個(gè)數(shù)據(jù)庫(kù)。在參數(shù)文件中用DB_NAME表示。
數(shù)據(jù)庫(kù)名是在安裝數(shù)據(jù)庫(kù)、創(chuàng)建新的數(shù)據(jù)庫(kù)、創(chuàng)建數(shù)據(jù)庫(kù)控制文件、修改數(shù)據(jù)庫(kù)結(jié)構(gòu)、備份與恢復(fù)數(shù)據(jù)庫(kù)時(shí)都需要使用到的。
如何查看數(shù)據(jù)庫(kù)名呢?方式有三:
·使用SQL語(yǔ)句:select name from v$database;
·使用show命令:show parameter db_name;
·查看參數(shù)文件:查看init.ora文件
二、數(shù)據(jù)庫(kù)實(shí)例名:
數(shù)據(jù)庫(kù)實(shí)例名是用于和操作系統(tǒng)進(jìn)行聯(lián)系的標(biāo)識(shí),也就是說(shuō)數(shù)據(jù)庫(kù)和操作系統(tǒng)之間的交互使用的是數(shù)據(jù)庫(kù)實(shí)例名。
實(shí)例名也被寫入?yún)?shù)文件中,該參數(shù)為instance_name,在winnt平臺(tái)中,實(shí)例名同時(shí)也被寫入注冊(cè)表。
數(shù)據(jù)庫(kù)名和實(shí)例名可以相同也可以不同。在一般情況下,數(shù)據(jù)庫(kù)名和實(shí)例名是一對(duì)一的關(guān)系,但如果在oracle并行服務(wù)器架構(gòu)(即oracle實(shí)時(shí)應(yīng)用集群)中,數(shù)據(jù)庫(kù)名和實(shí)例名是一對(duì)多的關(guān)系。
如何查看當(dāng)前數(shù)據(jù)庫(kù)實(shí)例名呢?方式有三:
·使用SQL語(yǔ)句:select instance_name from v$instance;
·使用show命令:show parameter instance
·查看參數(shù)文件:查看init.ora文件
數(shù)據(jù)庫(kù)實(shí)例名與ORACLE_SID兩者都表示oracle實(shí)例,但是有區(qū)別的。instance_name是oracle數(shù)據(jù)庫(kù)參數(shù)。而ORACLE_SID是操作系統(tǒng)的環(huán)境變量。ORACLD_SID用于與操作系統(tǒng)交互,也就是說(shuō),從操作系統(tǒng)的角度訪問(wèn)實(shí)例名,必須通過(guò)ORACLE_SID。
ORACLE_SID必須與instance_name的值一致。否則,你將會(huì)收到一個(gè)錯(cuò)誤。在unix平臺(tái),是“ORACLE not available”,在winnt平臺(tái),是“TNS:協(xié)議適配器錯(cuò)誤”。
三、數(shù)據(jù)庫(kù)域名與全局?jǐn)?shù)據(jù)庫(kù)名
隨著由多個(gè)數(shù)據(jù)庫(kù)構(gòu)成的分布式數(shù)據(jù)庫(kù)的普及,這種命令數(shù)據(jù)庫(kù)的方法給數(shù)據(jù)庫(kù)的管理造成一定的負(fù)擔(dān),因?yàn)楦鱾€(gè)數(shù)據(jù)庫(kù)的名字可能一樣,造成管理上的混亂。
為了解決這種情況,引入了Db_domain參數(shù),這樣在數(shù)據(jù)庫(kù)的標(biāo)識(shí)是由Db_name(數(shù)據(jù)庫(kù)名)和 Db_domain(數(shù)據(jù)庫(kù)域名)兩個(gè)參數(shù)共同決定的,避免了因?yàn)閿?shù)據(jù)庫(kù)重名而造成管理上的混亂。這類似于互連網(wǎng)上的機(jī)器名的管理。
我們將Db_name和 Db_domain兩個(gè)參數(shù)用‘.’連接起來(lái),表示一個(gè)數(shù)據(jù)庫(kù),并將該數(shù)據(jù)庫(kù)的名稱稱為Global_name(全局?jǐn)?shù)據(jù)庫(kù)名),即它擴(kuò)展了Db_name。Db_name參數(shù)只能由字母、數(shù)字、’_’、’#’、’$’組成,而且最多8個(gè)字符。
對(duì)一個(gè)網(wǎng)絡(luò)數(shù)據(jù)庫(kù)(Oracle database)的唯一標(biāo)識(shí),oracle建議用此種方法命令數(shù)據(jù)庫(kù)。該值是在創(chuàng)建數(shù)據(jù)庫(kù)是決定的,缺省值為Db_name. Db_domain。在以后對(duì)參數(shù)文件中Db_name與Db_domain參數(shù)的任何修改不影響Global_name的值,如果要修改 Global_name,只能用ALTER DATABASE RENAME GLOBAL_NAME TO 命令進(jìn)行修改,然后修改相應(yīng)參數(shù)。
如何查詢數(shù)據(jù)庫(kù)域名呢?方法有三:
·使用SQL命令:select value from v$parameter where name = ´db_domain´;
·使用show命令:show parameter domain
·查看參數(shù)文件:在參數(shù)文件中查詢。
四、數(shù)據(jù)庫(kù)服務(wù)名
該參數(shù)是oracle8i新引進(jìn)的。在8i以前,我們用SID來(lái)表示標(biāo)識(shí)數(shù)據(jù)庫(kù)的一個(gè)實(shí)例,但是在Oracle的并行環(huán)境中,一個(gè)數(shù)據(jù)庫(kù)對(duì)應(yīng)多個(gè)實(shí)例,這樣就需要多個(gè)網(wǎng)絡(luò)服務(wù)名,設(shè)置繁瑣。為了方便并行環(huán)境中的設(shè)置,引進(jìn)了Service_name參數(shù)。該參數(shù)對(duì)應(yīng)一個(gè)數(shù)據(jù)庫(kù),而不是一個(gè)實(shí)例。
該參數(shù)的缺省值為Db_name. Db_domain,即等于Global_name。如果數(shù)據(jù)庫(kù)有域名,則數(shù)據(jù)庫(kù)服務(wù)名就是全局?jǐn)?shù)據(jù)庫(kù)名;否則,數(shù)據(jù)庫(kù)服務(wù)名與數(shù)據(jù)庫(kù)名相同。
如何查詢數(shù)據(jù)庫(kù)服務(wù)名呢?方法有三:
·使用SQL語(yǔ)句:select value from v$parameter where name = ´service_name´;
·使用show命令:show parameter service_name
·查看參數(shù)文件:在參數(shù)文件中查詢。
從oracle8i開如的oracle網(wǎng)絡(luò)組件,數(shù)據(jù)庫(kù)與客戶端的連接主機(jī)串使用數(shù)據(jù)庫(kù)服務(wù)名。之前用的是ORACLE_SID,即數(shù)據(jù)庫(kù)實(shí)例名。
五、網(wǎng)絡(luò)服務(wù)名
網(wǎng)絡(luò)服務(wù)名,又可以稱為數(shù)據(jù)庫(kù)別名(database alias)。是客戶端程序訪問(wèn)數(shù)據(jù)庫(kù)時(shí)所需要,屏蔽了客戶端如何連接到服務(wù)器端的細(xì)節(jié),實(shí)現(xiàn)了數(shù)據(jù)庫(kù)的位置透明的特性。網(wǎng)絡(luò)服務(wù)名被記錄在tnsnames.ora文件中。
網(wǎng)絡(luò)服務(wù)名是從客戶端的角度出發(fā),當(dāng)客戶端連接遠(yuǎn)程數(shù)據(jù)庫(kù)或其他服務(wù)時(shí),可以指定Net服務(wù)名。因此需要使用一個(gè)或多個(gè)命名方法將此Net服務(wù)名解析為連接數(shù)據(jù)庫(kù)或其他服務(wù)的連接描述符。
通常選擇的是[本地]-將存儲(chǔ)在本地客戶機(jī)的tnsnames.ora文件中的網(wǎng)絡(luò)服務(wù)名解析為連接描述符。
[Oracle Names]-由Oracle名字服務(wù)器提供為網(wǎng)絡(luò)上的每個(gè)Oracle Net服務(wù)提供解析方法
[主機(jī)名]-通過(guò)TCP/IP環(huán)境中的主機(jī)別名連接到Oracle數(shù)據(jù)庫(kù)服務(wù)
[Sun NIS]/[DCE CDS]-專用系統(tǒng)用的,在Windows 2000系統(tǒng)環(huán)境下不適用
六、總結(jié)
Oracle中各種命名的比較名稱查詢方式
名稱
|
查詢方式
|
DB_NAME |
select name from v$database |
INSTANCE_NAME |
select instance_name from v$instance |
ORACLE_SID |
值和INSTANCE_NAME相同 |
DB_DOMAIN |
select value from v$parameter where name="db_domain" |
GLOBAL_NAME |
DB_NAME.DB_DOMAIN |
SERVICE_NAME |
select value from v$parameter where name="service_name" |
NET_SERVICE_NAME |
檢查tnsnames.ora文件 |
統(tǒng)一建模語(yǔ)言(Unified Modeling Language,UML)
一些術(shù)語(yǔ):
系統(tǒng)(system)指的是硬件和軟件的結(jié)合體,它能提供業(yè)務(wù)問(wèn)題的解決方案。
系統(tǒng)開發(fā)(system development)是為客戶建立一個(gè)系統(tǒng)的過(guò)程。
客戶(client)是需要解決問(wèn)題的人。
系統(tǒng)分析員(analyst)將客戶所要解決的問(wèn)題編制成文檔,并將該文檔轉(zhuǎn)交給開發(fā)人員。
開發(fā)人員(developer)是為了及決客戶的問(wèn)題而構(gòu)造軟件并在計(jì)算機(jī)硬件上實(shí)施該軟件的程序員。
UML的組成
UML包括了一些可以相互組合圖表的圖形元素。
1.類圖
一個(gè)類(class)是一類或一組具有類似屬性和共同行為的事物。
矩形方框代表類的圖標(biāo),它被分成3個(gè)區(qū)域。最上面的區(qū)域中是類名,中間區(qū)域是類的屬性,最下面區(qū)域里列的是類的操作。
舉一個(gè)例子,屬于洗衣機(jī)(washing machine)類的事物都具有諸如品牌(brand name)、型號(hào)(model name)、序列號(hào)(serial number)和容量(capacity)等屬性。這類事物的行為包括“加衣物(add clothes)”、“加洗滌劑(add detergent)”、“開機(jī)(turn on)”和“取出衣物(remove clothes)”等操作。

2.對(duì)象圖
對(duì)象(object)是一個(gè)類的實(shí)例,是具有具體屬性值和行為的一個(gè)具體事物。例如,洗衣機(jī)的品牌可能是“Laundatorium”,型號(hào)為“Washmeister”,序列號(hào)為“GL57774”,一次最多可以洗滌重量為16磅的衣物。
對(duì)象的圖標(biāo)也是一個(gè)矩形,和類的圖標(biāo)一樣,但是對(duì)象名下面要帶下劃線。具體實(shí)例的名字位于冒號(hào)的左邊而該實(shí)例所屬的類名位于冒號(hào)的右邊。

3.用例圖
用例(use case)是從用戶的觀點(diǎn)對(duì)系統(tǒng)行為的一個(gè)描述。
例,一個(gè)人使用一臺(tái)洗衣機(jī),顯然是為了洗衣服(wash clothes)。
代表洗衣機(jī)用戶的智力小人形被稱為參與者(actor)。橢圓形代表用例。
參與者(它是發(fā)起用例的實(shí)體)可以是一個(gè)人也可以是另一個(gè)系統(tǒng)。

4.狀態(tài)圖
一臺(tái)洗衣機(jī)可以處于浸泡(soak)、洗滌(Wash)、漂洗(Rinse)、脫水(Spin)或者關(guān)機(jī)(off)狀態(tài)。

最頂端的符號(hào)代表起始狀態(tài),最底端的符號(hào)表示終止?fàn)顟B(tài)。
5.順序圖
類圖和對(duì)象圖標(biāo)大的實(shí)系統(tǒng)的靜態(tài)結(jié)構(gòu)。在一個(gè)運(yùn)行的系統(tǒng)中,對(duì)象之間要發(fā)生交互,并且這些交互要經(jīng)歷一定的時(shí)間。UML順序圖所表達(dá)的正式這種基于時(shí)間的動(dòng)態(tài)交互。
例,洗衣機(jī)的構(gòu)件包括一個(gè)注水的進(jìn)水管(Water Pipe)、一個(gè)用來(lái)裝衣物的洗滌缸(Drum)以一個(gè)排水管(Drain)。假設(shè)已經(jīng)完成了“加衣物”、“加洗滌劑”和“開機(jī)”操作。洗衣服這個(gè)用例被執(zhí)行時(shí)按照如下順序進(jìn)行:
1通過(guò)進(jìn)水管想洗滌缸中注水。
2洗滌缸保持5分鐘靜止?fàn)顟B(tài)。
3水注滿,停止注水。
4洗滌缸往返旋轉(zhuǎn)15分鐘。
5通過(guò)排水管排掉洗滌后的臟水。
6重新開始注水。
7洗滌缸繼續(xù)往返旋轉(zhuǎn)洗滌。
8停止向洗衣機(jī)中注水。
9通過(guò)排水管排掉漂洗衣物的水。
10洗滌缸加快速度單方向旋轉(zhuǎn)5分鐘。
11洗滌缸停止旋轉(zhuǎn),洗衣過(guò)程結(jié)束。
圖中,對(duì)象之間發(fā)送的消息有:注入新水(Send fresh water)、保持靜止(Remain stationary)、停止注水(Stop)、往返旋轉(zhuǎn)(Ratate back and forth)、排掉洗滌后的臟水(Send soapy water)、排掉漂洗過(guò)的水(Send rinse water)等。

6.活動(dòng)圖
用例和對(duì)象的行為中的各個(gè)活動(dòng)之間通常具有時(shí)間順序。

7.協(xié)作圖
系統(tǒng)的工作目標(biāo)是由系統(tǒng)中各組成元素相互協(xié)作完成的。例子中洗衣機(jī)構(gòu)件的類集中又增加了一個(gè)內(nèi)部計(jì)時(shí)器(Internal Timer)。在經(jīng)過(guò)一段時(shí)間后,定時(shí)器停止注水,然后啟動(dòng)洗滌缸往返旋轉(zhuǎn)。圖中的序號(hào)代表命令消息的發(fā)送順序。

8.構(gòu)件圖
構(gòu)件圖和部署圖和整個(gè)計(jì)算機(jī)系統(tǒng)密切相關(guān)。

9.部署圖
UML部署圖顯示了基于計(jì)算機(jī)系統(tǒng)的物理體系結(jié)構(gòu)。它可以描述計(jì)算機(jī)和設(shè)備,展示它們之間的連接,以及駐留在每臺(tái)機(jī)器中的軟件。每臺(tái)計(jì)算機(jī)用一個(gè)立方體來(lái)表示,立方體之間的連線表示這些計(jì)算機(jī)之間的通信關(guān)系。
如果需要將圖中的組織元素分組,或者在圖中說(shuō)明一些類或構(gòu)件是某個(gè)特定子系統(tǒng)的一部分,可以通過(guò)將這些元素組織成包(package)來(lái)達(dá)到此目的。包用一邊突起的公文夾形圖標(biāo)來(lái)表示。

注釋(note)的圖標(biāo)是一個(gè)帶折角的矩形,矩形框中是解釋性文字。注釋和被注釋的圖元素之間用一條虛線連接。

構(gòu)造型(stereotype)能夠使用現(xiàn)有的UML元素來(lái)定制新的元素。構(gòu)造型用尖對(duì)括號(hào)括起來(lái)的一個(gè)名稱來(lái)表示,這個(gè)括號(hào)叫雙尖括號(hào)(guillemets)。
v
是特定事件出現(xiàn)的時(shí)候,自動(dòng)執(zhí)行的代碼塊。類似于存儲(chǔ)過(guò)程,但是用戶不能直接調(diào)用他們。
功能:
1、 允許/限制對(duì)表的修改
2、 自動(dòng)生成派生列,比如自增字段
3、 強(qiáng)制數(shù)據(jù)一致性
4、 提供審計(jì)和日志記錄
5、 防止無(wú)效的事務(wù)處理
6、 啟用復(fù)雜的業(yè)務(wù)邏輯
開始
create trigger biufer_employees_department_id
before insert or update
of department_id
on employees
referencing old as old_value
new as new_value
for each row
when (new_value.department_id<>80 )
begin
:new_value.commission_pct :=0;
end;
/
觸發(fā)器的組成部分:
1、 觸發(fā)器名稱
2、 觸發(fā)語(yǔ)句
3、 觸發(fā)器限制
4、 觸發(fā)操作
1、 觸發(fā)器名稱
create trigger biufer_employees_department_id
命名習(xí)慣:
biufer(before insert update for each row)
employees 表名
department_id 列名
2、 觸發(fā)語(yǔ)句
比如:
表或視圖上的DML語(yǔ)句
DDL語(yǔ)句
數(shù)據(jù)庫(kù)關(guān)閉或啟動(dòng),startup shutdown 等等
before insert or update
of department_id
on employees
referencing old as old_value
new as new_value
for each row
說(shuō)明:
1、 無(wú)論是否規(guī)定了department_id ,對(duì)employees表進(jìn)行insert的時(shí)候
2、 對(duì)employees表的department_id列進(jìn)行update的時(shí)候
3、 觸發(fā)器限制
when (new_value.department_id<>80 )
限制不是必須的。此例表示如果列department_id不等于80的時(shí)候,觸發(fā)器就會(huì)執(zhí)行。
其中的new_value是代表更新之后的值。
4、 觸發(fā)操作
是觸發(fā)器的主體
begin
:new_value.commission_pct :=0;
end;
主體很簡(jiǎn)單,就是將更新后的commission_pct列置為0
觸發(fā):
insert into employees(employee_id,
last_name,first_name,hire_date,job_id,email,department_id,salary,commission_pct )
values( 12345,’Chen’,’Donny’, sysdate, 12,
‘donny@hotmail.com’,60,10000,.25);
select commission_pct from employees where employee_id=12345;
觸發(fā)器不會(huì)通知用戶,便改變了用戶的輸入值。
觸發(fā)器類型:
1、 語(yǔ)句觸發(fā)器
2、 行觸發(fā)器
3、 INSTEAD OF 觸發(fā)器
4、 系統(tǒng)條件觸發(fā)器
5、 用戶事件觸發(fā)器
1、 語(yǔ)句觸發(fā)器
是在表上或者某些情況下的視圖上執(zhí)行的特定語(yǔ)句或者語(yǔ)句組上的觸發(fā)器。能夠與INSERT、UPDATE、DELETE或者組合上進(jìn)行關(guān)聯(lián)。但是無(wú)論使用什么樣的組合,各個(gè)語(yǔ)句觸發(fā)器都只會(huì)針對(duì)指定語(yǔ)句激活一次。比如,無(wú)論update多少行,也只會(huì)調(diào)用一次update語(yǔ)句觸發(fā)器。
例子:
需要對(duì)在表上進(jìn)行DML操作的用戶進(jìn)行安全檢查,看是否具有合適的特權(quán)。
Create table foo(a number);
Create trigger biud_foo
Before insert or update or delete
On foo
Begin
If user not in (‘DONNY’) then
Raise_application_error(-20001, ‘You don’t have
access to modify this table.’);
End if;
End;
/
即使SYS,SYSTEM用戶也不能修改foo表
[試驗(yàn)]
對(duì)修改表的時(shí)間、人物進(jìn)行日志記錄。
1、 建立試驗(yàn)表
create table employees_copy as select *from hr.employees
2、 建立日志表
create table employees_log(
who varchar2(30),
when date);
3、 在employees_copy表上建立語(yǔ)句觸發(fā)器,在觸發(fā)器中填充employees_log 表。
Create or replace trigger biud_employee_copy
Before insert or update or delete
On employees_copy
Begin
Insert into employees_log(
Who,when)
Values( user, sysdate);
End;
/
4、 測(cè)試
update employees_copy set salary= salary*1.1;
select *from employess_log;
5、 確定是哪個(gè)語(yǔ)句起作用?
即是INSERT/UPDATE/DELETE中的哪一個(gè)觸發(fā)了觸發(fā)器?
可以在觸發(fā)器中使用INSERTING / UPDATING / DELETING 條件謂詞,作判斷:
begin
if inserting then
-----
elsif updating then
-----
elsif deleting then
------
end if;
end;
if updating(‘COL1’) or updating(‘COL2’) then
------
end if;
[試驗(yàn)]
1、 修改日志表
alter table employees_log
add (action varchar2(20));
2、 修改觸發(fā)器,以便記錄語(yǔ)句類型。
Create or replace trigger biud_employee_copy
Before insert or update or delete
On employees_copy
Declare
L_action employees_log.action%type;
Begin
if inserting then
l_action:=’Insert’;
elsif updating then
l_action:=’Update’;
elsif deleting then
l_action:=’Delete’;
else
raise_application_error(-20001,’You should never ever get this error.’);
Insert into employees_log(
Who,action,when)
Values( user, l_action,sysdate);
End;
/
3、 測(cè)試
insert into employees_copy( employee_id, last_name, email, hire_date, job_id)
values(12345,’Chen’,’Donny@hotmail’,sysdate,12);
select *from employees_log
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1711633
下面就是例子程序
--明細(xì)表打印予處理 通用報(bào)表:
procedure mx_print_common(pd_id in mx_pd_syn.pd_id%type,
p_pd_mxb_id IN mx_pd_mxb_syn.p_mxb_id%type,
p_dept_no IN sc_mxk.dept_code%type,
p1 sc_bz_syn.bz_code%type,
p2 sc_cjjc_syn.cjjc_code%type,
p3 sc_mxk.warehouse_num%type)
is
sql2 varchar2(500); --存儲(chǔ)查詢語(yǔ)句
sql3 varchar2(500); --存儲(chǔ)查詢條件
str1 sc_print_syn.a%type; --存儲(chǔ)車間進(jìn)程
str2 sc_print_syn.b%type; --存儲(chǔ)班組(工藝、工序)進(jìn)程
s_ip sc_print_syn.ip%type;
type cursor_type is ref cursor;
c1 cursor_type;
type record_type is record(
pbom_id sc_mxk.pbom_id%type
);
r_c1 record_type;
/*
注意上面紅色的兩行和藍(lán)色的兩行
紅色的兩行定義一個(gè)游標(biāo)
藍(lán)色的兩行定義一個(gè)游標(biāo)中將要返回的數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)
*/
cursor c2(p_pbom_id sc_mxk.pbom_id%type) is
select a.dd_count,b.gx_name,c.bz_name,d.cjjc_name
from sc_p_gx_syn a,sc_gx_syn b,sc_bz_syn c,sc_cjjc_syn d
where pbom_id = p_pbom_id
and a.gx_code=b.gx_code(+) and b.dept_code=p_dept_no
and a.bz_code=c.bz_code(+) and b.dept_code=p_dept_no
and a.cjjc_code=d.cjjc_code(+) and b.dept_code=p_dept_no;
r_c2 c2%rowtype;
BEGIN
s_ip :=sys_context('USERENV','IP_ADDRESS');
delete from sc_print_syn where ip=s_ip and p_id=pd_id;
commit;
--下面開始構(gòu)造查詢語(yǔ)句
sql2:='select distinct a.pbom_id from sc_mxk a';
sql3:=' where a.p_id=' || pd_id || ' and a.dept_code= ''' || p_dept_no || '''';
if p_pd_mxb_id >0 then
sql2:=sql3 || ',mxk c ';
sql3:=sql3 || ' and c.m_mxb_id= ' || p_pd_mxb_id || ' and a.mxb_id = c.mxb_id';
end if;
if p1 is not null then
sql2:=sql2 || ',sc_p_gx_syn b';
sql3:=sql3 || ' and a.pbom_id=b.pbom_id and b.bz_code = ''' || p1 || '''';
end if;
if p2 is not null then
sql2:=sql2 || ',sc_p_gx_syn b';
sql3:=sql3 || ' and a.pbom_id=b.pbom_id and b.cjjc_code = ''' || p2 || '''';
end if;
if p3 is not null then
sql3:=sql3 || ' and a.warehouse_num = ''' || p3 || '''';
end if;
sql2:=sql2 || sql3;
--打開動(dòng)態(tài)游標(biāo),再往下就都一樣了
open c1 for sql2;
loop
fetch c1 into r_c1;
exit when c1%notfound;
str1:='';
str2:='';
--打開工序表進(jìn)行處理
open c2(r_c1.pbom_id);
loop
fetch c2 into r_c2;
exit when c2%notfound; --沒(méi)有記錄退出
if r_c2.cjjc_name is not null then
str1 :=str1 || to_char(r_c2.cjjc_name);
end if;
if r_c2.bz_name is not null then
str2 := str2 || r_c2.bz_name || to_char(r_c2.dd_count);
elsif r_c2.gx_name is not null then
str2 := str2 || to_char(r_c2.gx_name) || to_char(r_c2.dd_count);
end if;
end loop;
close c2;
insert into sc_print_syn(a,b,ip,p_id,r_id)
values(str1,str2,s_ip,pd_id,r_c1.pbom_id);
COMMIT;
end loop;
close c1;
END mx_print_common;
當(dāng)然,實(shí)現(xiàn)的方法一定很多,甚至可以用隱式游標(biāo)。但是隱式游標(biāo)中用動(dòng)態(tài)查詢語(yǔ)句也要費(fèi)一些周折的。
作者:Northsnow
電子郵件:northsnow@163.com
blog:http://blog.csdn.net/precipitant
Log4j有三個(gè)主要的組件:Loggers,Appenders和Layouts,這里可簡(jiǎn)單理解為日志類別,日志要輸出的地方和日志以何種形式輸出。綜合使用這三個(gè)組件可以輕松的記錄信息的類型和級(jí)別,并可以在運(yùn)行時(shí)控制日志輸出的樣式和位置。下面對(duì)三個(gè)組件分別進(jìn)行說(shuō)明:
1、 Loggers
Loggers組件在此系統(tǒng)中被分為五個(gè)級(jí)別:DEBUG、INFO、WARN、ERROR和FATAL。這五個(gè)級(jí)別是有順序的,DEBUG < INFO < WARN < ERROR < FATAL,明白這一點(diǎn)很重要,這里L(fēng)og4j有一個(gè)規(guī)則:假設(shè)Loggers級(jí)別為P,如果在Loggers中發(fā)生了一個(gè)級(jí)別Q比P高,則可以啟動(dòng),否則屏蔽掉。
Java程序舉例來(lái)說(shuō):
//建立Logger的一個(gè)實(shí)例,命名為“com.foo”
Logger logger = Logger.getLogger("com.foo");
//設(shè)置logger的級(jí)別。通常不在程序中設(shè)置logger的級(jí)別。一般在配置文件中設(shè)置。
logger.setLevel(Level.INFO);
Logger barlogger = Logger.getLogger("com.foo.Bar");
//下面這個(gè)請(qǐng)求可用,因?yàn)閃ARN >= INFO
logger.warn("Low fuel level.");
//下面這個(gè)請(qǐng)求不可用,因?yàn)镈EBUG < INFO
logger.debug("Starting search for nearest gas station.");
//命名為“com.foo.bar”的實(shí)例barlogger會(huì)繼承實(shí)例“com.foo”的級(jí)別。因此,下面這個(gè)請(qǐng)求可用,因?yàn)镮NFO >= INFO
barlogger.info("Located nearest gas station.");
//下面這個(gè)請(qǐng)求不可用,因?yàn)镈EBUG < INFO
barlogger.debug("Exiting gas station search");
這里“是否可用”的意思是能否輸出Logger信息。
在對(duì)Logger實(shí)例進(jìn)行命名時(shí),沒(méi)有限制,可以取任意自己感興趣的名字。一般情況下建議以類的所在位置來(lái)命名Logger實(shí)例,這是目前來(lái)講比較有效的Logger命名方式。這樣可以使得每個(gè)類建立自己的日志信息,便于管理。比如:
static Logger logger = Logger.getLogger(ClientWithLog4j.class.getName());
2、 Appenders
禁用與使用日志請(qǐng)求只是Log4j其中的一個(gè)小小的地方,Log4j日志系統(tǒng)允許把日志輸出到不同的地方,如控制臺(tái)(Console)、文件(Files)、根據(jù)天數(shù)或者文件大小產(chǎn)生新的文件、以流的形式發(fā)送到其它地方等等。
其語(yǔ)法表示為:
org.apache.log4j.ConsoleAppender(控制臺(tái)),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天產(chǎn)生一個(gè)日志文件),org.apache.log4j.RollingFileAppender(文件大小到達(dá)指定尺寸的時(shí)候產(chǎn)生一個(gè)新的文件),
org.apache.log4j.WriterAppender(將日志信息以流格式發(fā)送到任意指定的地方)
配置時(shí)使用方式為:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
…
log4j.appender.appenderName.option = valueN
這樣就為日志的輸出提供了相當(dāng)大的便利。
3、 Layouts
有時(shí)用戶希望根據(jù)自己的喜好格式化自己的日志輸出。Log4j可以在Appenders的后面附加Layouts來(lái)完成這個(gè)功能。Layouts提供了四種日志輸出樣式,如根據(jù)HTML樣式、自由指定樣式、包含日志級(jí)別與信息的樣式和包含日志時(shí)間、線程、類別等信息的樣式等等。
其語(yǔ)法表示為:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以靈活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的級(jí)別和信息字符串),
org.apache.log4j.TTCCLayout(包含日志產(chǎn)生的時(shí)間、線程、類別等等信息)
配置時(shí)使用方式為:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
…
log4j.appender.appenderName.layout.option = valueN
以上是從原理方面說(shuō)明Log4j的使用方法,在具體Java編程使用Log4j可以參照以下示例:
1、 建立Logger實(shí)例:
語(yǔ)法表示:public static Logger getLogger( String name)
實(shí)際使用:static Logger logger = Logger.getLogger (ServerWithLog4j.class.getName ()) ;
2、 讀取配置文件:
獲得了Logger的實(shí)例之后,接下來(lái)將配置Log4j使用環(huán)境:
語(yǔ)法表示:
BasicConfigurator.configure():自動(dòng)快速地使用缺省Log4j環(huán)境。
PropertyConfigurator.configure(String configFilename):讀取使用Java的特性文件編寫的配置文件。
DOMConfigurator.configure(String filename):讀取XML形式的配置文件。
實(shí)際使用:PropertyConfigurator.configure("ServerWithLog4j.properties");
3、 插入日志信息
完成了以上連個(gè)步驟以后,下面就可以按日志的不同級(jí)別插入到你要記錄日志的任何地方了。
語(yǔ)法表示:
Logger.debug(Object message);
Logger.info(Object message);
Logger.warn(Object message);
Logger.error(Object message);
實(shí)際使用:logger.info("ServerSocket before accept: " + server);
在實(shí)際編程時(shí),要使Log4j真正在系統(tǒng)中運(yùn)行事先還要對(duì)配置文件進(jìn)行定義。定義步驟就是對(duì)Logger、Appender及Layout的分別使用,具體如下:
1、 配置根Logger,其語(yǔ)法為:
log4j.rootLogger = [ level ] , appenderName, appenderName, …
這里level指Logger的優(yōu)先級(jí),appenderName是日志信息的輸出地,可以同時(shí)指定多個(gè)輸出地。如:log4j.rootLogger= INFO,A1,A2
2、 配置日志信息輸出目的地,其語(yǔ)法為:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
可以指定上面所述五個(gè)目的地中的一個(gè)。
3、 配置日志信息的格式,其語(yǔ)法為:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
這里上面三個(gè)步驟是對(duì)前面Log4j組件說(shuō)明的一個(gè)簡(jiǎn)化;下面給出一個(gè)具體配置例子,在程序中可以參照?qǐng)?zhí)行:
log4j.rootLogger=INFO,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=
%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
這里需要說(shuō)明的就是日志信息格式中幾個(gè)符號(hào)所代表的含義:
-X號(hào): X信息輸出時(shí)左對(duì)齊;
%p: 日志信息級(jí)別
%d{}: 日志信息產(chǎn)生時(shí)間
%c: 日志信息所在地(類名)
%m: 產(chǎn)生的日志具體信息
%n: 輸出日志信息換行
根據(jù)上面的日志格式,某一個(gè)程序的輸出結(jié)果如下:
0 INFO 2003-06-13 13:23:46968 ClientWithLog4j Client socket: Socket[addr=localhost/127.0.0.1,port=8002,localport=2014]
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server says: 'Java server with log4j, Fri Jun 13 13:23:46 CST 2003'
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j GOOD
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Command 'HELLO' not understood.'
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j HELP
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Vocabulary: HELP QUIT'
16 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j QUIT
在
項(xiàng)目的開發(fā)過(guò)程中,系統(tǒng)對(duì)日志的要求很細(xì),而且要求分類清楚。所以還是采用了Log4J。
在強(qiáng)調(diào)可重用組件開發(fā)的今天,除了自己從頭到尾開發(fā)一個(gè)可重用的日志操作類外,Apache為我們提供了一個(gè)強(qiáng)有力的日志操作包-Log4j。
Log4j是Apache的一個(gè)開放源代碼項(xiàng)目,通過(guò)使用Log4j,我們可以控制日志信息輸送的目的地是控制臺(tái)、文件、GUI組件、甚至是套接口服務(wù)器、NT的事件記錄器、UNIX Syslog守護(hù)進(jìn)程等;我們也可以控制每一條日志的輸出格式;通過(guò)定義每一條日志信息的級(jí)別,我們能夠更加細(xì)致地控制日志的生成過(guò)程。最令人感興趣的就是,這些可以通過(guò)一個(gè)配置文件來(lái)靈活地進(jìn)行配置,而不需要修改應(yīng)用的代碼。
此外,通過(guò)Log4j其他語(yǔ)言接口,您可以在C、php、C++、.Net、PL/SQL程序中使用Log4j,其語(yǔ)法和用法與在Java程序中一樣,使得多語(yǔ)言分布式系統(tǒng)得到一個(gè)統(tǒng)一一致的日志組件模塊。而且,通過(guò)使用各種第三方擴(kuò)展,您可以很方便地將Log4j集成到J2EE、JINI甚至是SNMP應(yīng)用中。
Log4j配置文件詳細(xì)說(shuō)明(*.properties和*.xml)
屬性文件Properties
properties屬性文件
編號(hào) 配置項(xiàng) 配置項(xiàng)描述 示例
1 log4j.threshold 閾值項(xiàng)
log4j.threshold = error
2 log4j.rootLogger 根日志屬性項(xiàng)
log4j.rootLogger = info,stdout1,stdout2
3 log4j.category. 子日志屬性項(xiàng)(舊)
log4j.category.com.eos = NULL,stdout1
4 log4j.logger. 子日志屬性項(xiàng)(新)
log4j.logger.com.eos.log = debug,stdout2
5 log4j.additivity. appender是否繼承設(shè)置
log4j.additivity.com.eos = false
6 log4j.appender. 輸出目的地定義項(xiàng)
log4j.appender.stdout2 = org.apache.log4j.ConsoleAppender
7 log4j.appender.A.layout 輸出格式定義項(xiàng)
log4j.appender.stdout2.layout = org.apache.log4j.PatternLayout
xml文件
編號(hào) 配置項(xiàng) 配置項(xiàng)描述 示例
1 threshold 閾值項(xiàng)
2 root 根日志屬性項(xiàng)
3 priority 級(jí)別項(xiàng)(舊)
4 level 級(jí)別項(xiàng)(新)
5 category 子日志屬性項(xiàng)(舊)
6 logger 子日志屬性項(xiàng)(新)
7 appender-ref 輸出端控制項(xiàng)
8 additivity appender是否繼承設(shè)置
9 appender 輸出目的地定義項(xiàng)
10 layout 輸出格式定義項(xiàng)
詳細(xì)說(shuō)明(只針對(duì)Log4j常用的,用戶可以自定義)Appender
Appender繼承關(guān)系
Appender基本種類
org.apache.log4j.ConsoleAppender(控制臺(tái))
org.apache.log4j.FileAppender(文件)
org.apache.log4j.DailyRollingFileAppender(每天產(chǎn)生一個(gè)日志文件)
org.apache.log4j.RollingFileAppender(文件大小到達(dá)指定尺寸的時(shí)候產(chǎn)生一個(gè)新的文件)
org.apache.log4j.WriterAppender(將日志信息以流格式發(fā)送到任意指定的地方)
· ConsoleAppender選項(xiàng)
Threshold=WARN:指定日志消息的輸出最低層次。
ImmediateFlush=true:默認(rèn)值是true,意謂著所有的消息都會(huì)被立即輸出。
Target=System.err:默認(rèn)情況下是:System.out,指定輸出控制臺(tái)
· FileAppender 選項(xiàng)
Threshold=WARN:指定日志消息的輸出最低層次。
ImmediateFlush=true:默認(rèn)值是true,意謂著所有的消息都會(huì)被立即輸出。
File=mylog.txt:指定消息輸出到mylog.txt文件。
Append=false:默認(rèn)值是true,即將消息增加到指定文件中,false指將消息覆蓋指定的文件內(nèi)容。
· DailyRollingFileAppender 選項(xiàng)
Threshold=WARN:指定日志消息的輸出最低層次。
ImmediateFlush=true:默認(rèn)值是true,意謂著所有的消息都會(huì)被立即輸出。
File=mylog.txt:指定消息輸出到mylog.txt文件。
Append=false:默認(rèn)值是true,即將消息增加到指定文件中,false指將消息覆蓋指定的文件內(nèi)容。
DatePattern='.'yyyy-ww:每周滾動(dòng)一次文件,即每周產(chǎn)生一個(gè)新的文件。當(dāng)然也可以指定按月、周、
天、時(shí)和分。即對(duì)應(yīng)的格式如下:
1)'.'yyyy-MM: 每月
2)'.'yyyy-ww: 每周
3)'.'yyyy-MM-dd: 每天
4)'.'yyyy-MM-dd-a: 每天兩次
5)'.'yyyy-MM-dd-HH: 每小時(shí)
6)'.'yyyy-MM-dd-HH-mm: 每分鐘
n RollingFileAppender 選項(xiàng)
Threshold=WARN:指定日志消息的輸出最低層次。
ImmediateFlush=true:默認(rèn)值是true,意謂著所有的消息都會(huì)被立即輸出。
File=mylog.txt:指定消息輸出到mylog.txt文件。
Append=false:默認(rèn)值是true,即將消息增加到指定文件中,false指將消息覆蓋指定的文件內(nèi)容。
MaxFileSize=100KB: 后綴可以是KB, MB 或者是 GB. 在日志文件到達(dá)該大小時(shí),將會(huì)自動(dòng)滾動(dòng),即將原來(lái)
的內(nèi)容移到mylog.log.1文件。
MaxBackupIndex=2:指定可以產(chǎn)生的滾動(dòng)文件的最大數(shù)。
詳細(xì)說(shuō)明(只針對(duì)Log4j,用戶可以自定義)Layout
Log4j的Layout基本種類
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以靈活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的級(jí)別和信息字符串),
org.apache.log4j.TTCCLayout(包含日志產(chǎn)生的時(shí)間、線程、類別等等信息)
· HTMLLayout選項(xiàng)
LocationInfo=true:默認(rèn)值是false,輸出java文件名稱和行號(hào)
Title=my app file: 默認(rèn)值是 Log4J Log Messages.
n PatternLayout 選項(xiàng)
log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
這里需要說(shuō)明的就是日志信息格式中幾個(gè)符號(hào)所代表的含義:
%X: 信息輸出時(shí)左對(duì)齊;
%p: 輸出日志信息優(yōu)先級(jí),即DEBUG,INFO,WARN,ERROR,F(xiàn)ATAL,
%d: 輸出日志時(shí)間點(diǎn)的日期或時(shí)間,默認(rèn)格式為ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},輸出類似:2002年10月18日 22:10:28,921
%r: 輸出自應(yīng)用啟動(dòng)到輸出該log信息耗費(fèi)的毫秒數(shù)
%c: 輸出日志信息所屬的類目,通常就是所在類的全名
%t: 輸出產(chǎn)生該日志事件的線程名
%l: 輸出日志事件的發(fā)生位置,相當(dāng)于%C.%M(%F:%L)的組合,包括類目名、發(fā)生的線程,以及在代碼中的行數(shù)。舉例:Testlog4.main(TestLog4.java:10)
%x: 輸出和當(dāng)前線程相關(guān)聯(lián)的NDC(嵌套診斷環(huán)境),尤其用到像java servlets這樣的多客戶多線程的應(yīng)用中。
%%: 輸出一個(gè)"%"字符
%F: 輸出日志消息產(chǎn)生時(shí)所在的文件名稱
%L: 輸出代碼中的行號(hào)
%m: 輸出代碼中指定的消息,產(chǎn)生的日志具體信息
%n: 輸出一個(gè)回車換行符,Windows平臺(tái)為"\r\n",Unix平臺(tái)為"\n"輸出日志信息換行,可以在%與模式字符之間加上修飾符來(lái)控制其最小寬度、最大寬度、和文本的對(duì)齊方式。如:
1)%20c:指定輸出category的名稱,最小的寬度是20,如果category的名稱小于20的話,默認(rèn)的情況下右對(duì)齊。
2)%-20c:指定輸出category的名稱,最小的寬度是20,如果category的名稱小于20的話,"-"號(hào)指定左對(duì)齊。
3)%.30c:指定輸出category的名稱,最大的寬度是30,如果category的名稱大于30的話,就會(huì)將左邊多出的字符截掉,但小于30的話也不會(huì)有空格。
4)%20.30c:如果category的名稱小于20就補(bǔ)空格,并且右對(duì)齊,如果其名稱長(zhǎng)于30字符, 就從左邊交遠(yuǎn)銷出的字符截掉。
· XMLLayout 選項(xiàng)
LocationInfo=true:默認(rèn)值是false,輸出java文件和行號(hào)
日志配置文件內(nèi)容范例
log4j.properties
#注意:在屬性配置文件中,屬性值的第一個(gè)一定是級(jí)別,輸出端可有可無(wú),以逗號(hào)分割。(而xml文件格式?jīng)]有這種限制)
log4j.xml
- < xml version="1.0" encoding="UTF-8" >
- <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
- <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false" threshold="null">
-
-
- <appender class="org.apache.log4j.ConsoleAppender" name="CONSOLE">
- <param name="Target" value="System.out"/>
- <param name="Threshold" value="INFO"/>
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern" value="%d [%p] - %m%n "/>
- </layout>
- <filter class="org.apache.log4j.varia.DenyAllFilter"/>
- <errorHandler class="org.apache.log4j.varia. FallbackErrorHandler"/>
- </appender>
-
- <appender class="org.apache.log4j.FileAppender" name="FILE">
- <param name="File" value="file.log"/>
- <param name="Append" value="false"/>
- <param name="Threshold" value="INFO"/>
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern" value="%d [%p] - %m%n "/>
- </layout>
- </appender>
-
- <appender class="org.apache.log4j.RollingFileAppender" name="ROLLING_FILE">
- <param name="Threshold" value="INFO"/>
- <param name="File" value="rolling.log"/>
- <param name="Append" value="false"/>
- <param name="MaxFileSize" value="10KB"/>
- <param name="MaxBackupIndex" value="1"/>
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern" value="%d [%p] - %m%n "/>
- </layout>
- </appender>
-
- <logger additivity="false" name="com.eos">
- <level value="info"/>
- <appender-ref ref="CONSOLE"/>
- </logger>
-
- <category additivity="true" name="com.eos.log">
- <priority value="warn"/>
- </category>
-
- <root>
- <priority value="info"/>
- <appender-ref ref="CONSOLE"/>
- </root>
- </log4j:configuration>