1、 JavaWeb基礎javascript
第一天:css
1.Eclipse詳解:html
(1).Bad versionnumber in .class file:編譯器版本和運行(JRE)版本不符合。高的JRE版本兼容低版本的編譯器版本。前端
(2).當程序有錯誤的時候,使用Debug as 運行程序。雙擊語句設置斷點。程序運行到此處中止。點擊跳入方法的內部代碼。點擊跳過,執行下一條代碼,點擊跳出,跳出方法。觀察變量的值,選中變量右擊選擇watch. 跳入下一個斷點。查看斷點,調試完後必定要清除斷點。結束運行斷點的jvm.java
2.HashSet和hashCode和equals方法node
java系統首先調用對象的hashCode()方法得到該對象的哈希嗎,而後根據哈希嗎找到相應的存儲區域,最後取出該存儲區域內的每一個元素與該元素進行比較.兩個equals相等,hashCode()相等。須要重寫equals,hashCode()方法.更改數據的值,hashCode()的值也更改了,並未刪除.內存泄露.有個東西不在被用,可是未被刪除,致使內存泄露.mysql
3.Junit測試框架程序員
(1).在測試類,方法前加註解:@Test,否則出現初始化異常。web
(2).方法before,after前加@Before,@After註解。在測試方法以前和以後運行方法。正則表達式
(3).靜態方法beforeClass,afterClass方法前加上註解@BeforeClass,@AfterClass,類加載的時候運行
(4).Assert斷言。判斷兩個對象是否相等。指望值和實際值相等。
4.獲得配置文件的路徑
經過類加載器 reflect.class.getClassLoader.getResourceAsStream();在class指定目錄下查找指定的類文件進行加載.編譯器把src中的.java文件編譯成class文件,全部非.java文件,原封不動的搬過去.可是這種方法是隻讀的.
經過類的信息reflect.class.getResourceAsStream();相對路徑
通常用絕對路徑,使用配置文件告訴用戶路徑.
必定要記住要用完整的路徑,可是完整的路徑不是硬編碼的,是計算出來的.
5.反射
(1).反射主要用於框架開發
(2).一個類有多個組成部分,例如:成員變量,方法,構造方法等,反射就是加載類,並解析類的各個組成部分。
(3).加載類使用Class.forName()靜態方法,給類的完整名稱,包名和類名。
(4).Class提供瞭解析public的構造方法,方法,字段等方法以及private。字段封裝數據,方法執行功能
(5).靜態方法無需傳遞對象,method.invoke()
(6).升級時保持兼容性,main函數的解析有點麻煩,反射解析數組參變量的時候的問題。啓動Java程序的main方法的參數是一個字符串數組,即public static void main(String[] args),經過反射方式來調用這個main方法時,如何爲invoke方法傳遞參數呢?按照jdk1.5的語法,整個數組是一個參數,而按照jdk1.4的語法,數組中的每一個元素對應一個參數,當把一個字符串數組做爲參數傳遞給invoke方法時,javac會到底按照哪一種語法進行處理呢?jdk1.5確定要兼容jdk1.4的語法,會按jdk1.4的語法進行處理,即把數組打散成若干個單獨的參數。因此,在給main方法傳遞參數時,不能使用代碼mainMethod.invoke(null,newString[]{"xxx"}),javac只把它當作jdk1.4的語法進行理解,而不把它當作jdk1.5的語法解釋,所以會出現參數類型不對的問題。解決的方法:
mainMethod.invoke(null,newObject[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)newString[]{"xxx"});編譯器會作特殊處理,編譯時不把參數當作數組看待,也就不會把數組打散成若干個參數了.
(7).對數組進行反射:相同的維數(不是數組元素的個數,例如都是一維數組,不關心數組的大小),相同的8種基本數據類型時數組有相同的字節碼.
6. 泛型
(1).泛型是對象類型,不能是基本類型,泛型存在源代碼級別上,給編譯器看的,生成class文件就不存在泛型了
(2).參數類型變量,實際類型變量,泛型類型,參數化的類型
(3).自定義泛型方法:public <T> void method(T args){};public<T,K,V> void method(T a,K b,V c){};
(4).靜態方法public static <T> void method(T t){};泛型類不做用靜態方法
7.可變參數
(1).可變參數就當作數組,能夠使用加強for循環
(2).可變參數列表爲最後一個參數
(3).能夠給可變參數傳遞一個數組
(4).可變參數的類型是基本類型仍是對象類型
8.課程介紹
(1).在谷歌心目中,「雲」必須具有如下條件:數據都存在網上,而非終端裏,軟件最終消失,只要你的「雲」設備擁有瀏覽器就能夠運行如今的一切,「雲」時代的互聯網終端設備將不只僅是PC,手機,汽車,甚至手錶,只要有簡單的操做系統加個瀏覽器就徹底能夠實現,因爲數據都在「雲」端,企業的IT管理愈來愈簡單,企業和我的用戶也不一樣在擔憂病毒,數據丟失等問題。
(2).李開復描述了這樣一個場景,只要你的PC或手機等終端裏安裝了一個簡單的操做系統和完整功能的瀏覽器,開機後輸入本身的用戶名和密碼,你存在「雲」中的應用軟件和數據就會同步到終端裏。
9.快捷鍵
(1).配置快捷鍵:window->preferences->key
(2).Alt+/ :內容提示
Ctrl+1 :快速修復
Ctrl+Shift+O :快速導入包
Ctrl+Shift+F :格式化代碼
Alt+方向鍵 :跟蹤代碼
Ctrl+Shift+/ :添加註釋
Ctrl+Shift+\ :取消註釋
Ctrl+Shift+X :更改成大寫
Ctrl+Shift+Y :更改成小寫
Ctrl+Shift+向下鍵 :複製代碼
Ctrl+Shift+向上,向下 :改變代碼行的順序
Ctrl+T :查看繼承關係
Ctrl+Shift+T :查看源代碼
Ctrl+Shift+L :查看全部的快捷鍵
10.類加載器及其委託機制的深刻分析
(1).Java虛擬機中能夠安裝多個類加載器,系統默認三個主要類加載器,每一個類負責加載特定位置的類:BootStrap,ExtClassLoader,AppClassLoader
(2).類加載器也是Java類,由於其餘java類的加載器自己也要被類加載器加載,顯然必須有第一個類加載器不是java類,這正是BootStrap(內嵌到JVM的內核中,使用C++語言編寫的)
(3).Java虛擬機中的全部類裝載器採用具備父子關係的屬性結構進行組織,在實例化每一個類轉載器對象時,須要爲其指定一個父級類裝載器對象或者默認採用系統類裝載器爲其父級類加載器
(4).
public class ClassLoaderTest{
public static void main(String[] args){
System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
//輸出爲sun.misc.Lanuncher$AppClassLoader;
System.out.println(System.class.getClassLoader());
//輸出爲null,由於類System是由BootStrap加載的;
}
}
(5).BootStrap->ExtClassLoader->AppClassLoader
ClassLoader loader= ClassLoaderTest.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());
loader = loader.getParent();//往上順序打印
}
System.out.println(loader);//最後打印老祖宗
(6).
BootStrap------>JRE/lib/rt.jar
ExtClassLoader----->JRE/lib/ext/*.jar
AppClassLoader------>ClassPath指定的全部jar或目錄
用Eclipse的打包工具將ClassLoaderTest打包成itcast.jar,而後放在jre/lib/ext目錄下,在eclipse中運行這個類,運行結果顯示爲ExtClassLoader,此時的環境狀態是classpath目錄有ClassLoaderTest.class,ext/itcast.jar包中也有ClassLoaderTest.class,這時咱們在打印ClassLoaderTest類的類加載名稱,發現是ExtClassLoader,而不是AppClassLoader.
(7).類加載的委託機制:
當Java虛擬機要加載一個類時,到底派出哪一個類加載器去加載呢:
首先當前線程的類加載器去加載線程中的第一個類
若是類A中引用了類B,Java虛擬機將使用加載類A的類加載器來加載類B
還能夠直接調用ClassLoader.loadClass()方法來指定某個類加載器去加載某個類
每一個類加載器加載類時,又先委託給其上級類加載器
當全部祖宗類加載器沒有加載到類,回到發起者類加載器,還加載不了,則拋ClassNotFoundException,不是再去找發起者類加載器的兒子,由於沒有getChild方法,即便有,那麼多個兒子,找哪個呢?
對着類加載器的層次結構圖和委託加載原理
(8).Thread類有一個方法setContextClassLoader(ClassLoader classLoader)//加載當前的類.
(9).每一個類加載都首先會委託送到BootStrap,那麼BootStrap很累,這樣,那爲何很少搞幾個BootStrap呢,之因此不這樣作,是由於,便於統一管理,由於全部的類都會找BootStrap,可能這時有幾個相同的類進行加載,那麼BootStrap,不會屢次將他們的class文件加載內存中,只會加載一份便可.這樣效率就高了.
(10).
public class MyClassLoader{
public static void main(String[]args){
String srcPath=args[0];
String destDir=args[1];//獲得目錄
String destFileName =srcPath.substring(srcPath.lastIndexOf('/')+1);//獲得文件名
String destFilePath=destDir+"\\"+destFileName;
FileInputStream fis = newFileInputStream(srcPath);
FileOutputStream fos=new FileOutputStream(destPath);
cypher(fis,fos);
fis.close();
fos.close();
}
private static void cyp(InputStreamips,OutputStream ops){
int b =-1;
while((b=ips.read())!=-1){
ops.write(b^0xff);//對內容進行異或處理
}
}
}
class ClassLoader extends Date{
public String toString(){
return "hello,itcast";
}
}
args[0]:ClassLoader.class的絕對路徑
args[1]:itcastlib
有包名的類不能調用無包名的類.
(11).編寫本身的類加載器:
知識講解:
自定義的類加載器必須繼承ClassLoader(抽象類)
覆蓋findClass方法
defineClass方法:獲得class文件轉換成字節碼
編程步棸:
編寫一個文件內容進行簡單加密的程序
編寫了一個本身的類加載器,可實現對加密過的類進行裝載和解密
編寫一個程序調用類加載器加載類,在源程序中不能用該類名定義引用變量,由於編譯器沒法識別這個類,程序中能夠出了使用ClassLoader.load方法以外,還能夠使用設置線程的上下文類加載器或者系統類加載器,而後再使用Class.forName
(12).
模板設計模式:
父類--->loadClass(相同的)
子類1(本身乾的)
子類2(本身乾的)
覆蓋findClass方法(本身幹)
(13).
public class MyClassLoader extendsClassLoader{
public MyClassLoader(){
}
public MyClassLoader(String classDir){
this.classDir = classDir;
}
@Override
protected Class<?> findClass(Stringname){
String classFileName = classDir +"\\" + name + ".class";
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = newByteArrayOutputStream();
cypher(fis,bos);
fis.close();
byte[] bytes = bos.toByteArray();
defineClass(bytes,0,bytes.length);
return super.findClass(name);
}
public static void main(String[] args){
Class clazz = newMyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");
ClassLoaderAttachment d1 =clazz.newInstance();
}
}
(14).windows->showview->problem查看錯誤.
(15).能夠查看Servlet的類加載委託繼承樹
11. 枚舉
(1).枚舉的做用限定指定的值,沒有枚舉前設計一個類,將構造函數設置成私有的,變量設置成靜態常量
(2).枚舉能夠有構造函數(私有的),字段,方法
(3).能夠定義set,get方法,獲取變量的值。
(4).帶有抽象方法的枚舉,不能new出新的對象了。在new對象的時候就重寫抽象方法,使用匿名內部類
(5).枚舉中的每一個枚舉值表明枚舉類的一個對象。
(6).枚舉也能夠實現接口,或繼承抽象 。
(7).JDK5中的switch拓展爲除了接受int,short,char,byte外,也能夠接受枚舉類型。
(8).枚舉的方法,name,ordial,valueOf,將字符串轉換成枚舉值。表單提交數據的時候。values返回枚舉的全部的枚舉值
12.內省
(1).內省:Introspector,專門操做Bean的屬性。
(2).Bean的屬性:只有字段提供了set/get方法,就是屬性。只要有get/set的方法,就有一個屬性,因此屬性是有get/set方法決定的
(3).任何類都繼承了Object類,又由於Object中有一個class屬性
(4).內省的入口類:Introspector,方法getPropertyDescriptors()獲取Bean的屬性
(5).操做Bean的指定屬性
(6).BeanUtils框架操做Bean
(7).在工程中新建一個文件夾,將架包複製到文件夾中,還需另外的架包loging,選中架包,右擊build path加入到工程的環境中
(8).BeanUtils使用方便,類型自動轉化,只支持8中基本數據類型
(9).給BeanUtils註冊類型轉換器,ConvertUtils.register()
(10).將Map中的數據整合到對象中,BeanUtils.populate()方法
13.配置Java模板代碼
window->preferences->java->Editor->Template:編輯模板代碼:line_selection,光標cursor,右擊選擇source with
14.享元模式
相同的對象只實例化一個,實例:桌面上的圖標,word中字符,有不少小的對象,有不少相同的屬性,不一樣的屬性叫作外部行爲,相同的屬性叫作內部行爲integer的緩衝池
15.註解
(1).@SuppressWarning("deprecation")過期註解
(2).@Deprecated註解,表示該方法是否過期,架包升級時添加的註解
(3).註解至關於一種標記,經過反射了解你的類及各類元素上有無何種標記
(4).註解至關於一個類:@interface Annotation{};註解類,應用註解類的類,對應用了註解類的類進行反射操做的類
(5).AnnotationTest.class.getAnnotation(ItcastAnnotation.class)獲得類AnnotationTest上的註解ItcastAnnotation,註解上使用註解叫作元註解,元數據,元信息
(6).@Retention(RetentionPolicy.RUNTIME)(保持到運行階段),@Retention(RetentionPolicy.SOURCE)(保持在源文件階段),@Retention(RetentionPolicy.CLASS)(保持在class文件中):源代碼->class文件->(類加載)內存中的文件(字節碼)
(7).@Override註解保持到SOURCE,@SuppressWarning註解保持到SOURCE,@Deprecated註解保持到RUNTIME(只有將該類調到內存中才知道該類中的方法是否過期了)
(8).@Target({ElementType.METHOD,ElementType.TYPE})註解只能標記到方法上或類,接口等類型上 (9).註解的屬性:String color();類有個color屬性,還有一個特殊的屬性value,屬性的默認值default,數組的屬性值,枚舉的屬性值,註解的屬性值
次日:
1.dom4j解析XML文檔
(1).Dom4j是一個簡單、靈活的開放源代碼的庫,Dom4j是由早期開發JDOM的人分離出來然後獨立開發的,與JDOM不一樣的是,dom4j使用接口和抽象基類,雖然Dom4j的api相對要複雜一些,可是他提供了比JDOM更好的靈活性
(2).Dom4j是一個很是優秀的Java XML API,具備性能優異、功能強大和極易使用的特色,如今不少軟件採用的Dom4j,例如hibernate,包括sun公司本身的JAXM也使用了Dom4j
(3).使用Dom4j開發,須要下載dom4j相應的jar文件
2. XML語法
(1).編寫XML文檔時,須要先使用文檔聲明,聲明XML文檔的類型,使用IE校驗XML文檔的正確性.
(2).XML文檔中的"中國"保存爲本地的碼錶的"中國"
(3).在XML文檔中,空格和換行都做爲原始內容被處理
(4).XML區分大小寫,一個標籤能夠有多個屬性,每一個屬性都有它本身的名稱和取值,在XML技術中,
標籤屬性所表明的信息也能夠被表示子標籤表示
(5).XML文件中的註釋採用:"<!-- >"格式
(6).CDATA區域內的內容,XML解析程序不會處理,而是直接原封不動的輸出
(7).轉義字符: "<":< ">":>
3. Xml語言
在xml語言中,它容許用戶自定義標籤,一個標籤用於描述一段數據,一個標籤可分爲開始標籤和結束標籤之間,又能夠使用其餘標籤描述其餘數據,以此來實現數據關係的描述.用於配置文件,以描述程序模塊之間的關係
4. XML約束
(1).在XML技術裏,能夠編寫一個文檔來約束一個XML文檔的書寫規範
(2).經常使用的約束技術:XML DTD 和 XML Schema
(3).若是dtd文件中含有中文,必定要存爲utf-8格式.
(4).IE5以上的瀏覽器內置了XML解析工具:Microsort.XMLDOM,開發人員能夠編寫javascript代碼,利用這個解析工具裝載xml文件,並對xml文件進行dtd驗證.建立xml文檔解析器對象:
var xmldoc = newActiveXObject("Microsoft.XMLDOM");
開啓xml校驗
xmldoc.validateOnParse= "true";
裝載xml文檔
xmldoc.load("book.xml");
獲取錯誤信息
xmldoc.parseError.reason;xmldoc.parseError.line
(5).將DTD文件導入到eclipse中,可進行校驗xml是否遵循dtd文件
在xml文件中編寫DTD
第三天:
1.HTTP請求頭各個頭字段詳解
請求頭詳解:
Accept:text/html,image/*用於告訴服務器,客戶機支持的數據類型,
Accept-Charset:客戶機採用的編碼
Accept-Encoding:客戶機支持的壓縮格式
Accept-Language:客戶機的語言環境,不一樣國家訪問的內容也是不一樣的,就是經過這個頭實現的,用於實現國際化
Host:www.it315.org:8080:告訴服務器,想訪問的主機名
If-Modified-Since:客戶機經過這個頭告訴服務器,資源的緩存時間,提升訪問效率
Referer:客戶機告訴服務器,他是從哪一個資源來訪問服務器的(防盜鏈),經過檢查該頭是不是從本網站點擊過來的,如不是的,就讓他跳到本網站的首頁來
User-Agent:客戶機告訴服務器,客戶機的軟件環境
Cookie:客戶機經過這個頭向服務器帶點數據
2.Http請求行和請求方式
一個完整的HTTP請求包含:一個請求行,若干個請求頭,以及實體內容,請求行,請求頭,以後空一行,帶有請求信息(如表單提交數據爲post方式)以下所示
Get/books/java.html HTTP/1.1 :請求行,用於描述客戶端的請求方式,請求的資源名稱,以及使用的HTTP的協議版本號
Accept:*
Accept-Language:en-us
Connection:Keep-Alive
Host:localhost
Referer:http://localhost/links.asp
User-Agent:Mozilla/4.0
Accept-Encoding:gzip,deflate
以上的內容爲多個消息頭,用於描述客戶端請求哪臺主機,以及客戶端的一些環境信息等
請求行中的GET稱之爲請求方式,請求方式:post,get,head,options,delete,trace,put,經常使用的有:get,post,用戶沒有設置,默認狀況下瀏覽器向服務器發送的都是get請求,例如在瀏覽器直接輸地址訪問,點鏈接訪問都是get,用戶如想把請求方式改成post,可經過更改表單的提交方式實現.無論post或get,都用於向服務器請求某個web資源,這兩種方式的區別主要表如今數據傳遞上:請求方式爲get方式,則能夠再請求的URL地址後以?的形式帶上交給服務器的數據,多個數據之間以&進行分割,同時在URL地址後附帶的參數是有限制的,其數據容量一般不能超過1k,若請求方式爲post方式,則能夠再請求的實體內容中向服務器發送數據,post方式的特色:傳送的數據無限制
<ahref="1.html?name=aaaaaaa">鏈接</a>get方式
3.Http響應頭字段詳解
響應頭:
Location:
這個頭配合302狀態碼使用,用於告訴用戶找誰
,response.setStatus(302),response.setHeader("Location","/day04/1.html");請求重定向,判斷瀏覽器的地址欄的地址是否發生變化,實例是用戶登陸
Server:服務器經過這個頭,告訴瀏覽器的類型
Content-Encoding:服務器經過這個頭,數據的壓縮格式,收費是靠數據出口量算的, 因此爲了省錢和效率高,要進行數據壓縮,jdk中的GZIPOutputStream類,壓縮類流,包裝流和底層流,最好將包裝流關了或者刷新,數據寫入到底層流中去,
response.setHeader("Content-Encoding","gzip");response.setHeader("Content-length",gzip.length+"");
Content-Length:服務器會送的數據的長度
Content-Type:服務器會送數據的類型,response.getOutputStream().write();服務器會送數據都是二進制,經過這個頭,能夠告訴瀏覽器這個二進制是什麼類型,this.getServletContext().getResourceAsStream("/1.bmp");intlen = 0;byte buffer[] =new byte[1024];OutputStream out =response.getOutputStream();while((len=in.read(buffer))>0){out.write(buffer,0,len)},在服務器的目錄下的web.xml中查看各個數據類型的respose.setHeader("content-type","")的寫法.
Refresh:告訴瀏覽器隔多長時間刷新一次,response.setHeader("refresh","3;url=""")控制瀏覽器隔三秒跳到指定的網頁上
Content-Disposition:告訴瀏覽器如下載的方式打開文件,response.setHeader("content-disposition","attachment;filename=3.jpg);
Transfer-Encoding:告訴瀏覽器數據的傳送格式
ETag:緩存相關的頭,服務器根據數據的內容生產一個字符串,客戶機第二次訪問服 務器時帶來的ETag的值和服務器的值同樣,就拿緩存給客戶,實時更新,
Expires:高速瀏覽器,把會送的資源緩存多少時間,-1或0,則是不緩存的
Pragma:no-cache
Cache-Control:no-cache
控制瀏覽器不要緩存數據,當數據不發生改變時,就要緩存,當實時性要求很高的數據不能緩存.
4.Http響應狀態行
HTTP /1.1 200OK :狀態行,用於描述服務器對請求的處理結果
Server:Microsoft-IIS/5.0
Date:....
Content-Length:2291
Content-Type:text/html
Cache-control:private
多個消息頭:用於描述服務器的基本信息,以及數據的描述,服務器經過這些數據的描述信息,能夠通知客戶端如何處理等一會它會送的數據
<HTML>
<BODY>
實體內容:表明服務器向客戶端會送的數據
具體:
狀態行:
格式:HTTP版本號 狀態碼 緣由敘述<CRLF>
舉例:HTTP1.1 200 OK
狀態碼用於表示服務器對請求的處理結果,他是一個三位的十進制數,響應狀態碼分爲5類,
100-199:表示接收請求,要求客戶端繼續提交下一次請求才能完成整個處理過程
200-299:表示成功接收請求並已完成整個處理過程,經常使用200
300-399:爲完成請求,客戶需進一步細化請求,例如,請求的資源已經移動一個新地址,經常使用302(你請求我,我叫你去找別人),307和304(拿緩存)
400-499:客戶端的請求有錯誤,經常使用404,403(沒有權限訪問)
500-599:服務器端出現錯誤,經常使用500
5.http協議概述
(1).瀏覽全部的服務器:
在命令行中敲入
telnet localhost 8080
GET /aa/1.html HTTP/1.1
Host:
(2).在HTTP1.0協議中,客戶端和Web服務器創建鏈接後,只能得到一個web資源。在HTTP1.1協議,容許客戶端與web服務器創建鏈接後,在一個鏈接上獲取多個web資源。
(3).在命令行中敲入以上的命令,HTTP/1.0獲取一次資源,就斷開鏈接,而HTTP/1.1不會斷開鏈接
(4).一個web頁面中,使用img標籤引用了三幅圖片,當客戶端訪問服務器中的這個web頁面時,客戶端總共會訪問幾回服務器,即向服務器發送了幾回HTTP請求:
<img src="1.jpg">
<img src="2.jpg">
<img src="3.jpg">
共四次請求,瀏覽器認識img標籤,瀏覽器解析img,則img一次請求,最好將這三張圖片整合到一張圖片上,這樣就只有兩次請求了
(5).<scriptsrc="1.js">
<scriptsrc="1.js">
共三次請求,能夠將多個js文件整合到一個js文件,避免屢次向服務器發出請求
6. tomcat服務器目錄結構
bin:存放啓動和關閉Tomcat的腳本文件(命令)
conf:存放Tomcat服務器的各類配置文件(核心文件:server.xml)
lib:存放Tomcat服務器的支撐jar包
logs:存放Tomcat的日誌文件(catalina文件)
temp:存放Tomcat運行時產生的臨時文件
webapps:web應用所在目錄,即供外界訪問的web資源的存放目錄
work:Tomcat的工做目錄,jsp翻譯成servelet文件
7.web服務器啓動問題
(1).tomcat是使用Java編寫的,啓動的時候須要JVM,java環境設置不正確的話可能致使服務器不能啓動,還有就是8080端口被佔用了.
(2).在conf->中server.xml中對tomcat服務器進行配置
(3).個人電腦->計算機管理->服務->中止windows服務
(4).http://www.sina.com/是url;www.sina.com是主機名;sina.com是域名,在域名下能夠搭建多個網站
(5).tomcat文件下的webapps下新建一個web資源
(6).Cataline_home環境變量的設置問題,服務器的目錄,啓動的服務器的目錄要和服務器存放的目錄一致
8. web開發相關概念和常見web服務器
(1).本身開發服務器:
public class Server
{
public static void main(String[] args)throws Exception{
ServerSocket server = newServerSocket(9999);//服務器創建端口
Socket sock server.accept();//客戶端訪問
FileInputStream in = newFileInputStream("c:\\a\\1.html");
OutputStream out = sock.getOutputStream();
int len = 0;
byte buffer[] new byte[1024];
while((len = in.read(buffer))>0){
out.write(buffer,0,len);
}in.close();
out.close();
sock.close();
server.close();
}
}
(2).WebLogic是BEA公司的產品,是目前應用最普遍的Web服務器,支持J2EE規範,是商業產品,須要收費
(3).經常使用的Web服務器IBM公司的WebShpere,也遵循J2EE規範,也要收費
(4).Tomcat服務器只支持JSP以及Servlet規範,不支持EJB,若是要支持能夠使用JBOSS
(5).http:80,smtp:25,pop3:110,ftp:23,https:443
9. web應用和虛擬目錄
(1).web應用虛擬目錄映射:本身開發好的web應用項目到部署到服務器上的web應用項目的關係目錄,在服務器的配置文件中配置,虛擬目錄,<Context path="/itcast"docBase="c:\news"/>,"/itcast"就是虛擬目錄,"c:\news"是磁盤上web應用的目錄,修改配置文件後必定要重啓服務器.http://localhost:8080/itcast/1.html,瀏覽c:\news\1.html文件.當path=""時,就是缺省設置
(2).tomcatdocument中reference中觀察tomcat的配置文件設置方法.在Catanena\localhost\新建一個context.xml文件.這種方法無需重啓服務器.
<ContextdocBase="c:\news"/>,http://localhost:8080/1.html;多級虛擬目錄:文件名:a#b#c.xml,http://localhost:8080/a/b/c/1.html;缺省的虛擬目錄,把文件名改爲ROOT.xml重啓服務器,覆蓋原先的缺省設置.
(3).webapps文件夾中的web應用可讓外界訪問,又服務器自動映射目錄,把news文件放到webapps中去,http://localhost:8080/news/1.html,當服務器和webapps不在一個目錄時,此方法不能夠.
10. web應用組織結構
(1).mail:Web應用所在目錄
html,jsp,css,js文件等,這些文件通常存在web應用根目錄下,根目錄下的文件外界能夠直接訪問,java類,jar包,web應用的配置文件存在這個目錄下WEB-INF,這個文件夾的文件名一個單詞都不能寫錯,這個文件要新建,在這個文件夾下載新建一個classes文件夾,放置java程序,新建一個lib文件夾,放置架包,該目錄下的文件外界沒法非法直接訪問,由web服務器負責調用.每一個web應用應該有個web配置文件,是最重要的一個配置文件,一切配置能夠經過這個文件進行配置web.xml
(2).對於web.xml文件,能夠從server.xml中拷貝,拷貝頭和尾部分.
11. web資源訪問流程
(1).服務器認識war包,自動解壓,放到webapps中,自動生成應用.
(2).配置context元素的reloadable="true",應用程序修改,不須要從新發布應用,建議不要配置,由於只要一修改,就要從新加載,出現問題.
(3).context.xml中<Context>中配置,被全部的web應用使用.
12. 配置虛擬主機
(1).在一個tomcat服務器中能夠放置多個網站,所謂配置虛擬主機,就是在tomcat服務器中配置一個網站
(2).如需在Web服務器中配置一個網站,需使用Host元素進行配置,例:<Hostname="site1"appBase="c:\app"></Host>,配置的主機(網站)要想被外部訪問,必須在DNS服務器或windows系統中註冊.
(3).在server.xml新建一個主機,新建一個Host標籤,<Host name=www.sina.com appBase="c:\sina"><Contextpath="/.mail" docBase="c:\sina\main"/></Host>
(4).互聯網訪問流程:ie中輸入錯誤!超連接引用無效。 返回給ie,接着使用ip地址訪問sina.com
(5).ie開始會訪問windows的host文件,若是這個文件知道這個地址,就不會去問DNS,沒有則會去訪問DNS服務器:/windows/system32/drivers/etc/hosts文件進行配置.
13. 軟件密碼學和配置tomcat的https鏈接器
(1).對稱加密:加密和解密的密碼是相同的.
(2).非對稱加密:接受者生成一對公鑰和私鑰,公鑰加密的數據只能私鑰解,私鑰加密的數據只能公鑰解,而後接受者將公鑰傳給發送者,用接受者的公鑰進行加密,而後接受者用私鑰解密.然而,在傳送公鑰的途中可能被黑客攔截,而後黑客本身生成本身的公鑰和私鑰,而後將公鑰送給發送者,發送者如何知道公鑰是否是接受者的公鑰?這是在互聯網上有個叫CA的機構,接受者產生一對公鑰和私鑰,而後讓CA認證,CA給接受者一份數字證書,這時發送者受到的是數字證書,發送者驗證數字證書是否是接受者的數字證書.信任點就是CA.網上銀行出示一份數字證書(公鑰),瀏覽器能夠驗證數字證書正確,註冊,當你填寫信息時,瀏覽器就是用這份數字證書驗證.可是數字證書可能被黑客攔截,怎麼肯定數字證書是接受者發的仍是黑客發的,數字簽名(私鑰加密),發送者本身生成一對公鑰和私鑰,發送者想讓接受者相信是本身加密的,發送者就須要簽名(使用本身的私鑰),而後只有接受者使用公鑰解密,就相信是發送者將內容加密的.MD5算法獲得數據指紋.而後對數據指紋加密.將密文和指紋加密信息傳給接受者,能夠判斷在傳送過程沒有被篡改,用密文生成數據指紋,和傳過來的數據指紋對比.
第四天:
1.Eclipse開發servlet
(1).web工程的名稱,該工程部署時,在webapps目錄下就會有一個example的web應用
(2).src文件夾,Java程序的開發目錄,該目錄下編寫的全部程序在部署時,會自動部署到example/web-inf/classes目錄下
(3).WebRoot文件夾,webroot對應於web應用的根目錄,該目錄下的全部子目錄和子文件夾在部署時,會原封不動的發佈到web應用目錄下
(4).Web rootfolder:WebRoot應用的根目錄,Context root URL:映射到服務器的目錄,在WebRoot目錄下新建Web資源,應用發佈時,將src文件夾中的源文件編譯成class到webRoot\web-inf下的classes文件夾
(5).servlet導入源碼,到tomcat服務器的目錄下查找文件夾
(6).爲servlet配置對外訪問路徑,
<servlet><servlet-name>
別名
</servlet-name><servlet-class>
類名(全稱)
</servlet-class></servlet>
<servlet-mapping><servlet-name>
別名
</servlet-name><url-pattern>/aa<url-pattern></servlet-mapping>
(7).還能夠更改tomcat的運行java環境
2. HttpServlet和一些開發細節
Servlet接口sun公司定義了兩個默認實現:
GenericServlet,HttpServlet
httpservlet指可以處理HTTP請求的servlet,它在原有的servlet接口上添加了一些與http協議處理方法,它比servlet接口的功能更爲強大,所以開發人員在編寫Servlet時,一般應繼承這個類,而避免直接去實現Servlet接口
httpservlet在實現servlet接口時,複寫了service方法,該方法體內的代碼會自動判斷用戶的請求方式,如爲get請求,則調用httpservlet的doGet方法,如爲post請求,則調用doPost方法,所以,開發人員在編寫Servlet時,一般只須要複寫doGet或doPost方法,而不要去複寫service方法.能夠查看httpservlet的源碼,
修改servlet的模板代碼.進入myeclipse,搜索Servlet.java文件,打開修改文件.
3.ServletConfig對象以及它的開發場景
(1).在servlet的配置文件中,能夠使用一個或多個<init-param>標籤爲servlet配置一些初始化參數,當servlet配置了初始化參數後,web容器在建立servlet實例對象時,會自動將這些初始化參數封裝到servletconfig對象中,並在調用servlet的init方法時,將servletConfig對象傳遞給servlet,進而,程序員經過servletconfig對象就能夠獲得當前servlet的初始化參數信息.
(2).web服務器建立的容器對象
request,response,servletconfig,servletcontext,session,cookie
(3).servletconfig
配置參數信息.
<init-param><param-name>data</param-name><param-value>xxxx</param-value></init-param>
(4).覆寫init()方法帶有servletconfig參數的,服務器自動將參數信息封裝到servletconfig對象中.調用init()方法時,servlet能夠獲得這些參數信息.在servlet中定義一個servletconfig對象,將init()傳遞過來的servletconfig接收到.private ServletConfig config;public voidinit(ServletConfig config){this.config = config;}String value =config.getInitParameter("data");System.out.println(value);參數信息不在程序中,而是在配置文件.同時this.getServletConfig();代替privateServletConfig config;由於父類已經定義了ServletConfig對象,只需調用父類的getServletConfig方法便可.
(5).能夠配置多個參數信息,同時爲獲得全部的參數信息:
Enumeration e =this.getServletConfig().getInitParameterName();
while(e.hasMoreElements())
{
String name = (String)e.nextElement();
String value =this.getServletConfig().getInitparameter(name);
System.out.println(value);
}
(6).開發過程當中,參數不能在程序中寫死,須要在配置文件中給出參數信息.實例:字符集,鏈接不通的數據庫.讀取不一樣的配置文件.
(7).struts就是個實例.能夠到struts實例中查看struts的web.xml文件
4. ServletContext讀取web應用中的資源文件
(1).在web開發中,用於資源文件的類型通常爲兩種xml,property,若是數據之間是沒有關係就是用property,有關係就使用xml,數據庫的鏈接關係沒有關係,通常用property文件,
(2).db.properties文件,url=jdbc:mysql://localhost:3306/test;username=root;password=root;讀取db.properties文件.通用代碼:
InputStream in
= this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
斜槓就表明web應用根目錄,Properties props = new Properties();props.load(in);把文件load到Properties對象中,用Map對象存儲String url =props.getProperty("url");
String username =props.getProperty("username");
String password = props.getProperty("password");
(3).web應用中不使用FileInputStream in = newFileInputStream("src/db.properties");採用的是相對路徑,相對路徑中有這個資源,這是相對路徑相對的是jvm的啓動目錄.當時jvm的啓動目錄是服務器的啓動目錄:服務器/bin
(4).另外的讀取資源的方法:
String path = this.getServletContext().getRealPath("/WEB-INF/classes/db.properties");獲得硬盤的絕對路徑 此時就能夠使用FileInputStream in = new FileInputStream(path);需求:下載,能夠獲得資源的名稱,讀取文件時客戶機帶來的,當前讀取的資源文件的名稱.能夠從path中截取出來.
String filename =path.substring(path.lastIndexOf("\\")+1);
(5).在servlet中用dao對象操做數據庫,dao對象須要properties文件的內容即數據庫的鏈接信息.dao通常放到cn.itcast.dao包中.不能將servlet中的context對象傳遞給dao對象,web層和服務層不能有交織了.若是讀取資源文件的程序不是servlet的話,此時要經過類裝載器讀取資源,在UserDao對象中InputStream in =UserDao.class.getClassLoader().getResourceAsStream,資源文件("db.properties");通常只需讀取一次,有個習慣:將讀取文件代碼放在static{};靜態代碼塊中.一般拋出初始化錯誤throw new ExcepitonInitaliterError(e);文件不能太大,類加載器要裝載到內存中
(6).有人可能把properties文件改了.而後讀取的內容是原始的,仍是修改的,讀取的是原始的,由於類裝載的方式,可是類只裝載一次.可是想讀取更新後的內容:String path =UserDao.class.getClassLoader.getResource("db.properties").getPath();而後用FileInputStream去讀取.
5. ServletContext對象
(1).web容器在啓動時,它會爲每一個web應用程序都建立一個對應的ServletContext對象,它表明當前web應用,servletconfig對象中維護了servletcontext對象的引用,開發人員在編寫servlet時,能夠經過servletconfig.getServletContext方法獲得servletContext對象.同時也能夠context = this.getServletContext();獲得父類的context查看servletContext api 文檔瞭解servletContext對象的功能.
(2).servletContext爲全部的servlet對象實現數據共享.是全局概念.方法getContext()爲了獲得別的web應用.getInitParmeter()方法能夠獲得web應用的配置信息.getNameDispatcher()獲得一個轉發對象.getRealPath()方法獲得資源的硬盤真實路徑,方法getResource()獲得資源的路徑,只是返回的是URL地址.getResourcePaths()方法返回給定目錄下的全部資源.getServletContextName()方法返回web應用的名稱.setAttribute(),getAttribute(),removeAttribute();
(3).因爲一個web應用中全部servlet共享同一個servletcontext對象,因此多個servlet經過servletcontext對象是實現數據共享。servletcontext對象一般也被稱之爲context域對象.能夠獲取web應用的初始化參數,實現servlet的轉發,利用servletcontext對象讀取資源文件:獲得文件路徑,讀取資源文件的三種方式,.properties文件(屬性文件),context域,request域,session域,page域.
(4).實例:聊天室,資源共享.開個瀏覽器說,開個瀏覽器讀
(5).<context-param><param-name>data</param-name>
<param-value>XXXX</param-value></context-param>
和config不一樣,config是給某個servlet初始化參數,而context是全部的servlet初始化參數
(6).servletconfig中的初始化信息中鏈接數據庫信息,應該寫成context的初始化信息,由於可能有多個servlet須要數據庫鏈接
(7).轉發和重定向:轉發:你找我,我幫你去找他,重定向:你找我,我告訴你,你本身去找他;若是是轉發:只有一次請求,地址欄不發生改變重定向:兩次請求。servlet中不適合瀏覽器的數據的輸出,由於要數據的排版,應該在html,jsp中輸出數據.servlet要將輸出數據要轉發給jsp顯示.
RequestDispatcherrd = this.getServletContext().getRequestDispatcher("/1.jsp");
rd.forward(request,response);
在jsp中取數據,
<% String.data=application.getAttribute("data");out.write(data);%>servletcontext能夠,可是在開發過程當中是有問題的:有一我的來訪問數據,把它的數據存到context中,當他在jsp中取數據的時候,此時又來一我的,也來存數據,可能覆蓋前一我的的數據,那前一我的取得數據多是後來的那我的的數據,由於context是共享資源,存在線程安全問題.在開發過程當中通常用request域帶數據.
(8).服務器啓動時,爲每一個web應用建立servletcontext對象,當服務器中止時,servletcontext對象被銷燬.
6. Servlet的生命週期
ie瀏覽器,web服務器,Servelet程序
瀏覽器輸入地址->鏈接web服務器,發送HTTP請求,解析出客戶機想訪問的主機名,解析客戶機想訪問的web應用和web資源-->使用反射技術建立一個servlet實例對象(servlet第一次訪問的時候被建立)-->調用servlet.init()完成對象的初始化->調用servlet.service()響應客戶端的請求,服務器建立request,response容器,service方法執行->向response容器中寫入客戶機請求的數據->服務器從response中取出數據構建一個HTTP響應回寫給客戶機-->回寫http響應
客戶機第一次訪問服務器,生成一個servlet,第二次在訪問同一個servlet就不會再建立了,當關閉服務器時servlet就消亡了
7. Servlet的線程安全
(1).當多個客戶端併發訪問同一個servlet時,web服務器會爲每個客戶端的訪問請求建立一個線程,並在這個線程上調用servlet的service方法,所以service方法若是訪問了同一個資源的話,就有可能引起線程安全的問題,若是某個servlet實現了SingleThreadModel接口,那麼servlet引擎將以單線程模式來調用其service方法。SingleThreadModel接口中沒有定義任何方法,只要在servlet類的定義中增長實現SingleThreadModel接口的聲明便可.對於實現了SingleThreadModel接口的servlet,servlet引擎仍然支持對該servlet的多線程併發訪問,其採用的方式是產生多個servlet實例對象,併發的每一個線程分別條用一個獨立的servlet實例對象。實現SingleThreadModel接口並不能真正解決servlet的線程安全問題,由於servlet的引擎會建立多個Servlet實例對象,而真正意義上解決多線程安全問題是指一個servlet實例對象被多個線程同時調用的問題,事實上,在servlet api2.4中,已經將SingleThreadModel標記爲Deprecated(過期的).標準的解決方案是同步方式sychronized
(2).若是線程向person的靜態list集合中加入了數據(aaa),數據用完後,通常要移除靜態集合中的數據(aaa),不然集合中的數據愈來愈多,就會致使內存溢出。對象銷燬了,靜態資源的字節碼仍然駐留在內存中.
(3).web中的異常通常不能拋,要catch住.
8. Servlet開發的一些重要細節
(1).因爲客戶端是經過URL地址訪問web服務器中的資源,因此Servlet程序若想被外界訪問,必須把servlet程序映射到一個URL地址上,這個工做在web.xml文件中,使用<servlet>元素和<servlet-mapping>元素完成
(2).<servlet>元素用於註冊Servlet,它包含有兩個主要的子元素:<servlet-name>和<servlet-class>,分別用於設置Servlet的註冊名稱和Servlet的完整名.
一個<servlet-mapping>元素用於映射一個已註冊的Servlet的一個對外訪問路徑,它包含有兩個子元素:<servlet-name>和<url-patteren>,分別用於指定Servlet的註冊名稱和Servlet的對外訪問路徑
(3).進入myeclipse->Web更改Web-Content-root
(4).一個servlet能夠映射到多個對外訪問路徑.只需多複製個
<servlet-name><url-patteren>(僞靜態)
(5).同一個Servlet能夠映射到多個URL上,即多個<servlet-mapping>元素的<servlet-name>子元素的設置值但是同一個servlet的註冊名
(6).在servlet映射到的URL中能夠使用*通配符,可是隻能有兩種固定的格式:一種格式是:"*.擴展名",另外一種格式是以正斜槓/開頭並以"/*"結尾的.
<servlet-mapping><servlet-name>anyName</servlet-name>
<url-pattern>*.do</url-pattern></servlet-mapping>
匹配全部後綴名爲do的訪問路徑(也能夠不須要後綴名, 匹配全部的文件)<servlet-mapping><servlet-name>anyName</servlet-name><url-pattern>/action/*</url-pattern></servlet-mapping>服務器在更改web.xml時,無需重啓,<WatchedResource>WEB-INF/web.xml</WatchedResource>在服務器的conf文件夾下的context.xml配置文件中,監視web.xml是否發生改動
(7).servlet1 映射到 /abc/*
servlet2 映射到 /*
servlet3 映射到 /abc
servlet4 映射到 *.do
問題:
1.當請求URL爲"/abc/a.html","/abc/*"和"/*"都匹配,可是servlet引擎會調用servlet1
2.當請求URL爲"/abc"時,"/abc/*"和"/abc"都匹配,可是servlet引擎會調用servlet3
3.當請求URL爲"/abc/a.do"時,"/abc/*"和"*.do"都匹配,可是servlet引擎會調用servlet1
4.當請求URL爲"/a.do"時,"/*"和"*.do"都匹配,可是servlet引擎會調用servlet2
5.當請求URL爲"/xxx/yyy/a.do"時,"/*"和"*.do"都匹配,可是servlet引擎會調用servlet2
總結:誰長的最像,誰先匹配,同時*的優先級最低.
(8).Servlet是一個供其餘Java程序(Servlet引擎:服務器端調用servlet的程序)調用的Java類,它不能獨立運行,它的運行徹底由servlet引擎來控制和調度.針對客戶端的屢次servlet請求,一般狀況下,服務器只會建立一個servlet實例對象,也就是說servlet實例對象一旦建立,他就會駐留在內存中,爲後續的其餘請求服務,直至web容器退出,servlet實例對象纔會銷燬.在servlet的整個生命週期內,servlet的init方法只會被調用一次,而對一個servlet的每次訪問請求都致使servlet引擎調用一次servlet的service方法,對於每次訪問請求,servlet引擎都會建立一個新的httpservletrequest請求對象和一個新的httpservletresponse響應對象,而後將這兩個對象做爲參數傳遞給她調用的servlet的service方法,service方法再根據請求方式分別調用doXXX方法.
(9).右擊,能夠選擇父類的方法,進行重寫父類的方法
(10).服務器啓動時,servlet並未建立,只有當訪問web資源時會建立一個servlet,調用init()方法.一個servlet爲多個請求服務,當服務器停了,servlet就被摧毀了,調用destory()方法.
(11).針對客戶端的每一次請求,服務器端都會爲用戶建立一個request和reponse,他們的生命週期很短,若是是併發的請求,大量的請求可能建立大量的request和reponse對象,可能致使服務器運行變慢,可是不是併發的話,可能不會建立那麼多個對象
(12).若是在<servlet>元素中配置一個<load-on-startup>元素,那麼web應用程序在啓動時,就會裝載並建立servlet的實例對象,以及調用servlet實例對象的init()方法,<servlet><servlet-name>invoker</servlet-name><servlet-class>...</servlet-class><load-on-startup>2</load-on-startup>;用途爲web應用寫一個initservlet,這個servlet配置爲啓動時裝載,爲整個web應用建立必要的數據庫表和數據.(即啓動服務器時就建立了servlet),struts就是一個很是大的servlet,其中數字的大小就是啓動的優先級,數值越小優先級越高.struts中的數值爲2,擔憂用戶須要擴張,因此不使用1.
(13).若是某個servlet的映射路徑僅僅爲一個正斜槓/,那麼這個servlet就成爲當前web應用程序的缺省servlet;凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它們的訪問請求都將交給缺省servlet處理,也就是說,缺省servlet用於處理全部其餘servlet都不處理的訪問請求.在<tomcat的安裝目錄>\conf\web.xml文件中,註冊了一個名稱爲org.apach.catalina.servlets.DefaultServlet的Servlet,並將這個servlet設置爲缺省servlet;當訪問tomcat服務器中的某個靜態html文件和圖片時,其實是在訪問這個缺省servlet。這個用途主要用於:用戶請求一個數據(html,jpg,avi等)服務器將啓動一個缺省的servlet提供給客戶數據.在瀏覽器看到的任何內容都是數據都須要由一個servlet來爲客戶提供數據,包括錯誤信息.
(14).配置文件修改了,不須要重啓服務器,可是源程序修改了,要重啓服務器.
9. Servlet開發入門
(1).jsp就是servlet,servlet是sun公司提供的一門用於開發動態web資源的技術,編寫一個java類,實現servlet接口.得到servlet啓動信息,經過getServletConfig(),service的兩個參數,ServletRequest req,ServletResponse res,這個方法是服務器調用的,獲取客戶機的對象,只需找request,輸出信息找response.getOutputStream();
(2).進入服務器的目錄,新建一個web應用,在應用中新建一個java,class,lib,在文件夾java下新建一個servlet文件,
package cn.itcast
public classFistServlet extendsGenericServlet{
public void service(
ServletRequest req,ServletResponseres)throws ServletException,java.io.IOException{
OutputStream out = res.getOutputStream();
out.write("Servlet".getBytes());
}
}
(3).servlet所需的架包導入,tomcat能運行servlet,則在服務器的lib目錄下含有架包,爲servlet配置對外訪問路徑web.xml,代碼能夠到服務器的web.xml文件中拷貝
第五天:
1. HttpServletResponse簡介
(1).web服務器收到客戶端的http請求,會對每一次請求,分別建立一個用於表明請求的request對象,和表明響應的reponse對象.request和reponse對象既然表明請求和響應,那咱們要獲取客戶及提交過來的數據,只須要找request對象就好了,要向客戶機輸出數據,只須要找response對象就好了.
(2).reponse的相關方法,構建一個http響應的方法,getStatus(),setHeader(),getWriter(),getOutputStream();字節流能夠寫任何數據
2.request獲取請求頭和請求數據
(1).用戶點擊連接和表單提交向服務器提交數據
(2).在服務器端獲取數據:
request.getParameter("username");
request.getParmeterName();
獲得全部的名稱的枚舉.
request.getParameterValues();
獲得名稱相同的值
(3).在用戶提交的數據中,可能拋空指針異常,增強代碼的健壯性.
(4).Mapmap=request.getParameterMap();將用戶的數據封裝到對象中User,先獲取Map,再用JavaBeans將Map轉化成對象User,其中map關鍵字是請求參數名稱,關鍵字的類型是String,值的類型是String[],由於可能有多個相同名稱對應的值就有多個,因此是個數組;Map將值填充對象User,迭代Map,獲得關鍵字和對應的值,利用反射技術將這些數據填充到User對象中.設計一個對象表明表單.BeanUtils.copyProperties(user,formbean);將表單beans拷貝到user對象beans中;BeanUtils.populate(user,map);對象上的字段必須是八種基本類型,不是的話,要本身寫個轉換器.
(5).服務器以斷點的方式運行進行調試,
(6).request.getInputStream();獲取輸入流,進行獲取數據,進行文件上傳.
3. request簡介.txt
(1).HttpServletRequest對象表明客戶端的請求,當客戶端經過HTTP協議訪問服務器時,HTTP請求頭中的全部信息都封裝在這個對象中,開發人員經過這個對象的方法,能夠得到客戶這些信息.
(2).getMethod();獲取請求方式,getRequestURI();獲取客戶想我訪問的資源;getHeader();getDateHeader();getHeaderNames();獲取全部的頭信息,getHeaders();與getHeaderNames();不一樣的是:可能有同名的頭信息,此時就要用getHeaders()方法,a-xx;y-xx;getParameter獲取客戶帶來的數據name="aaa";password="rot";getParameterNames();getParameterValues();getInputStream():將用戶的數據當作流讀入,如:客戶進行文件上傳;
(3).getRequestURL():/day07/servlet/demo1;
getRequestURI():http://localhost:8080/day07/servlet/demo1
用在權限攔截上,不一樣的網頁有不一樣訪問權限,作個攔截器,過濾器,還有是在記錄頁面的訪問次數.getQueryString()客戶攜帶的查詢信息,地址欄後的類容,getRemoteAddr();獲得訪問者的ip地址;getRemoteHost():主機在DNS註冊了,打印了主機名.getRemotePort();來訪者的端口,ie也是一個程序,須要一個端口getLocalAddr();返回web服務器的ip地址,getMethod():獲取請求方式,默認是get方式
4.request亂碼
(1).在文本框中敲入中文,瀏覽器提交數據的碼錶就是瀏覽器使用哪一個碼錶打開.request容器也是默認碼錶是iso8859碼錶,因此在獲取數據時,request.setCharsetsEncoding("UTF-8");這種改變碼錶的方式只對post方式有效,對get方式無效.這種方式解決的是post方式提交的數據.
(2).可是這種方式不能用於get方式,若是是get提交,一定是亂碼,因此必須在servlet中手動的改變亂碼,newString(username.getBytes("iso8859"),"UTF-8");
(3).用戶使用超連接提交數據時,帶有中文,其實超連接就是一個get方式,因此要使用get方式去解決亂碼問題.
(4).更改服務器的配置,解決亂碼問題,可是在開發中嚴禁這種方式的使用,從瀏覽器中進入tomcat,修改鏈接器,HTTP->URLEncoding,而後到tomcat目錄下的server.xml中修改鏈接器Connector,在其中添加一個屬性:URLEncoding="UTF-8";還有一種方法,在鏈接器中添加一個屬性,useBodyEncodingForURI="true";是將post方式解決亂碼的方法能夠用於解決get方式亂碼.因此還要事先設置好request容器的碼錶.
(5).request.setCharacterEncoding("UTF-8");
String username =request.getParameter("username");
response.setCharacterEncoding("gb2312");
response.setContentType("text/html";charset="gb2312");
response.getWriter.write(username);不會有亂碼
5.request實現請求轉發和mvc設計模式.txt
(1).request是個域對象,request做用的範圍是請求範圍中,主要用於設計mvc中.
(2).servlet中轉發到jsp顯示數據,
request.getRequestDispatcher("/mesage.jsp").forward(request,response);
在開發中通常都是用這種方式,而不是用ServletContext的轉發方式.每一次請求都對應一個request,正好,request的做用域是一個請求,全部request的轉發,都是一個請求,都在這個做用域中.
(3).request.getParameter()方法是獲取客戶機帶來的數據,而request.getAttribute()方法是獲取request域中獲取數據.
(4).forward方法用於將請求轉發到requestDispatcher對象封裝的資源,若是在調用forward方法以前,在servlet程序中寫入的部份內容已經被真正的傳送到了客戶端(關閉response流),forward方法將拋出IlegalStateExcepiton異常,若是在調用forward方法以前向servlet引擎的緩衝區(response)中寫入了內容,只要寫入到緩衝區中的內容尚未被真正輸出到客戶端,forward方法就能夠被正常執行,原來寫入到輸出緩衝區中的內容將被清空,可是,已經寫入到HTTPServletResponse對象中的響應頭字段信息保持有效.
(5).if(true){request.getRequestDispatcher("/index.jsp").forward(request,response)}
跳轉到index.jsp向客戶機寫數據
request.getRequestDispatcher("/index.jsp").forward(request,response)也要向用戶寫數據。因此一個良好的習慣是跳轉以後必定要return;
(6).String data ="aaa";response.getWriter().write(data);不要關閉這個流,這是能夠request.getRequestDispatcher("/index.jsp").forward(request,response);理論上應該是兩部份內容的組合,即data和jsp中的內容,可是實際上只顯示jsp中的內容,由於jsp的內容將覆蓋data內容,可是消息頭不改變,在這個過程當中建立了兩個servlet,由於jsp也是一個servlet,可是隻有一塊兒請求.不會建立兩個容器.
6. request實現頁面包含
全部的網頁都有相同的網頭和網腳,此時只須要將網頭和網腳設置共同的部分html,request.getRequesetDispatcher("head.jsp").include(request,response);response.getWriter().writer("hhh");request.getRequestDispatcher("foot.jsp").incude(request,response);被包含頁面不能出現全局架構標籤,由於可能重複.實現頁面包含的做用
7.response的outputstream輸出數據的問題
(1).在程序中的"中國"變成了UTF-8碼錶,可是瀏覽器默認的是gb2312打開內容,在程序中發送一個頭,告訴瀏覽器用哪一個碼錶打開.
response.setHeader("Content-type","text/html;charset=UTF-8");
(2).html中的標籤<meta>能夠模擬一個http響應頭.<meta http-equiv='conten-type'content='text/html;charset='UTF-8'>;
(3).當Content-type寫成content-type就變成下載了.
(4).out.write(1);在瀏覽器上顯示的並非1,由於1對應的碼錶.
8.response的writer輸出數據的問題.txt
(1).String data ="中國";PrintWriter out =response.getWriter();out.writer(data);有問題顯示亂碼
(2).servlet將"中國"寫入response中,可是response用的是iso8859碼錶,對應沒有" 中國",因此在瀏覽器中看到亂碼,response.setCharacterEncoding("UTF-8");設置response的碼錶爲UTF-8;同時也要告訴瀏覽器以utf-8碼錶打開.
(3).簡便的調用response.setContentType("text/html;charset="UTF-8");能夠代替以上兩條設置碼錶的代碼.
9.response實現請求重定向和response的一些細節
(1).怎麼實現請求重定向:發送狀態碼302,
response.setStatus(302);response.setHeader("location","/day06/index.jsp");
同時sun公司提供了一個方法response.sendRedirect("/day06/index.jsp");重定向的特色是地址欄發生改變.可能加劇服務器的負擔,由於有屢次請求,通常用於用戶登陸,由於轉發,地址欄不變,用戶看不到是否跳到首頁了,重定向的地址欄變化,能夠告訴用戶跳到首頁,還有一種是購物,點擊購買,服務器的一個servlet幫你買成了,servlet會跳到購物車顯示頁面,顯示你購買的東西.當使用轉發時,用戶點擊刷新,可能又買了一次,從新干一次就很差了,因此用重定向,其餘狀況下都最好使用轉發,減輕服務器的負擔
(2).getOutputStream和getWriter方法分別用於獲得輸出二進制數據,輸出文本數據的servletoutputstream,printwriter對象,可是這兩個方法是互斥的,條用了其中的任何一個方法後,就不能再調用另外一個方法,servlet程序向ServletOutputStream或printwriter對象中寫入的數據將被servlet引擎從response裏面獲取,servlet引擎將這些數據當作響應消息的正文,而後再與響應狀態行和個響應頭組合後輸出到客戶機,servlet的service方法結束後,servlet引擎將檢查getWriter或getOutputStream方法返回的輸出流對象是否已經調用過close方法,若是沒有,servlet引擎調用close方法關閉該輸出流對象
(3).當在demo1中使用了getOutputStream()方法,轉發到demo2,可是在demo2中使用了getWriter();就是在一條調用鏈中不能存在這兩個方法,當使用重定向了,有兩次請求,產生了兩個response對象因此不會有問題,可是轉發就有問題了.
(4).無需關心流的關閉,servlet引擎會自動關閉,可是你本身定義的流須要本身關閉,引擎不會去關閉的.
10.response實現文件下載
(1).在webroot應用下新建一個download文件夾,存放下載資源.
當下載的文件是中文文件,則文件名須要通過url編
碼,URLEncoder.encode(filename,"UTF-8");filename是下載文件名.
11.web工程中各種地址的寫法
(1).request.getRequestDispatcher("/form1.html").forward(request,response);給服務器用的
(2).response.sendRedirect("/day05/form1.html");給瀏覽器用的
(3).this.getServletContext().getRealPath("/form1.html");給服務器用的
(4).this.getServletContext().getResourceAsStream("/form1.html");給服務器用的
(5).<ahref="/day05/form1.html">ddd</a>給瀏覽器用的
(6).<formaction="/day05/form1.html"></form>給瀏覽器用的
總結:寫地址以斜槓開頭,若是地址是給瀏覽器用的,這個斜槓表明一個網站,若是是給服務器用的,這個斜槓表明web應用.瀏覽器向服務器發送請求,就是給瀏覽器用的
12. 防盜鏈
首先檢查你是否從我指定的網頁來的,沒有的話,String referer =request.getHeader("referer");直接在地址欄上粘貼地址,還有就是refer.startsWith("http://localhost");檢查是否從首頁跳轉過來的.首頁上放廣告,再有鳳姐日記的連接.網站就是靠廣告運行的.
13.輸出隨機認證碼圖片
(1).BufferedImage:內存中的一幅圖片.構造函數指定了圖片的長和寬,像圖片上寫隨機數,getGraphics()方法返回一個圖形的對象,而後向該圖像上寫數據.在把內存中的這幅圖片輸出到瀏覽器,ImageIO圖像的輸入輸出;write();方法,指定一個和瀏覽器相關的輸出流.
(2).告訴瀏覽器以圖片的方式讀取數據.
(3).<imgsrc="/day04/servlet/ResponseDemo3">;
(4).刷新後圖片爲改變,沒有控制瀏覽器是否進行緩存,瀏覽器默認是進行緩存的,因此每次拿的都是緩存圖片,沒有改變,因此要控制瀏覽器不要緩存,
response.setDateHeader("expries",-1);response.setHeader(Cache-Control","no-cache");response.setHeader("Parma","no-cache");必須三個都要設置好,才起做用.刷新有兩個做用:從新向服務器發出請求,還有就是把上一次的事情在幹一次.(防止表單重複提交);
(5).<imgsrc="/day05/servlet/ResponseDemo4" οnclick="change(this)"alt="換一張"style="cusor:hand"><br/><scripttype="text/javascript">function change(img){img.src = img.src+"?"+new Date().getTime()}</script>若是後面不跟隨機數,仍是拿緩存了.
14. 用Expires頭控制瀏覽器緩存
(1).控制瀏覽器緩存,在servlet向客戶會送的數據不變,就須要進行緩存,不須要向服務器發送請求,只須要到硬盤的緩存存放文件夾中讀取緩存
(2).Internet選項->常規->點擊設置->查看文件,瀏覽器把index.jsp緩存了,緩存時間是當前時間值:System.currentTime()+1000*3600;
15. 用refresh控制瀏覽器定時刷新
(1).刷新頭:response.setHeade("refresh","3;url='/day04/index.jsp'");控制瀏覽器每隔3秒刷新一次,
(2).假設是一個用於登錄的servlet:提交用戶名和密碼,須要到數據庫查詢是否登錄成功,response.getWriter("恭喜你,登錄成功,本瀏覽器將在3秒後,跳到首頁,若是沒有調,請點擊<a href=" ">點擊</a>");
(3).this.getServletContext().getRequestDispatcher("/message.jsp").forword(request,response);
(4).servlet怎麼告訴jsp定時刷新.字符串寫給jsp"<metahttp-equiv='refresh'conten='3;url=/day08/index.jsp'>"
第六天:
1. Cookie的細節
一個Cookie只能標識一種信息,它至少含有一個標識該信息的名稱(name)和設置值(value),一個web站點能夠給以一個web瀏覽器發送多個Cookie,一個web瀏覽器也能夠存儲多個web站點提供的Cookie,瀏覽器通常只容許存放300個Cookie,每一個站點最多存放20個Cookie,每一個Cookie的大小限制爲4KB,若是建立了一個cookie,並將他發送到瀏覽器,默認狀況下它是瀏覽器以後即被刪除,若但願瀏覽器將該cookie存儲到磁盤上,則須要使用maxAge,並給出一個以秒爲單位的時間,將最大失效設置爲0,則是命令瀏覽器刪除該cookie,注意,刪除cookie時,path必須一致,不然不會刪除.cookie不能太大,否則可能形成網絡擁堵,也能夠將cookie刪除.當兩個cookie的名稱相同時,刪除其中一個,就是刪除兩個.即這兩個cookie必須相同,setPath();也要設置.能夠到ie保存Cookie的文件中查看文件.javascript也能夠刪除cookie
2.Cookie顯示用戶上次訪問網站的時間
(1).javax.servlet.http.Cookie類用於建立一個Cookie,response接口也定義了一個addCookie方法,它用於在其響應頭中增長了一個相應的Set-Cookies方法,它用於獲取客戶端提交的Cookie,使用Cookie封裝用戶的數據.
(2).構造函數,Cookie(name,value);給Cookie一個名稱name,用戶下一次訪問時,帶着上一次訪問的Cookiet,因此在Request.getCookies();返回一個Cookie數組,即用戶的全部Cookie,Cookie的有效期默認是瀏覽器的進程過程,能夠經過setMaxAge()方法設置有效期,案例:用戶在一段時間內自動登陸。方法setPath();用於設置訪問哪一個web資源時攜帶Cookie,方法setDomain(),設置訪問哪一個域名時攜帶Cookie.IE默認禁止這種第三方Cookie,你訪問個人網站,我給你帶回一個的網站的Cookie.
(3).用戶第一次訪問時,沒有攜帶Cookie,Cookie cookie[] =request.getCookies();cookie可能爲空,
首先要判斷一下是否爲空,
for(inti=0;cookies!=null&&i<cookies.length;i++){if(cookies[i].getName().equals("lastAccessTime")
{long cookieValue= Long.parseLong(cookies[i].getValues()}};
新建一個
Cookie:Cookiecookies = new Cookie("lastAccessTime",System.currentTimeMillis());
設置Cookie有效期:cookie.setMaxAge(,,,);
設置有效路徑(默認的是"/day04/servlet");cookie.setPath("/day04");
3.session的工做原理
(1).若是把Cookie禁止,則不能購買東西,Internet選項->隱私->高級
(2).URL重寫,你訪問個人服務器,都是超連接點擊過來的,不一樣用戶訪問首頁時,就幫用戶建立session獲得session的id號,而後將id號放在超連接URL中攜帶過來,不是Cookie攜帶了,因此全部的URL要重寫,
request.getSession();Stringurl = response.encodeURL("/day03/servlet/SessionDemo")此方法自動將session的id號加入該地址中.<a href = '+url+'>點擊</a>
(3).若是沒有禁止Cookie,瀏覽器帶一個Cookie來了,就不會進行URL重寫
4.session的一些細節問題
(1).一個瀏覽器就佔用一個session,開一個新的網頁,不建立session,超連接彈出的新窗口也是共享一個session,同時開多個瀏覽器,建立多個session,這個問題也不是絕對的,不一樣的瀏覽器,功能不同,將session的id號寫到瀏覽器進程,
5.Session簡介
(1).默認狀況下,一個瀏覽器獨佔一個session對象
(2).HttpSessionsession = request.getSession();
session.setAttribute("name","value");
session.getAttribute("name");
(3).寫好過濾器,就能夠解決web應用中的亂碼問題
(4).一個session只爲一個會話服務.(只打開一個瀏覽器窗口)
(5).session的生命週期,第一次訪問getSession()方法時,session就被建立,session是30分鐘沒有人用了(即便瀏覽器不關閉),服務器就將其刪除.而不是結束會話服務.固然這個時間能夠實現的.
<session-config><session-timeout>10</session-timeout></session-config>單位是分鐘,同時在代碼中也能夠控制,session.invalidate();request.getSession(true)方法是指:有session,不建立,沒有session就建立,request.getSession(false)方法是指:只獲取session,不建立session,顯示購物車的時候,不必建立session只須要獲取session.
(6).request.getSession()方法怎麼知道獲取指定用戶的session,即session的工做原理:servlet1中:session=request.getSession(),產生一個session的id號,以Cookie的形式回寫給用戶,下一次用戶就帶着session的id號來,因此該方法就知道獲取指定用戶的session,可是這個Cookie沒有寫有效期,會話結束後,這個id號就沒有了,下一次訪問,服務器又要建立session,買東西的過程當中,關閉瀏覽器,下一次再接着買,上次買的東西,都沒有了,因此解決這個問題,只需設置到這個Cookie的有效期便可.sessionid = session.getId();回寫一個Cookie,名稱爲JSESSIONID,值爲sessionid,覆蓋默認的Cookie.
6.防止表單的重複提交
(1).用javascript防止表單的重複提交,<script type="text/javascript">variscommitted = false;function dosubmit(){if(!iscommitted){iscommitted =true;return ture}else return false;}</script>
<formaciton="/day01/servlet/Demo1" method="post"οnsubmit="return dosubmit()">客戶端阻止,第一個問題是,用戶能夠查看頁面的源代碼,修改js代碼,用戶也能夠點擊刷新,把上次乾的事在幹一次,也能夠點擊後退按鈕.可是在開發過程當中仍是用js,
(2).在服務器端防止表單提交,表單是由程序給出,爲用戶提交的表單提供一個隨機數(表單號),服務器端,查看錶單的id號,檢查表單是否提交過了,若是提交了,刪除該表單的id號,產生令牌的發生器class TokenProcessor{}爲了保證令牌是獨立的,通常講其設置爲單例模式,是一個對象建立的隨機數重複性低,仍是多個對象建立的隨機數重複對象低,是後一種重複性低,爲了獲得隨機數的長短同樣,就要用數據的指紋(摘要),數據的指紋都是同樣大的,是128位,類MessageDigest dis =MessageDigest.getInstatnce("md5");使用md5碼,byte[] md5 = dis.digest(input);input是傳過來的數據,用字節數組變成字符串返回,用到新的算法:base64編碼,任何數據的base64碼變成ASCII碼,原理:將數據字節變成四個字節,0011 0010 1100 1101 0010 1001,這三個字節變成四個字節,每六位裝到一個字節,少兩位,就在前面補兩個零,爲:00001100 00101100 00110100 00101001,最小數爲0,最大數爲63,不會超過64,base64本身定義了一個碼錶,好比:0:a,1:b,3:c ........63: 因此任何數據均可以變成鍵盤上對應的字符,人們熟悉的字符,應用在數據的傳輸過程當中,須要給數據的帶一個結束符號和開始符號,因此把要傳輸的數據變成base64,開始符號和結束符號使用base64中沒有的字符便可,BASE64Encoder encoder=newBASE64Encoder();return encoder.encoder(md5);這個方法可能在api中查找不到,
(3).在servlet中產生一個令牌String token 而後跳到jsp中,這是將token存到session域中,而不是request中,由於token是id號,之後還要使用,在jsp中<input type="hidden"name="token" value="${token}">在用戶不知道的狀況下將令牌帶過去,
(4).將已經提交過的表單號刪除,struts中就是這樣防止表單的重複提交的.
7.會話管理
(1).會話可簡單理解爲:用戶開一個瀏覽器,點擊多個超連接,訪問服務器多個web資源,而後關閉瀏覽器,整個過程稱之爲一個會話,
(2).會話過程當中要解決的一些問題:每一個用戶與服務器進行交互的過程當中,各自會有一些數據,程序想辦法保存每一個用戶的數據,例如:用戶點擊超連接經過一個servlet購買一個商品,程序應該保存用戶購買的商品,以便用戶點結帳servlet時,結帳servlet能夠獲得用戶商品,爲用戶結帳。
(3).若是用戶的數據存到request中,結帳servlet是一個從新的瀏覽器請求,因此結帳servlet與購買servlet是兩次請求,不是共享request,因此結帳servlet不能獲得購買servlet的內容.ServletContext存在併發問題
(4).通常使用Cookie和Session;Cookie是客戶端技術,程序把每一個用戶的數據以cookie的形式寫給用戶各自的瀏覽器,當用戶使用瀏覽器再去訪問服務器中的web資源時,就會帶着各自的數據去,這樣,web資源處理的就是用戶各自的數據,你找我買個的東西,買完了東西,你帶回去(可能將東西cookie放在ie緩存,在本地硬盤中),當你來結帳時,把東西在帶來,即訪問結帳請求,瀏覽器拿緩存(cookie);
(5).Session是服務器端技術,利用這個技術,服務器在運行時能夠爲每一個用戶的瀏覽器建立一個其獨享的Session對象,因爲session爲用戶瀏覽器獨享,因此用戶在訪問服務器的web資源時,能夠把各自的數據放在各自的session中,當用戶再去訪問服務器中的其餘web資源時,其餘web資源再從用戶各自的session中取出數據爲用戶服務.用戶的數據存在服務器端.購買Servlet:session =request.getSession();Session.setAttribute(name,object);結帳Servlet:Session=request.getSession();obj=Session.getAttribute(name);不會再爲這個用戶建立一個session,而去拿服務器中該用戶的session
8.三個域對象的總結
(1).request,session,servletContext三個域:request:程序顯示完,數據沒有用了,就用request(轉發);session:程序顯示完數據後,該數據還有用,就用session,servletContext:顯示完了,除了該本身用,還要給別人用(聊天室)
(2).md5碼,數據指紋,保存用戶名的密碼,爲了安全性,將密碼的指紋保存,根據md5碼反推,須要時間很長,也能夠暴力破解,由於密碼的長度是必定的,因此通常把數據加上一個隨機數,還有一種用途是進行數據完整性的校驗,
第七天:
1. jsp入門和jsp運行原理
(1).<%Date date= new Date();out.write(date.toLocaleString();%>顯示當前時間.
(2).jsp的運行原理:首先將jsp中翻譯成servlet放在服務器的work目錄下,調用servlet的service方法,在這個方法中將jsp中內容經過流out.write()寫到瀏覽器中,jsp中的java代碼將原封不動的運行,能夠查看jsp的servlet代碼,查看全部定義的對象,不只有out對象,還有其餘對象,JspWriter就至關於PrintWriter
(3).最佳實踐:一種技術能夠用在多個地方,可是隻有一個最佳的地方就是最佳實踐.
2. jsp語法page指令詳解
(1).修改jsp的模板,進入到MyEclipse,搜索servlet.java,在這個目錄下,點開jsp找到Jsp.vtl
(2).jsp指令是爲jsp引擎,引擎就是將jsp翻譯成servlet的程序,他們並不直接產生任何可見輸出,而是告訴引擎如何處理jsp頁面中的其他部分,在jsp2.0規範中共定義了三個指令:page指令,include指令,taglib指令
(3).指令的語法:<%@ 指令屬性名=「值」%>,不會產生任何輸出,舉例:<%@ pagecontentType="text/html;charset=gb2312"%>,若是一個指令有多個屬性,這多個屬性能夠寫在一個指令中,也能夠分開寫,例如:<%@ pagecontenType="text/html";charsetgb2312"%><%@ pageimport="java.util.Date"%>也能夠寫做<%@ pagecontentType="text/html;charset=gb2312" import="java.util.Date"%>
(4).page指令用於定義JSP頁面的各類屬性,不管page指令出如今JSP頁面中的什麼地方,它的做用都是整個JSP頁面,爲了保持程序的可讀性和遵循良好的變成習慣,page指令最好是放在整個JSP頁面的起始位置.
(5).JSP2.0規範中定義的page指令的完整語法:
<%@ page
[language="java"]
[extends="package.class"]
[import="{package.class|package.*},..."]
JSP引擎自動導入下面的包:java.lang.*;javax.servlet.*;javax.servlet.jsp;javax.servlet.http;能夠在一條page指令的import屬性中引入多個類或包,其中的每一個包或類之間使用逗號分隔:<%@ pageimport="java.util.Date,java.sql.*,java.io.*"%>
[session="true|false"]:session是否建立,若是爲true,則翻譯成servlet時,就建立一個session,不然不建立,設置true,後就能夠在JSP中片斷中使用session對象,不然不能使用,默認值爲true,由於session的週期比較長,爲了減輕服務器的負載
[buffer=none|8kb|sizeKb]:默認值爲none,jsp的out直接寫到response中,沒有緩衝了
[autoFlush="true|false"]:自動刷新,默認值爲true
[isThreadSafe="true|false"]:JSP是不是線程安全的,設置true後,將發現翻譯後的servlet就繼承了SingleThreade接口,一個servlet只爲一次請求服務,不會出現線程安全問題
[info="text"]:帶信息
[errorPage="relative_url"]:指定JSP錯誤的處理頁面,errorPage屬性的設置值必須使用相對路徑,若是以"/"開頭,表示相對於當前web應用程序的更目錄(注意不是站點根目錄),不然,表示相對於當前頁面,errorPage="/errors/error.jsp";也能夠在web.xml文件中使用<error-page>元素爲整個web應用程序設置錯誤處理頁面,其中的<excepiton-type>子元素指定異常類的徹底限定名,<location>元素指定以"/"開頭的錯誤處理頁面的路徑.做用域不一樣一個是在一個頁面,一個是全局的配置,<error-page><exception-type>java.lang.ArithmeticException></exception-type><location>/errors/error.jsp</location></error-page>;<error-page><error-code>404</error-code><location>/errors/404.jsp</location></error-page>;若是設置了某個JSP頁面的errorPage屬性,那麼在web.xml文件中設置的錯誤處理將不對該頁面起做用,全局性的優先級高,會覆蓋頁面的配置
[isErrorPage="true|false"]:這個頁面是否爲錯誤處理頁面,默認值爲false,好處是:服務器在翻譯成servlet時將異常封裝成一個對象,九大隱式對象中包含一個Excepiton,可是這個對象並非任什麼時候候都有,只有設置這個參數後,纔有,記住
[contentType="mimeType[ ;charset=charachterSet]"|"text/html;charset=ISO-8859-1"]頁面的類型
[pageEncoding="charachterSet|ISO-8859-1"]:頁面的編碼
[isELIgnored="true|false"]:是否忽略EL表達式,EL表達式時JSP2.0的新語法,使用EL表達式時須要將其設置爲false.默認都支持EL表達式
(6).jsp亂碼問題:Jsp程序存在有與Servlet程序徹底相同的中文亂碼問題,輸出響應正文時出現的中文亂碼問題,讀取瀏覽器傳遞的參數信息時出現的中文亂碼問題,Jsp引擎將JSP頁面翻譯成Servlet源文件時也可能致使中文亂碼問題,jsp引擎將jsp源文件翻譯成servlet源文件默認採用UTF-8編碼,而jsp開發人員能夠採用各類字符集編碼來編寫jsp源文件,所以,jsp引擎將jsp源文件翻譯成servlet源文件時,須要進行字符編碼的轉換,若是jsp文件中沒有說明他採用的字符集編碼,jsp引擎將把它當作默認的ISO8859-1字符集編碼處理,經過page指令的contentType屬性說明JSP源文件的字符集編碼,page指令的pageEncoding屬性說明JSP源文件的字符集編碼
(7).tomcat6已經不存在亂碼問題了,在jsp中是"中國",將"中國"保存到硬盤中,默認的查本機的碼錶,web服務器,翻譯jsp的時候就是用iso8859-1碼錶,因此翻譯後的servlet中的"中國"將是亂碼,因此要改變服務器的編碼,因此就用pageEncoding="gb2312"設置服務器翻譯的碼錶,翻譯到servlet後"中國"是正常的,可是此時還要設置瀏覽器以哪一個碼錶打開因此須要設置:contentType="text/html;charset=UTF-8",其實最後的一個設置不須要設置了,由於response是以UTF-8編碼的
3. jsp中經常使用標籤
(1).jsp標籤也稱之爲Jsp Action(jsp動做)元素,它用於在jsp頁面中提供業務邏輯功能,避免在jsp頁面中直接編寫java代碼,形成jsp頁面難以維護.
(2).<jsp:include>,<jsp:forward>:<jsp:forwardpage="/index.jsp"></jsp:forward>應用場景:一般爲網站配置首頁,首頁是個servlet,顯示是jsp,配置首頁的時候,servlet是無效的,可是jsp是有效的,因此能夠使用這個標籤從jsp跳轉到servlet.<jsp:includepage="/1.jsp"></jsp:include>是動態包含,翻譯成兩個servlet,在運行時進行合併,靜態包含是在源代碼的基礎上進行合併<jsp:param>標籤是設置參數,<jsp:forward page="/servelet/ServletDemo"><jsp:paramname="username" value="xxxx"/></jsp:forward>,跳轉到servlet時帶數據過去,value能夠是一個腳本表達式<%=x%>,x爲變量.
4. div+css
(1).盒子模型:每一塊數據都用div套起來,可能控制div的邊框,屬性border,上邊框:Border-top;下邊框:Border-bottom;左邊框:Border-left;右邊框:Border-right,Padding-top,Padding-bottom,Padding-left,Padding-right;控制數據在div盒子中的位置,Margin-top,Margin-bottom,Margin-left,Margin-right:控制一個盒子在頁面的位置,
(2).div是個行級元素,默認的是div單獨排在一行,有時想將兩個div排成一行,這是就要用到定位技術:Float和Position兩種技術
(3).<style>
body{margin:4px 3px 2px 1px;}
#father{
background-color:#FFFFEE;
width:100;
height:100px;
border:1px dashed green;//邊框是綠色的虛線,
}
#son1{float:left;}
#son2{float:left;}
#son3{float:left;}
</style>
<body>
<div id="father">
<divid="son1">aaaaaaa></div>
<divid="son2">bbbbbb</div>
<div id="son3">ccccc</div>
</div>
</body>
這個代碼是有問題的,當添加<div id="son4">ddddd</div>時,也會浮動,受到前面幾個div的影響,因此要清楚浮動效果<divid="clear"></div>,#clear{clear:both;}
(4).position技術就是將#son1{position:relative;left:60%}
5.jsp九大隱式對象
(1).每一個jsp頁面在第一次被訪問時,web容器都會把請求交給JSP引擎(就是一個Java程序)去處理,JSP引擎先將JSP翻譯成一個_jspServlet(實質上也是一個servlet),而後按照servlet的調用方式進行調用,因爲JSP第一次訪問時會翻譯成servlet,因此第一次訪問一般會比較慢,可是第二次訪問,JSP引擎若是發現JSP沒有變化,就不在翻譯,而是直接調用,因此程序的執行效率不會受到影響,JSP引擎在調用JSP對應的_jspServlet時,會傳遞或建立9個與web開發相關的對象供_jspServlet使用,JSP技術的設計者爲便於開發人員在編寫JSP頁面時得到這些web對象的引用,特地定義了9個相應的變量,開發人員在JSP頁面中經過這些變量就能夠快速得到這9大對象的引用,
(2).HttpServletRequestrequest,HttpServletResponse response,ServletContextapplication,ServletConfig config JspWriter out,Exception,PageContextpageContext,Object page,Session session,具體的能夠查看文檔
6.jsp映射和jsp常見錯誤處理
在web.xml中
<servlet><servlet-name>xxx</servlet-name><jsp-file>/1.jsp</jsp-file></servlet><servlet-mapping><servlet-name>xxx</servlet-name><url-pattern>/12.html</url-pattern>
7.jsp語法
(1).jsp語法:jsp模板元素,jsp表達式,jsp腳本片斷,jsp註釋,jsp指令,jsp標籤,jsp內置對象,如何查找jsp頁面中的錯誤
(2).jsp腳本表達式(expression)用於將程序數據輸出到客戶端:語法:<%=變量或表達式%>舉例:當前時間:<%=new java.util.Date()%>;jsp引擎在翻譯腳本表達式時,會將程序數據轉成字符串,而後再相應位置用out.print(...)將數據輸出給客戶端,jsp腳本表達式中的變量或表達式後面不能有分號(;)
(3).jsp腳本片斷<% %>,中寫入java代碼,原封不動的變成java代碼
(4).變量能夠再多個腳本片斷中是能夠相互訪問的.
(5).jsp聲明:jsp頁面中編寫的全部代碼,默認會翻譯到servlet的service方法中,而jsp聲明中的java代碼被翻譯到_jspService方法的外面,語法:<%! java代碼%>(多個感嘆號),因此,jsp聲明可用於定義jsp頁面轉換成的Servlet程序的靜態代碼塊、成員變量和方法,多個靜態代碼塊、變量和函數能夠定義在一個jsp聲明中,也能夠分別單獨定義在多個jsp聲明中,jsp隱式對象的做用範圍僅限於Servlet的_jspService方法,因此在jsp聲明中不能使用這些隱式對象,<% public void run(){} %>這種語法是錯誤的,<%! public void run(){}%>這種語法是正確的.
(6).註釋:<%-- --%> jsp中的註釋不會打給瀏覽器,可是html註釋會打給瀏覽器.
8. out隱式對象
(1).out隱式對象用於向客戶端發送文本數據,out對象是經過調用pageContext對象的getOut方法返回的,其做用和用法與ServletResponse.getWriter()方法返回的PrintWriter對象很是類似,JSP頁面中的OUT隱式對象的類型爲JspWriter,JspWriter至關於一種帶有緩存功能的PrintWriter,設置JSP頁面的page指令的buffer屬性能夠調整它的緩存大小,甚相當閉它的緩存,只有向out對象中寫入了內容,且知足以下任何一個條件時,out對象纔去調用servletresponse.getWriter方法,並經過該方法返回的PrintWriter對象將out對象的緩衝區中的內容真正寫入到servlet引擎提供的緩衝區中:設置page指令的buffer屬性關閉了out對象的緩存功能,out對象的緩衝區已經滿了,整個JSP頁面結束.
(2).out.write("hahahahhahha");response.getWriter().write("wowowowo");理論上是先看到hahahah在看到wowowowowwo,可是實際是wowowowowowo hahahahhaha,緣由就是out有緩存,JspWriter向緩衝區中寫入內容,respose的緩衝中是wowowwowowwo,當jsp結束後,response緩衝區發現JspWriter的緩衝區中有內容,就將其刷新到response,因此先看到wowowowowwo,而後就是hahahahahhaha,就是response寫入的內容最早顯示,因此在開發過程當中不要兩種方式都使用,通常使用out.write();
9.pageContext對象
(1).pageContext對象是JSP技術中最重要的一個對象,它表明JSP頁面的運行環境,這個對象不只封裝了對其餘8大隱式對象的應用,它自身仍是一個域用來保存數據,而且,這個對象還封裝了web開發中常常涉及到的一些經常使用操做,例如引入和跳轉其餘資源,檢索其餘對象中的屬性等
(2).它的方法:getException()返回exception隱式對象,getPage()方法返回page隱式對象,getRequest()返回request隱式對象,getResponse()返回response隱式對象,getServletConfig返回config隱式對象,getServletContext返回application隱式對象,getSession()方法返回session隱式對象,getOut()方法返回out隱式對象,pageContext封裝了其餘8中內置對象,用途是在自定義標籤中,jsp中最好不要出現java代碼,有時想顯示數據,又不能有java代碼,String data -(String)request.getAttribut("data");out.write(data);在頁面中寫入一個標籤<flx:viesData/>傳遞一個pageContext對象就至關於傳遞8大隱式對象.
(3).pageContext也是一個域對象,pageContext.setAttribut("data","aaa");域的生命週期是整個頁面的週期,做用域是整個頁面,四個域中,做用範圍最小的一個,request就是一個請求範圍內,session就是在一個會話範圍內,servletContext整個web應用
(4).表明各個域的常量:
PageContext.APPLICATION_SCOPE,PageContext.SESSION_SCOPE,PageContext.REQUEST_SCOPE,PageContext.PAGE_SCOPE
(5).封裝了訪問其餘域的方法,方法public java.lang.Object getAttribute(java.lang.Stringname,int scope)是個重載的方法:Stringdata=(String)pageContext.getAttribut("data",PageContext.REQUEST_SCOPE);管理全部域的入口
(6).最重要的一個方法:findAttribute:查找各個域中的屬性,pageContext.findAttribut("data")首先從page,request,session,application逐級查找,找不到返回空.用於el表達式,el的執行就是按照這個方法的原理執行的.
(7).引入和跳轉到其餘資源,PageContext類中定義了一個forward方法和兩個include方法來分別簡化和代替RequestDispatcher.forward方法和include方法,方法接受的資源若是以"/"開頭,就表明當前的web應用
10.jsp語法include指令詳解
(1).include指令用於引入其餘JSP頁面,若是使用include指令引入了其餘JSP頁面,那麼JSP引擎將把這兩個JSP翻譯成一個Servlet,因此include指令引入一般稱之爲靜態引入
(2).語法:<%@ include file="relativeURL"%>其中的file屬性用於指定被引入文件的路徑,路徑以"/"開頭,表示表明當前web應用,細節:被引入的文件必須遵循JSP語法,被引入的文件能夠使用任意的擴展名,即便其擴展名爲html,JSP引擎也會按照處理jsp頁面的方式處理它裏面的內容,爲了見明知意,JSP規範建議使用jspf(JSP fragments)做爲靜態引入文件的擴展名,因爲使用include指令將會涉及到2JSP頁面,並會把2個JSP翻譯成一個servlet,因此這2個JSP頁面的指令不能衝突(除了pageEncoding和導入包除外);
(3).查看源文件,不是格式良好的html頁面,就將被包含的頁面的頭刪除
(4).動態包含:request.getRequestDispatcher("/public/head.jsp").inlude(request,response);它會把三個JSP翻譯成三個servlet,靜態包含只翻譯一個Servlet,
(5).在開發過程當中兩種包含都用到:靜態包含編譯時包含,性能高,動態包含是運行時包含
第八天:
1. sun公司的jstl標籤庫
(1).JSTL標籤庫:核心標籤,國際化標籤,數據庫標籤,XML標籤,JSTL函數(EL函數)
(2).sun公司的全部標籤都放在standard.jar架包中,
(3).<c:out>標籤用於輸出一段文本內容到pageContext對象當前保存的"out"對象中,
(4).<c:set>標籤用於把某一個對象存在指定的域範圍內,或者設置web域中的java.util.Map類型的屬性對象或JavaBean類型的屬性對象的屬性.
(5).<c:remove>標籤用於刪除各類web域中的屬性
(6).<c:catch>標籤用於捕獲嵌套在標籤體中的內容拋出異常
(7).<c:if>,<c:foreach>標籤
2.標籤案例-打包本身的標籤庫
(1).首先建一個java工程,將全部的標籤處理器類考到這個java工程中,而後將tld文件存放到meta-inf文件夾中,而後將java工程導出爲jar
3.標籤案例-開發foreach
(1).List list =new ArrayList();而後迭代輸出list中的數據
(2).<c:foreachvar="str" items="$(list)">$(str),var屬性記錄list中數據的類型.var的類型爲String
(3).在doTag()方法中,List list = (List)items;Iteratro it =list.iterator();while(it.hasNext()){Object value =it.next();this.getJspContext().setAttribute(var,value);this.getJspBody().invoke(null);標籤中的內容執行回到pagecontext的域中拿去屬性var的值,而後輸出}
(4).將foreach功能增強能夠迭代數組,Map,Collection判斷是否是Map,仍是Collection,仍是Object[],可是要進行類型轉換,可能有不少重複代碼,因此都直轉型Collection collection便可,if(items instanceof Collection){collection =(Collection)items;}if(itemsinstanceof Map){Map map (Map) items;collection = map.entrySet();} if(itemsinstanceof Object[]){Object obj[] =(Object[])items;CollectionArrays.asList(obj);}當碰到基本數據類型時,就須要迭代數組,將數組中的數據放到collection中,
(5).sun公司的foreach標籤在standard架包中,在web-inf,c.tld,查找forEach標籤對應的類
(6).反射技術,Class類中有一個方法isArray()判斷是不是數組類型,不只適用於任何類型,在反射類中有一個類Array,它有個方法
getLength(),for(inti=0;i<length;i++){Object value =Array.get(items,i);this.collection.add(value);}
對任意數組進行操做.
4.標籤案例-開發if-else標籤
(1).<c:iftest="$(user!=null)">aaa</c:if><c:else>bbb</c:else>這兩個標籤是共享一個變量,即讓他們兩個共享同一父標籤<c:choose></c:choose>,共享的變量在父標籤中定義,則此時須要寫三個標籤處理器類,
(2).同時在父標籤中要讓其標籤體執行,因此在父標籤中的doTag()方法,this.getJspBody().invoke(null);
(3).獲得父標籤的變量,因此在子標籤的doTag()方法中Choose parent = (Choose)this.getParent();就能夠獲得父標籤的處理器類
5. 標籤案例-開發防盜鏈標籤
就是將前面的request的課程中的方法定義爲標籤處理類便可
6. 標籤案例-開發轉義
(1).好比超連接的原始輸出,
StringWriter sw =new StringWriter();JspFragment jf = this.getJspBody();jf.invoke(sw);Stringcontent = sw.getBuffer().toString();服務器的webapps/examples/web-inf/htmlFilter,java中的方法filter();
7. 標籤簡介和開發第一個標籤
(1).自定義標籤主要用於移除jsp頁面中java代碼,使用自定義標籤移除jsp頁面中的java代碼,只須要完成如下兩個步棸:編寫一個實現Tag接口的Java類,把頁面java代碼移動這個java類中(標籤處理類),編寫標籤庫描述符(tId)文件,在tId文件中對標籤處理器類描述成一個標籤,
(2).<%request.getRemoteAddr();out.print(ip);%>,顯示用戶的IP,使用自定義標籤,首先將java代碼移動到java類,繼承Tag接口,標籤有開始標籤和結束標籤,doEndTag()方法,doStartTag()方法,標籤可能有爸爸,因此方法getParent()將父標籤做爲一個對象傳給jsp引擎,方法setPageContext()方法最重要,就是傳遞一個PageContext對象,他同時也攜帶了其餘8個對象,因此將java代碼移動到doStartTag();Tag是個接口,因此使用其繼承類,TagSupport,本身編寫的類只須要覆蓋doStartTag();準備好request,response,因此要獲得PageContext,又由於服務器在執行doStartTag()方法以前,默認執行了setPageContext()方法了,因此能夠寫爲:HttpServletRequest request =(HttpServletRequest)this.pageContext.getRequest();還有獲取out對象,用於輸出.移動完java代碼後,第二步:就是在標籤庫中將標籤處理器描述標籤,能夠到tomcat的webapps中自帶的一些例子,web-inf/jsp2/taglib.tld.
(3).<tag><name>viewIP</name><tag-class>cn.itcast.web.tag.ViewIPTag</tag-class><body-content>empty</body-content></tag>.<url><http://www.itcast.cn</url>
(4).導入標籤<@taglib url="http://www.itcast.cin"prefix="itcast"%>:prefix是前綴,能夠容易定位到描述標籤的文件
(5).<itcast:viewIP/>便可itcast就是前綴prefix,viewIP就是tag的名稱name
(6).tld文件的位置必須放在web-inf中.
8. 傳統標籤庫功能詳解
(1).控制標籤體內容是否輸出:覆寫父類的doStartTag()方法,public int doStartTag(){returnTag.EVAL_BODY_INCLUDE}執行標籤體,而後再添加tld文件,<body-content>JSP</body-content>,若是不執行只須要return Tag.SKIP_BODY便可
(2).控制整個JSP是否輸出,就在JSP開始頭定義一個標籤,doEndTag()的返回值是否繼續執行餘下的JSP內容,只需覆蓋父類的doEndTag()方法,public int doEndTag(){return Tag.SKIP_PAGE}不執行JSP內容,而後添加tld文件,必定要將該定義的標籤放在JSP開始的部分.返回Tag.EVAL_PAGE就能夠執行餘下的JSP內容.
(3).控制某一部份內容重複執行,只需將重複類容套在自定義的標籤中,Tag接口找不到指定的方法,必需要使用其子接口IterationTag接口,其內部中有個方法:doAfterBody(),這個方法在標籤體結束後執行,若是返回EVAL_BODY_AGAGIN,就接着執行,直到返回SKIP_BODY,因此繼承IterationTag接口,可是TagSupport繼承IterationTag,因此仍是繼承TagSupport便可,首先覆寫方法doStartTag()方法,返回Tag.EVAL_BODY_INCLUDE;而後覆寫方法doAfterBody(){x--;if(x>0)return IterationTag.EVALBODY_AGAIN;elsereturn IterationTag.SKIP_BODY;}其中x應該定義爲成員變量,而不是在doAfterBody的局部變量,由於doAfterBody()方法是屢次執行的,不是隻執行一次.
(4).修改jsp頁面的內容,將顯示的內容修改爲大寫的,將內容套在自定義標籤中,接口BodyTag接口,此接口繼承了IterationTag接口,覆寫BodyTagSupport方法doStartTag()返回EVAL_BODY_BUFFERED,會將標籤體最爲一個對象經過方法setBodyContent傳給標籤處理器類,而後在doEndTag()方法中BodyCotned bc = this.getBodyContent();獲得標籤體,String content = bc.getString();content=content.toUpperCase;而後經過pageContext輸出,最後要記得返回EVAL_PAGE,執行餘下的jsp內容,就拿到標籤體中的內容,而後對內容進行操做.
(5).IterationTag接口的子類爲TagSupport類,TagSupport類的子類爲BodyTagSupport類,同時BodyTagSupport類是實現BodyTag接口,同時BodyTag接口繼承了IterationTag接口,IterationTag接口繼承了Tag接口,同時Tag接口和SimpleTag接口都繼承JspTag接口,可是Tag接口時JSP2.0之前定義的,如今不用了,而如今主要用的就是SimpleTag接口,其子類爲SimpleTagSupport類.因此稱Tag接口爲傳統標籤,SimpleTagSupport爲簡單標籤
9. 簡單標籤庫功能詳解
(1).SimpleTag接口集成了之前的Tag,IterationTag,BodyTag三個傳統標籤的功能,因此SimpleTag有個默認實現類SimpleTagSupport
(2).JspContext就是PageContext,方法,setJspBody(JspFragment jspBody),服務器經過這個方法將PageContext對象傳遞給標籤處理器,而後調用doTag()方法,沒有開始和結束標籤,doTag()方法中拋出個異常,返回SKIP_PAGE控制整個頁面不輸出,
(3).簡單標籤的通常邏輯都在doTag()方法中,JspFragment jf = this.getJspBody();jf.invoke(this.getJspContext().getOut());簡單標籤中寫成<body-content>scriptless</body-content>,JSP2.0規定了jsp中不能寫腳本代碼了,因此不死JSP,而是scriptless,jf是獲得了一個標籤體,jf.invoke()標籤體運行,在this.getJspContext().getOut()的out對象寫入瀏覽器
(4).若是想讓標籤體不執行,jf.invoke()方法不執行就能夠了
(5).jf就是標籤體,能夠寫成:for(int i=0;i<5;i++)jf.invoke(null);這樣寫也行,就是默認的也是寫給瀏覽器,重複將標籤體
(6).StringWritersw = new StringWriter();jf.invoke(sw);將jf的標籤體的內容輸入到本身的緩衝,能夠本身定義一個緩衝流,String content = sw.toString();content =content.toUpperCase();
(7).控制整個標籤是否執行,只需在doTag()方法中拋出異常,throw new SkipPageException();便可.同時這個標籤也要放在jsp頁面的開始處,jsp餘下的內容就不執行了
(8).jsp運行時遇到簡單標籤,實例化標籤處理器類,而後經過調用對象setJspContext()方法,將PageContext對象傳遞給標籤處理器類,接着調用setParent()方法將父標籤傳遞給標籤處理器類,而後把標籤體的內容封裝JspFragment對象傳遞給標籤處理器類,而後調用doTag()方法執行自定義標籤,執行完後,標籤就變成垃圾了,等待垃圾回收器回收,這一點和傳統的自定義標籤不一樣
10.開發帶屬性的標籤
(1).要想讓一個自定義標籤具備屬性,一般須要完成兩個任務:在標籤處理器中編寫每一個屬性對應的setter方法,在TLD文件中描述標籤的屬性,爲自定義標籤訂義屬性時,每一個屬性都必須按照JavaBean的屬性命名方式,在標籤處理器中定義屬性名對應的setter方法,用來接收JSP頁面中調用自定義標籤時傳遞過來的屬性值,例如屬性url,在標籤處理器類中就要定義相應的setUrl(String url)方法,在標籤處理器中定義相應的set方法後,JSP引擎在解析執行開始標籤前,也就是調用doStartTag方法前,會調用set屬性方法爲標籤設置屬性
(2).在tld文件中聲明:
<body-content>scriptless</body-content>,<attribute><name>count</name><required>true</required><rtexprvalue>true</retexprvalue>這個設置爲true說明不只能夠傳遞變量,也能夠傳遞腳本表達式</body-content>
(3).count="5"屬性傳遞過去的是字符串,可是count是int型的,服務器自動轉型,可是隻支持8中基本類型
11.自定義標籤功能概述
(1).自定義標籤功能擴展,開發人員在編寫JSP頁面時,常常還須要在頁面中引入一些邏輯例如:控制jsp頁面某一部份內容是否執行,控制整個jsp頁面是否執行,控制jsp頁面內容重複執行,修改jsp頁面內容輸出,
(2).但願有段jsp代碼只有權限的人才能訪問,就是控制某一部份內容是否執行,格式化頁面輸出的內容.
12.自定義標籤運行原理
(1).ie->web服務器->1.jsp(1.servlet)[<itcast:viewIP/>]首先實例化標籤處理器類,而後調用自定義的繼承Tag接口的類的方法,調用setPageContext方法,把頁面的pageContext對象傳遞給標籤處理器類,若是有父類標籤,就把標籤對象經過setParent方法傳遞給標籤處理器類,完成以上初始化工做後,就開始執行開始標籤,doStartTag()方法,若是標籤有標籤體,服務器通常會執行標籤體,而後執行結束標籤.而後標籤調用release()方法,釋放標籤佔用的資源,通常是web應用中止時調用,服務器接着執行jsp餘下的內容,web服務器會將web-inf中的tld文件加載到內存中,
(2).jsp翻譯成Servlet中能夠看到service方法,調用自定標籤的動做放在方法中,首先獲取pageContext,將標籤處理器類的對象建立出來,
第九天:
1. el表達式和jstl快速入門
(1).EL表達式用於獲取數據,在jsp頁面中能夠使用${標示符}的形式,通知JSP引擎調用pageContext.findAttribute()方法,以標示符爲關鍵字從各個域對象中獲取對象,若是域對象中不存在標示符所對應的對象,則返回結果爲""(注意不是null),EL表達式中也能夠使用$(customerBean.address)的形式來訪問JavaBean對象的屬性,結合JSTL標籤,EL表達式也能夠輕鬆獲取各類集合中的元素,EL表達式也能夠使用類如${1==1}的新辦公室進行簡單的邏輯判斷
(2).<% Person p= new Person();p.setName("aaa");request.setAttribute("person",p);%> ${person.name}
(3).用el表達式在取數據時,一般用.號,若取不出來,就用[],${map['aaa'].name};
(4).${pageContext.request.contextPath}獲取當前web應用的路徑,<a href=${pageContext.request.contextPath}>點擊</a>
(5).<c:forEachvar="entry" items="${map}">${entry.key}:${entry.value.name} </c:forEach>
2. jsp和javabean
(1).javabean是一個遵循特定寫法的Java類,它一般具備以下特色:這個Java類必須具備一個無參的構造函數,屬性必須私有化,私有化的屬性必須經過public類型的方法暴露給其餘程序,而且方法的命名也必須遵照必定的命名規範
(2).javabean在j2ee開發中,一般用於封裝數據,對於遵循以上寫法的javabean組件,其餘程序能夠經過反射技術實例化javabean對象,而且經過反射那些遵照命名規範的方法,從而獲知javabean的屬性,進而調用其屬性保存數據
(3).屬性的修改器和訪問器就是set,get;
(4).JSP技術提供了三個關於JavaBean組件的動做元素,即JSP標籤,他們分別是:<jsp:useBean>標籤:用於在JSP頁面中查找或實例化一個JavaBean組件,<jsp:setProperty>標籤:用於在JSP頁面中設置一個JavaBean組件的屬性,<jsp:getProperty>標籤:用於在JSP頁面中獲取一個JavaBean組件的屬性
(5).<jsp:useBean>標籤用於在指定的域範圍內查找指定名稱的JavaBean對象:若是存在則直接返回該JavaBean對象的引用,若是不存在則實例化一個新的JavaBean對象並將它以指定名稱存儲到指定的域範圍中,經常使用語法:<jsp:useBean id="beanName"class="package.class"scope="page|request|session|application"/>;id屬性用於指定JavaBean實例對象的引用名稱和其存儲在域範圍中的名稱,class屬性用於指定JavaBean完整的類名(即必須帶有包名)scope屬性用於指定JavaBean實例對象所存儲的域範圍,其取值只能是page,request,session和application等四個值中的一個,其默認值是page,從page中查找,查到不到就建立一個javabean,查找到了就返回定義的javabean,反正最後確定有一個javabean
(6).<jsp:useBean....>body</jsp:useBean>標籤體只會在javabean實例化時執行.
3. mvc開發模式
(1).按照三層結構開發流程:servlet,jsp是web層,service,javabean是業務邏輯層(service層),dao是數據訪問層(dao層),一般在層與層之間定義接口,因此在dao層和service層之間定義dao接口,同理在web層和service層之間定義一個service接口,dao層使用jdbc,hibernate編寫,當dao層修改了,service的代碼不須要修改,由於在service中使用的的是dao接口
(2).組織包接口:Cn.itcast.domain:存放javabean類,cn.itcast.dao存放dao層的類,cn.itcast.dao.impl:存放dao層接口,cn.itcast.service:存放service層類,cn.itcast.service.impl:存放service層接口,cn.itcast.web.controller:存放servlet的類,cn.itcast.web.listener:存放監聽器類,cn.itcast.web.filter:存放過濾器類,cn.itcast.web.util:存放工具類,cn.itcast.juit.test:存放測試類,jsp頁面放在web-inf下的jsp文件夾下.
2、 Struts1
1. ActionForm的工做流程分析
(1).ActionForm的工做原理:處理ActionForm的通常步驟:
第一步:檢查Action的映射,肯定Action中已經配置了對ActionForm的映射
第二步:根據name屬性,查找form-bean的配置信息
第三步:檢查Action的form-bean的使用範圍,肯定在此範圍下(request,session),是否已經有此form-bean的實例
第四步:假如當前範圍下,已經存在了此form-bean的實例,而是對當前請求來講,是同一種類型的話,那麼就重用
第五步:不然,就從新構建一個form-bean的實例(調用構造方法),而且保存在必定做用範圍
第六步:form-bean的reset()方法被調用
第七步:調用對應setter方法,對狀態屬性賦值
第八步:若是validate的屬性設置爲true,那麼就調用form-bean的validate()方法
第九步:若是validate()方法沒有返回任何錯誤,控制器將ActionForm做爲參數,傳給Action實例的execute()方法並執行
注意:直接從ActionForm類繼承的reset()和validate()方法,並不能實現什麼處理功能,因此要本身從新覆蓋方法
2. ActionForm相關的attribute屬性
(1).配置文件簡介:使ActionServlet,ActionMapping,Action,ActionForm這幾個不一樣層次的組件相互協調工做,這些配置文件是在系統啓動的時候,讀入到內存中,供控制器使用的
(2).<action-mappings>元素幫助進行框架內部的流程控制,可將請求URL映射到Action類,將Action對象與ActionForm對象相關聯,<action-mappings>元素內可定義多個<action>子元素,<action>元素,所描述的是特定的請求路徑和一個相應的Action類之間的映射關係,有如下屬性:attribute:設置和Action關聯的form bean在request/session內的屬性key,經過request/session的getAttribute(attribute)方法返回該form bean實例,用來存取form的關鍵字,缺省值與name同樣.而不是attribute的值.(ActionForm),input:當表單驗證失敗時將請求轉發的URL
(3).ActionForm相關的input屬性
input屬性是用來記錄路徑:當validate校驗不經過,即ActionForm的validate方法返回爲ActionErrors對象,且該對象攜帶一些錯誤信息,就跳轉到指定的錯誤頁面(action)的路徑,通常是結合validate=true是結合使用的,當爲false時,input屬性就沒有意義了.
ActionForm獲取用戶的請求參數,其屬性的名稱必需要和請求參數名相同,必需要定義對應的get/set方法
3. ActionForm相關的validate屬性
(1).數據校驗:判斷用戶帶來的數據是否符合規定的格式.
(2).服務器端的校驗:看ActionForm有沒有調用validate(),能夠返回ActionErrors對象,此對象返回一個錯誤封裝對象,沒有錯誤就返回null,缺省的狀況是返回null,因此子類要覆蓋這個方法.也能夠經過在actionForm子類的配置中設置validate="false"的值,使該方法不調用
復位:是總控制器恢復bean屬性的默認值
4. ActionForward的有關問題
(1).ActionForward對象的配置對象,這些配置對象擁有獨一無二的標識以容許他們按照name屬性等來檢索,ActionForward對象封裝了向前進的URL路徑且被請求處理器用於識別目標視圖
(2).其內部的方法就是兩種方法:request.Dispatch().forward();和response.sendRedirect(),前面講到的重定向和轉發.
ActionForward:Redirect是false,就是容器內跳轉,就是Request.Dispathcer.forward();在這種狀況下path爲相對路徑是true,就是容器外跳轉,就是Response.sendRedirect(),在這種狀況下path爲絕對路徑,要在域名前加上協議頭錯誤!超連接引用無效。
6.ActionMapping的深刻研究
(1).ActionMapping:每一個<action>元素都與類ActionMapping的一個實例對應,表明着請求信息,該類就是對<action>元素中的信息進行封裝保存.該類含有這些信息的訪問器和修改器.
(2).mapping.getName();mapping.getPath();mapping.getType();mapping.findForwards();返回局部跳轉的頁面信息,即就在一個action內部.
7.Action的深刻研究和分析
(1).怎麼測試Action實例化了,根據構造函數便可,public Action(){System.out.println("Action isrunning!");}Action在發出請求時初始化,不是在讀取配置時初始化,每一個action只會初始化一次,即便在不一樣的會話中(打開兩個瀏覽器).內存中只有一份,資源能夠共享和重用,可是不安全.對以多個請求只建立一個action,含有併發問題.須要進行同步.struts2.x是安全的,
(2).安全的話注意兩點:第1、不要用類變量或實例變量共享只是針對某個請求的數據,第2、注意資源操做的同步性
(3).一個action被調用多少次?這是就能夠使用action的不安全性,定義一個變量count便可
8.bean-message標籤的講解
bean-message:國際化信息,key是信息的關鍵字,其對應value是資源文件
bean-message的使用:
第一步:定義資源文件,
com.itcast.ApplicationResources.properties;com.itcast.ApplicationResources_zh-cn;ApplicationResoruces是基名,後面能夠跟不一樣國家的名稱
第二步:在struts-config中添加:<message-resourcesparameter="com.itcast.ApplicationResources"key="myKey"/>
例如:<message-resourcesparameter"cn.itcast.ApplicationResource" key="myKey">寫的是基名,在資源文件中定義greeting=welcome username=root password=root鍵值對key屬性是防止全部的properties文件混淆
第三步:在頁面中使用:bean:message test<br> <bean:message bundl
e="myKey"key="userName"/> <bean:message bundle="myKey"key="password"/>
例如:
<tr>
<td colspan="2">
<bean-message bundle="myKey"key="greeting"/>
</td>
</tr>
<tr>
<td colspan="2">
<bean-message bundle="myKey"key="username"/>
</td>
</tr>
第四步:切換瀏覽器的語言類型,工具-->Internet選項->常規->語言
在struts-config.xml文件內的<message-resources>標籤中也能夠不指定key屬性,那麼,該<message-resources>標籤指定資源包將成爲struts的默認資源包;響應的,在JSP文件內的<bean:message>標籤中特能夠不指定bundle屬性,這表示要在struts的默認資源包中去查找信息.
9.bean-write標籤的講解
<bean-writescope="request" name="user"property="username"/>
從哪一個域中輸出指定bean以及bean的一些屬性
10. DispatchAction的講解
(1).實現一個模塊,須要對學生信息進行CRUD操做:
AddStudentAction
DeleteStudentAction
UpdateStudentAction
QueryStudentAction
分別調用四個類的execute方法,可是四個定義四個類有點過多.全部能夠合併相關的Action
(2).特殊的Action的使用:DispatchAction,起做用是能夠減小Action的數量,使用方法:繼承DispatchAction,在裏面添加上須要使用的全部方法,參數和返回值與原來的execute方法徹底同樣,配置文件中帶上parameter屬性,如parameter="opertationType";使用的時候才用以下形式:/*.do?operationType=add
(3).一個Action能夠調用CRUD的方法,DispatchAction中的四個方法的參數和返回類型都和原先的execute方法同樣,就是將execute複製四份,同時將方法的名稱改成addStudent,deleteStudent,updateStudent,queryStudent便可,而後在這四個方法中編寫響應的代碼.
(4).在地址欄中輸入..../DispatchAction.do?operationType=addStudnt,就是訪問增長學生頁面
(5).第一步:能夠開發一個DispatchAction的子類,這裏的方法注意必須與原來的execute方法同參數同返回值
第二步:配置parameter屬性
11.ForwardAction的講解
(1).訪問jsp訪問形式:須要統一訪問模式*.do;全部的jsp在web-inf下,因此請求jsp以前須要先訪問action
(2).由於每一個jsp對應一個Action,可能要許多Action,因此咱們只定義一個跳轉Action:ForwardAction 訪問:/testForwardAction.do,目的是統一以*.do形式訪問全部的模塊;<action path="/testForwardAction"forward="/TestForwardAction.jsp"/>
12. logic_iterate標籤的講解
(1).邏輯標籤:邏輯庫的標記可以用來處理外觀邏輯而不須要使用scriptlet,struts邏輯標籤庫包含的標記可以有條件的產生輸出文本,在對象集合中循環從而重複的產生輸出文本,以及應用程序流程控制,它也提供了一組在jsp頁面中處理流程控制的標記,這些標記封裝在文件名爲struts-logic.tld的標記包中,邏輯標記庫定義的標記可以執行下列三個功能:條件邏輯,重複,轉發/重定向響應
(2).Logic:iterate的使用:單重循環,雙重循環,對指定的集合的循環,這個集合必須是一個Iterator,Collection,Map,Array;
(3).
<%String[]usernames={"aa","bb","cc","dd"};
request.setAttribute("usernames",usernames);%>
<logic:iterate id="username" scope="request"name="usernames">
${username}
</logic:iterate>
name所指代的bean必須是一個集合類型
name+property:每個人的全部愛好:
User mengfanlong = new User();
mengfanlong.setUsername("mengfanlong");
String[]mengfanlongFavorites={"sports","sleep","study"};
mengfanlong.setFavorites(mengfanlongFavorites);
用ArrayList存儲用戶
<logic:iterate id="user"scope="request" name="userList">
${user.username}
<logic:iterate id="favorite"name="user" property="favorites">
${favorite}
</logic:iterate>
</logic:iterate>
(4).id是迭代時的臨時屬性,還有其餘屬性:length,offset,indexId等控制循環變量
13.struts插件的講解
(1).插件(Plugin)生命週期方法:init,destroy,用戶提供setter方法,告訴ActionServlet中心控制器把屬性設置
(2).應用:在struts啓動時把hibernate加載進來,就是要把hibernate的配置文件讀進來,同時打開hibernate的sessionfactory
須要struts.jar包+hibernate.jar包
設計一個類 HibernatePlugin,實現plugin接口,讀取hibernate配置文件,打開SessionFactory;
在struts-config.xml配置文件中添加一對<plugin>標籤,在plugin中加上子標籤
(3).
<plug-inclassName="cn.itcast.HibernatePlugin">
<set-property property="hibernateConfigFile"value="/WEB-INF/hibernate.cfg.xml"/>
</plug-in>
(4).struts當服務啓動時啓動,插件在struts啓動時讀取配置文件時啓動.能夠在中心控制器ActionServlet的init方法對plugin初始化,destroy銷燬了
(5).服務啓動-->ActionServlet->讀取struts-config.xml->根據各類標籤的內容進行一系列的初始化(plugin,ActionMapping)
14.struts的MVC組件
(1).組件:ActionServlet,ActionClasses,ActionMapping,ActionForward,ActionFormBean
(2).struts中的MVC:
第一:模型:本質上來講在struts中model是一個商業邏輯類,開發者實現商業邏輯
第二:視圖:View是由與控制器Servlet配合工做的一整套JSP定製標籤庫構成,利用他們能夠快速創建應用系統的界面
第三:控制器:前端控制器是一個Servlet,它將客戶端請求轉發到相應的後端控制器Action類
15.struts的工做原理
第一步:初始化,讀取struts-config.xml,Struts框架總控制器(ActionServlet)是一個Servlet,在web.xml中配置成自動啓動的Servlet,讀取配置文件(struts-config.xml)的配置信息,爲不一樣的struts模塊初始化相應的ModuleConfig對象:ActionConfig、ControlConfig、FormBeanConfig、ForwardConfig、MessageResourceConfig
第二步:等待Http請求:用戶提交表單或調用URL向Web應用服務器提交一個請求,請求的數據用HTTP協議上傳給Web服務器
第三步:填充FormBean:實例化、復位、ActionServlet拿到用戶的數據填充FormBean、校驗、保存等,(*.do)從ActionConfig中找到對應該請求的Action子類,如沒有對應的Action,控制器直接轉發給JSP或靜態頁面,若有相應的Action且這個Action有一個相應的ActionForm,ActionForm被實例化並用HTTP請求的數據填充其屬性,而且保存在ServletContext中(request或session中),這樣它們就能夠被其它Action對象或者JSP調用
第四步:將請求轉換到具體Action處理:控制器根據配置信息ActionConfig將請求派發到具體的Action,相應的FormBean一併傳給這個Action的execute()方法.(後臺控制器)
第五步:調用後臺的業務功能類完成商務邏輯:Action通常只包含一個execute方法,它負責執行相應的業務邏輯(調用其餘業務模塊),完成後返回一個ActionForward對象,控制器經過該ActionForward對象來進行轉發工做.
第六步:返回響應對象:Action根據業務處理的不一樣結果返回一個目標響應對象給總控制器(前端控制器actionservlet),該目標響應對象對應一個具體的JSP頁面或另外一個Action
第七步:轉換Http請求到目標響應對象(jsp):總控制器根據業務功能action返回的目標響應對象,找到相應的資源對象,一般是一個具體的JSP頁面
第八步:Http響應:前幾步都在服務器端,這一步在客戶端,目標響應對象將結果展示給用戶目標響應對象(jsp)將結果頁面展示給用戶
控件:總控制器ActionServlet的做用最大,Action(execute)ActionForm,配置文件,將各個模塊鏈接起來
注意web.xml和struts-config.xml的區別,總控制器ActionServlet要在web.xml中註冊,ActionForm中的屬性,Action等的信息都在struts-config.xml中註冊.
16.Struts相關基礎理論介紹
(1).案例:實現一個用戶登陸功能,若是用戶輸入的用戶和密碼都正確,就跳轉到登陸成功頁面,不然跳轉到登陸錯誤頁面
(2).知識點:爲何要使用Struts,FrameWork的概念,Struts的概念和體系結構,Struts的工做原理,Struts的組件,Struts配置文件簡介,Struts標記庫,錯誤處理框架,校驗框架,高級特性,Struts優缺點
(3).框架(Framework):人們用於解決相同或者類似類型問題的方案,可重用性,可擴展性,可收縮性
(4).Struts是Apache組織的一個開源項目,主要是採用了servlet和jsp技術來實現的,是基於Sun JavaEE平臺開發的
17.struts中的異常處理
(1).異常的做用:增長健壯性,模塊間傳遞信息
(2).配置異常:定製異常有兩種:全局異常(全部Action使用的)和局部異常(一個Action使用的),
(3).全局異常的定義方法:
<global-exceptions>
<exceptionkey="user.login.invalideUser" path="/Login.jsp"type="com.future.struts.MyException"/>
</global-exceptions>
局部異常的定義方法:
<action-mappings>
<action attribute="loginForm" name="loginForm"path="/loginAction" scope="request"type="com.future.struts.LoginAction" validate="false"/>
<exceptionkey="user.login.invalideUser" path="/Login.jsp"/>
</action-mappings>
(4).當Action的execute方法拋出異常時,調用異常處理器類ExceptionHandler
(5).怎麼使用:
第一步:配置<exception>能夠指明path,key,type,Path是指出現異常後的跳轉頁面,Key是指異常信息的鍵,對應的值在資源文件當中,Type所要處理的異常
第二步:在相應的action中的execute方法拋出異常
第三步:在異常處理頁面(path所指頁面)使用html:errors標籤打印提示信息
18.struts註冊程序的組件設計和流程分析
(1).編寫action,actionform,首先編寫actionform,由於action中要用到actionform,在struts-config.xml中註冊
actionform,action<form-bean name="addStudentForm"type="cn.itcast.AddStudentForm"></form-bean> <action path="/addStudentAction"type="cn.itcast.AddStudentAction"name=""></action>
,還有跳轉頁面:
<forwardname="addStudentSuccess"path="/AddStudentSuccess.jsp"/><forwardname="addStudentFailure" path="/AddStudent.jsp"/>
(2).對action的編寫要控制線程安全
(3).ActionServlet將請求參數封裝成FormBean的屬性時進行轉換而致使的結果,ActionServlet內部調用BeanUtil這個工具包來將字符串類型的請求參數轉換成FormBean中對應的屬性的類型,而後再將轉換結果裝配到FormBean中,這時候能夠打印出FormBean中各個屬性的結果看看,就大概清楚BeanUtil轉換後的結果了,咱們在之後的項目實戰課程中有對此問題的詳細分析和巧妙的解決方案
19.搭建struts開發環境
導入jar包,使用相關類,創建一個配置文件:struts-config.xml 放在web-inf下,web.xml註冊struts中心控制器---ActionServlet,注意事項:struts配置文件的位置,預先加載控制器,能夠到struts自帶的例子程序,從這些例子程序中的web-inf項目下拷貝struts-config.xml文件
20.動態FormBean的講解
(1).動態form不須要用戶本身寫代碼ActionForm
(2). <form-bean name="dynaFormForm"type="org.apache.struts.action.DynaActionForm">
<form-property name="userName"type="java.lang.String"/>
<form-property name="password"type="java.lang.String"/>
<form-property name="age"type="java.lang.Integer"/>
</form-bean>
須要告訴form-bean的名稱,屬性名,屬性的類型等信息,其中type必須爲包裝類型.
(3).提交一個表單數據,用數據填充動態表單,在Action中的execute方法中,
DynaActionForm addStudentForm =(DynaActionForm)form;
String sname =(String)addStudentForm.get("sname");
java.sql.Date birth =(java.sql.Date)addStudentForm.get("birth");
21.分析struts程序的執行流程
(1).ie->服務器->控制器ActionServlet(前端控制器)(送ActionForm給後端控制器)->LoginAction(後端控制器,第一句要進行轉型LoginForm loginForm = (LoginForm)form;)->前端控制器->顯示頁面
(2).if(loginForm.getUsername().equals("itcast")){returnURLKeyWord= "loginSuccess";}else{returnURLKeyWord="loginFailure";}
(3).前端控制器是根據struts-config.xml配置文件找到後端控制器,跳轉到成功或者失敗的頁面也是根據struts-config.xml配置文件的.後端控制器纔是真正執行代碼.
(4).問題:誰來填充From?,何時填充?,根據什麼內容來填?
ActionServlet怎樣把請求派發給Action?
Action運行完後怎樣跳轉?
22.分析本身寫struts框架的思路
開發如下類:
ActionServlet:讀取配置文件dom4j,填充form,派發請求:調用對應的action的execute方法,查找響應,跳轉
ActionForm{reset(),validate()}
Action{execute(ActionMapping,ActionForm,HttpServletRequest,HttpServletResponse}
ActionMapping{path,name,type,validate}使用HashMap存儲
ActionForward{name,path}使用HashMap存儲
配置文件struts-config.xml
23.配置全局跳轉
ActionA----->Error.jsp
ActionB----->Error.jsp
ActionC----->Error.jsp
之前的方法是在三個Action中都寫上forward
全局跳轉:Action A,B,C---->Error.jsp,在struts-config.xml文件中添加標籤:<global-forwards><forward name="error"path="Error.jsp"/></global-forwards>
24.經過bean_define標籤入門struts標籤庫
(1).JSP視窗組件所使用的struts標記庫由四類標記組成:Bean標記:用來在JSP頁面中管理bean,Struts-bean.tld;邏輯標記:用來在JSP頁面中控制流程,Struts-logic.tld;HTML標記:用來生成HTML標記,在表單中顯示數據,使用會話ID對URL進行編程Struts-html.tld;tiles標記:使用動態模板構造普通格式的頁struts-tiles.tld
(2).Bean標記,這個標記庫中包含用於定義新bean、訪問bean及其屬性的標記,Bean標記庫將標記定義在四個子類別中:建立和複製bean的標記,腳本變量定義標記,bean翻譯標記,消息國際化標記.
(3).Bean:define:從已經有的變量或者變量的屬性定義一個新的變量:id,name,property,scope(老bean),toScope(新bean),
查找:到scope中查找name的變量獲取它的property,
定義:定義一個新變量的名字(id),同時這個屬性是必須的
保存:將新變量保存到toScope
25.經過代碼瞭解ActionForm的基本工做流程
(1).經過ActionForm的構造函數來觀察是否被實例化了,經過reset()方法來觀察是否執行了復位方法.set()方法,觀察他們三者的運行順序,
(2).首先調用構造方法,而後調用reset方法,而後在調用set方法,復位只須要只對請求復位.
26.用struts開發簡單的登錄示例程序
(1).建一個類,繼承ActionForm,註冊,修改配置文件struts-config.xml
(2).public classLoginForm extends ActionForm{
private String username = null;
private String password = null;
}
(3).在struts-config-xml文件中添加一個標籤<form-beans></form-beans>
(4).開發Action,創建一個類,繼承Action,覆蓋execute方法,須要強制轉型,調用其餘模塊,跳轉(根據關鍵字,關鍵字參照action中forward標籤中的name屬性),註冊:修改配置文件struts-config.xml,Path:指明調用者(jsp)能經過中心控制器ActionServlet找到Action,Type:指明該action類全名,Name:該action引用的form的名稱
(5).public classLoginAction extends Action{覆蓋execute方法}
27.用監聽器探索ActionForm如何被存儲
(1).怎樣檢查ActionForm被存儲了,有兩種方法:一種是從過程去查看,另外一種從結果去查看.
(2).從過程查看,經過監聽器,從結果查看execute方法
(3).製做監聽器:定義一個類,AttributeListener,而後實現兩個接口,HttpSessionAttributeListener,HttpRequestAttributeListener,在web.xml中寫入:<listener><listener-class>cn.itcast.AttributeListener</listener-class></listener>監聽session,request域中屬性的變化.
28.在execute方法中分析ActionForm的問題
(1).從結果查看,
AddStudentFormaddStudentFormInSession
=(ActionForm)request.getSession().getAttribute("addStudentForm");
或者
AddStudentFormaddStudentFormInScope
=null;if(mapping.getScope().equals("request")){addStudentFormInScope
=(AddStudentForm)request.getAttribute("addStudentForm");}else{addStudentFormInScope
=(AddStudentForm)request.getSession().getAttribute("addStudentForm");}從不一樣的域中獲取ActionForm
(2).參數form是和存儲在域中的form是相同的(ActionForm);
(3).沒有sname成員變量,可是有setName()方法,因此準確來講是看是否有setName();頁面中的控件<input type="text"name="name">中的name是否和ActionForm中成員變量的名稱同樣,其實不是當作員變量,而只是看標準的set方法,經過反射技術執行.
3、 Struts2
1. Action名稱的搜索順序
(1).獲取請求路徑的URL,例如URL是:http://server/struts2/path1/path2/path3/test.action
(2).首先尋找namespace爲/path1/path2/path3的package,若是不存在這個package則執行步棸3,若是存在這個package,則在這個package中尋找名字爲test的action,當在該package下尋找不到action時就會直接跑到默認namespace的package裏面中尋找action(默認的命名空間爲空字符串),若是在默認namespace的package裏面還尋找不到該action,頁面提示找不到action
(3).尋找namespace爲/path1/path2的package,若是不存在這個package,則轉至步棸4,若是存在這個package,則在這個package中尋找名字爲test的action,當在該package中尋找不到action時就會直接跑到默認namespace的package裏面去找名字爲test的action,在默認的namespace的package裏面還尋找不到該action,頁面提示找不到action
(4).尋找namespace爲/path1的package,若是不存在這個package則執行步棸5,若是存在這個package,則在這個package中尋找名字爲test的action,當在該package中尋找不到action時就會直接跑到默認namespace的package裏面去找名字爲test的action,在默認namespace的package裏面還尋找不到該action,頁面提示找不到action
(5).尋找namespace爲"/"的package,若是存在這個package,則在這個package中尋找名字爲test的action,當在package中尋找不到action或者不存在這個package時,都會去默認namespace的package裏面尋找action,若是仍是找不到,頁面提示找不到action
(6).在struts2中默認的處理後綴是.action,不加後綴也能夠
2. Action配置的各項默認值
(1).struts1中:<actionpath="/control/employee/addUI"forward="/WEB-INF/page/employeeAdd.jsp"/>實現請求轉發,action將請求轉發給視圖jsp
(2).在struts2中,<actionname="addUI"><result>/WEB-INF/page/employeeAdd.jsp</result></action>,不須要設置addUI的類路徑class屬性了
(3).Action配置中的各項默認值:
<package name="itcastnamespace="/test" extends="struts-default">
<action name="hellowrold"class="cn.itcast.action.HelloWorldAction"method="execute">
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
若是沒有爲action指定class,默認是ActionSupport,能夠查看ActionSupport的源代碼,首先交給ActionSupport類處理.
若是沒有爲action指定method,默認執行action中的execute()方法,這個方法的返回值爲"success";
(4).若是沒有指定result的name屬性,默認值爲success,正好和execute方法的返回值相同,因此能夠實現視圖的轉發
3.OGNL表達式
(1).OGNL表達式:OGNL是Object Graphic Navigation Language(對象圖導航語言)的縮寫,它是一個開源項目,struts2框架使用OGNL做爲默認的表達式語言
第一:相對EL表達式,它提供了平時咱們須要的一些功能,如:
支持對象方法的調用,如:xxx.sayHello();
第二:支持類靜態方法調用和值訪問,表達式的格式爲@[類全名 (包括包路徑) ]@[方法名|值名],例如:@java.lang.String@format('foo %s,'bar')或@cn.itcast.Constant@APP_NAME
第三:操做集合對象
(2).OGNL有一個上下文(Context)概念,說白了上下文就是一個MAP結構,他實現了java.utils.Map接口,在struts2中上下文(context)的實現爲ActionContext
(3).struts2中的OGNLContext是實現者爲ActionContext,它的結構爲:OGNL Context:ValueStack(值棧,他是根對象),parameters,request,session,application,attr,當struts2接受一個請求時,會迅速建立ActionContext,ValueStack,action,而後把action存放進ValueStack,因此action的實例變量能夠被OGNL訪問
(4).當要訪問某個對象只需在其前面加上一個'#',例如:#request,固然有一個特殊的例子,就是根對象,會省略'#',OGNL會設定一個根對象(root對象),在struts2中根對象就是ValueStack(值棧),若是要訪問根對象(即ValueStack)中的對象的屬性,則能夠省略#命名空間,直接訪問該對象的屬性便可.
(5).在struts2中,根對象ValueStack的實現類爲OgnlValueStack,該對象不是咱們想象的只存放單個值,而是存放一組對象,在OgnlValueStack類裏有一個List類型的root變量,就是使用它存在一組對象,在root變量中處於第一位的對象叫棧定對象(存放action),一般咱們在OGNL表達式裏直接寫上屬性的名稱便可訪問root變量對象的屬性,搜索順序是從棧定對象開始尋找,若是棧定對象不存在該屬性,就會從第二個對象尋找,若是沒有找到就從第三個對象尋找,依次往下訪問,直到找到爲止。你們注意:struts2中,OGNL表達式須要配合struts標籤才能夠使用,如:<s:property value="name"/>,value屬性接受的是OGNL表達式,搜索是否含有name屬性
(6).因爲ValueStack(值棧)是struts2中OGNL的根對象,若是用戶須要訪問值棧中的對象,在JSP頁面能夠直接經過下面的EL表達式訪問ValueStack(值棧)中對象的屬性:
${foo}得到值棧中某個對象的foo屬性
若是訪問其餘Context中的對象,因爲他們不是根對象,因此在訪問時,須要添加#前綴
第一:application對象:用於訪問ServletContext,例如#application.userName或者#application['userName'],至關於調用ServletContext的getAttribute("username");
第二:session對象:用來訪問HttpSession,例如#session.userName或者#session['userName'],至關於調用session.getAttribute("userName");
第三:request對象:用來訪問HttpServletRequest屬性(attribute)的Map,例如#request.userName或者#request['userName'],至關於調用request.getAttribut("userName");
第四:parameters對象:用於訪問HTTP的請求參數,例如#parameters.userName或者#parameters['userName'],至關於調用request.getParameter("username");
第五:attr對象:用於按page->request->session->application順序訪問其屬性.
(7).爲什麼使用EL表達式可以訪問valueStack中對象的屬性:緣由是struts2對HttpServletRequest作了進一步的封裝,簡單代碼以下
public class StrutsRequestWrapper extends HttpServletRequestWrapper{
publicStrutsRequestWrapper(HttpServletRequest req){
super(req);
}
public Object getAttribut(String s){
ActionContext ctx = ActionContext.getContext();
Object attribute = super.getAttribute(s)//先從request範圍獲取屬性值
if(ctx !=null){
if(attribute==null){
....
ValueStack stack =ctx.getValueStack();
attribute=stack.findValue(s);//尋找規則就是前面的尋找規則
....
}
}
return attribute
}
}
EL表達式只能訪問ValueStack對象中的內容
(8).採用OGNL表達式建立List/Map集合對象,若是須要一個集合元素的時候(例如List對象或者Map對象),能夠使用OGNL中同集合相關的表達式,使用以下代碼直接生成一個List對象:
<s:set name="list"value="{'zhangming','xiaoi','liming'}"/>把當前迭代的對象放在值棧的棧頂
<s:iterator value="#list">
<s:property/><br>
</s:iterator>
Set標籤用於將某個值放入指定範圍
scope:指定變量被放置的範圍,該屬性能夠接受application,session,request,page或action.若是沒有設置該屬性,則默認放置在OGNL Context中.
value:賦給變量的值,若是沒有設置該屬性,則將ValueStack棧頂的值賦給變量
生成一個Map對象:
<s:set name="foobar"value="#{'foo1':'bar1','foo2':'bar2'}"/>
<s:iteratorvalue="#foobar">//迭代標籤,把當前迭代的對象放在值棧的棧頂(entry對象),foobar是Map對象和request等對象是同等地位,訪問時須要使用'#'
<s:property value="key"/>=<s:propertyvalue="value"/><br>
</s:iterator>
數字不用任何符號,字符串使用單引號('),對於Map採用的是maps.entrySet()這個方式進行迭代的.
(9).property標籤用於輸出指定值:
<s:set name="name"value="kk"/>
<s:property value="#name"/>
default:可選屬性,若是須要輸出的屬性值爲null,則顯示該屬性指定的值
escape:可選屬性,指定是否格式化HTML代碼
value:可選屬性,指定須要輸出的屬性值,若是沒有指定該屬性,則默認輸出ValueStack棧頂的值
id:可選屬性,指定該元素的標識
(1)0.對於集合類型,OGNL表達式能夠使用in和not in兩個元素符號,其中in表達式用來判斷某個元素是否在指定的集合對象中,not in判斷某個元素是否不在指定集合對象中,以下所示:
in表達式:
<s:if test="'foo' in {'foo','bar'}">
在
</s:if>
<s:else>
不在
</s:else>
no in 表達式
<s:if test="'foo' not in {'foo','bar'}">
不在
</s:if>
<s:else>
在
</s:else>
(11).OGNL表達式的投影功能,除了in和not in以外,OGNL還容許使用某個規則得到集合對象的子集,經常使用的有如下3個相關操做符:
第一:?:得到全部符號邏輯的元素
第二:^:得到符合邏輯的第一個元素
第三:$:得到符合邏輯的最後一個元素
例如代碼:
<s:iterator value="books.{?#this.price>35}">
<s:propertyvalue="title"/>-$<s:propertyvalue="price"/><br>
</s:iterator>
在上面代碼中,直接在集合後緊跟{}運算符代表用於取出該集合的子集,{}內的表達式用於獲取符合條件的元素,this指的是爲了從大集合books篩選數據到小集合,須要對大集合books進行迭代,this表明當前迭代的元素,本例的表達式用於獲取集合中價格大於35的書集合.
public class BookAction extends ActionSupport{
private List<Book> books;
@Override
public String execute(){
books = new LinkedList<Book>();
books.add(new Book("aadfsd","spring",23));
books.add(newBook("basdfd","ejb3.0",15));
}
}
4.result配置的各類視圖轉發類型
(1).在struts1中有兩種視圖轉發類型:容器內轉發,容器外轉發(重定向);
<actionpath="/control/employee/manage".../>
<forwardname="add">/index.jsp</forward>
<forward name="add"redirect=""/index.jsp</forward>
</action>
(2).struts2中的視圖轉發類型:result配置相似於struts1中的froward,可是struts2中提供了多種結果類型,經常使用的類型有:dispatcher(默認值)、rdierect、redirectAction、plainText;dispatcher對應於struts1中的容器內部請求轉發,redirect對應於struts1中的容器外部轉發(重定向)
(3).<actionname="helloworld"class="cn.itcast.action.HelloWorldAction">
<resultname="success">/WEB-INF/page/hello.jsp</result>
</action>
在result中還能夠使用${屬性名}表達式訪問action中的屬性,表達式裏的屬性名對應的action中的屬性,以下:<resulttype="redirect">/View.jsp?id=${id}</result>,使用重定向可能須要將Action中的數據屬性代入視圖頁面,這種方式過重要了,很實用.這種表達式叫作ognl表達式,struts1中是沒有的,只能將屬性值在代碼中寫死了,不像struts2中的這個表達式,很靈活.當傳遞的屬性是中文時,須要進行URLEncoder.encode("傳智播客","UTF-8")編碼.
(4).下面是redirectAction結果類型的例子,若是重定向的action中同一個包下:
<resulttype="redircetAction">helloworld</result>
若是重定向的action在別的命名空間下:
<resulttype="redirectAction">
<paramname="actionName">helloworld</param>
<paramname="namespace">/test</param>
</result>
當添加用戶完後,能夠回到一個用戶列表,此時能夠重定向到action
(5).plaintext顯示原始文件內容,例如:當咱們須要原樣顯示JSP文件源代碼的時候,咱們能夠使用此類型
<result name="source"type="plainText">
<paramname="location">/xxx.jsp</param>
<paramname="charSet">UTF-8</param><!--指定讀取文件的編碼-->
</result>
在Eclipse中jsp是用UTF-8編碼存放的,當讀取jsp的內容時,是用本地字符編碼的,可能出現亂碼,因此要設置讀取文件的編碼集.
(6).瀏覽器重定向的JSP不能放在web-inf目錄中,而請求轉發的JSP能夠放在web-inf目錄中
(7).struts2中的全局視圖:
<global-results>
<resultname="index.jsp"></result>
<global-results>
和struts1中的全局視圖是很類似的
咱們能夠定義一個包,而後將全局視圖的配置放到這個包中
5.struts2經常使用標籤
(1).property標籤用於輸出指定值:
<s:setname="name" value="kk"/>
<s:propertyvalue ="#name"/>
default:可選屬性,若是須要輸出的屬性值爲null,則顯示該屬性指定的值
escape:可選屬性,指定是否格式化HTML代碼
value:可選屬性,指定須要輸出的屬性值,若是沒有指定該屬性,則默認輸出ValueStack棧頂的值
id:可選屬性,指定該元素的標識
(2).iterator標籤用於對集合進行迭代,這裏的集合包含List、Set和數組
<s:set name="list"value="{'aa','bb','cc'}"/>
<s:iterator value="#list"status="st">
<font color=<s:iftest="#st.odd">red</s:if><s:else>blue</s:else>>
<s:property/></font><br>
</s:iterator>
value:可選屬性,指定被迭代的集合,若是沒有設置該屬性,則使用ValueStack棧頂的集合,
id:可選屬性,指定集合裏元素的id(已被標註爲過期)
status:可選屬性,該屬性指定迭代時的iteratorStatus實例,該實例包含以下幾個方法:
int getCount(),返回當前迭代了幾個元素
int getIndex(),返回當前迭代元素的索引
boolean isEven(),返回當前被迭代元素的索引是否爲偶數
boolean isOdd(),返回當前被迭代元素的索引是不是奇數
boolean isFirst(),返回當前被迭代元素是不是第一個元素
boolean isLast(),返回當前被迭代元素是不是最後一個元素
(3).<s:setname="age" value="21" scope="request"/>
<s:iftest="#request.age==23">
23
</s:if>
<s:elseif test ="#age==21">
21
</s:elseif>
<s:else>
都不等
</s:else>
(4).url標籤:
<s:url action="helloworld_add"namespace="/test">
<s:param name="personid"value="3"/>
</s:url>
生成相似以下路徑:
/struts/test/helloworld_add.action?persionid=3
當標籤的屬性值做爲字符串類型處理時,"%"符號的用途是計算OGNL表達式的值
<s:set name="myurl"value='"http://www.foshanshop.net"'/>
<s:url value="#myurl"/>
<s:url value="%{#myurl}"/>
輸出結果:
#myurl
http://www.foshanshop.net
(5).表單標籤checkboxlist複選框
若是集合爲list
<s:checkboxlist name="list"list={'Java','Net','RoR','PHP'}" value="{'Java','Net'}"/>
<input type="checkbox"name="list" value="Java"checked="checked"/><label>Java</label>
<input type="checkbox"name="list" value="Net" checked="checked"/><label>Net</label>
<input type="checkbox"name="list" value="RoR" ><label>Java</label>
<input type="checkbox"name="list" value="Java"/><label>Java</label>
若是集合爲MAP
<s:checkboxlist name="map"list="#{1:'aa',2:'bb'}" listKey="key"listValue="value" value="{1}"/>
生成以下html代碼:
<input type="checkbox"name="map" value="1"checked="checked"/><label>aa</label>
<input type="checkbox"name="map" value="2" /><label>bb</label>
固然集合裏面存放的也能夠是對象類型
(6).單選框
<s:radio name="beans"list="#request_person" listKey="personid"listValue="name"/>
6.struts2的處理流程與Action的管理方式
(1).用戶請求->(查看web.xml文件)StrutsPrepareAndExecuteFilter->Interceptor(struts2內置的一些攔截器或用戶自定義攔截器)->Action(用戶編寫的Action類,相似Struts1中的action,針對每一次請求,都建立一個Action)->Result(相似struts1中的forward)->Jsp/html(響應)
(2).StrutsPrepareAndExecuteFilter是struts2框架的核心控制器,它負責攔截由<url-pattern>/"</url-pattern>指定的全部用戶請求,當用戶請求到達時,該fileter會過濾用戶的請求,默認狀況下,若是用戶請求的路徑不帶後綴或者後綴以.action結尾,這時請求將被轉入到struts2框架處理,不然struts2框架將略過該請求的處理,當請求轉入struts2框架處理時會先通過一系列的攔截器,而後再到Action,與struts1不一樣,struts2對用戶的每一次請求都會建立一個Action,因此struts2中的action是線程安全的.
7.XML配置方式實現對action的全部方法進行校驗
(1).基於XML配置方式實現對action的全部方法進行輸入校驗:
使用基於XML配置方式實現輸入校驗時,Action也須要繼承ActionSupport,而且提供校驗文件,校驗文件和action類放在同一個包下,文件的取名格式爲:ActionClassName-validation.xml,其中,ActionClassName爲action的簡單類名,-validation爲固定寫法,若是Action類爲cn.itcast.UserAction,那麼該文件的取名應爲:UserAction-validation.xml,下面是校驗文件的模板:
<validators>
<field name="username">
<field-validatortype="requiredstring">
<paramname="trim">true</param>
<message>用戶名不能爲空</message>
</field-validator>
</field>
</validators>
<field>指定action中要校驗的屬性,<field-validator>指定校驗器,上面指定的校驗器requirestring是由系統提供的,系統提供了能知足大部分驗證需求的校驗器,這些校驗器的定義能夠在xwork-2.x.jar中的com.opensymphony.xwork2.validator.validators下的default.xml中找到,<message>爲校驗失敗後的提示信息,若是須要國際化,能夠爲message指定key屬性,key的值爲資源文件中的key,在這個校驗文件中,對action中字符串類型的username屬性進行驗證,首先要求調用trim()方法去掉空格,而後判斷用戶名是否爲空.
(2).struts2提供的校驗器列表:
required(必填校驗器,要求field的值不能爲Null)
requiredstring(必填字符串校驗器,要求field的值不能爲null,而且長度大於0,默認狀況下會對字符串取錢後空格)
stringlength(字符串長度校驗器,要求field的值必須在指定的範圍內,不然校驗失敗,minLength參數指定最小長度,maxLength參數指定最大長度,trim參數指定校驗field以前是否去除字符串先後的空格)
regex(正則表達式校驗器,檢查被校驗的field是否匹配一個正則表達式,expression參數指定正則表達式,caseSensitive參數指定進行正則表達式匹配時,是否區分大小寫,默認值爲true)
int(整數校驗器,要求field的整數值必須在指定範圍內,min指定最小值,max指定最大值)
double(雙精度浮點數校驗器,要求field的雙精度浮點數必須在指定範圍內,min指定最小值,max指定最大值)
fieldexpression(字段OGNL表達式校驗器,要求field知足一個OGNL表達式,expression參數指定OGNL表達式,該邏輯表達式基於ValueStack進行求值,返回true時校驗經過,不然不經過)
email(郵件地址校驗器,要求若是field的值非空,則必須是合法的郵件地址)
URL(網址校驗器,要求若是field的值非空,則必須是合法的URL地址)
date(日期校驗器,要求field的日期值必須在指定範圍內,min指定最小值,max指定最大值)
conversion(轉換校驗器,指定在類型轉換失敗時,提示的錯誤信息)
visitor(用於校驗action中的符合屬性,它指定一個校驗文件用於校驗符合屬性中的屬性)
expression(OGNL表達式校驗器,expression參數指定ognl表達式,該邏輯表達式基於ValueStack進行求值,返回true時校驗經過,不然不經過,該校驗器不可用在字段校驗器風格的配置中)
(3).![CDATA[文本內容]]:文本內容不會被解析,只會原封不動的當作文本處理
(4).編寫校驗文件時,不能出現幫助信息:
在編寫ActionClassName-validation.xml校驗文件時,若是出現不了幫助信息,能夠按照下面方式解決:
windows->preferences->myeclipse->filesand editors->xml->xmlcatalog:點擊add,在出現的窗口中的location中選"file system"而後再xwork-2.1.2戒菸目錄的src\java目錄中選擇xwork-validator-1.0.3.dtd,回到設置窗口的時候,不要急着關閉窗口,應把窗口中的Key Type改成URI,Key改成http://www.opensymphoney.com/xwork/xwork-validaor-1.0.3.dtd
8.XML配置方式實現對action的指定方法校驗
(1).基於XML配置方式對指定action方法實現輸入校驗:
當校驗文件的取名爲ActionClassName-validation.xml時,會對action中的全部處理方法實施輸入校驗,若是你只須要對action中的某個action方法實施校驗,那麼校驗文件的取名應爲:ActionClassName-ActionName-validation.xml,其中ActionName爲struts.xml中的action的名稱,例如:在實際應用中,常有如下配置:
<action name="user_*"class="cn.itcast.action.UserAction" method="{1}">
<resultname="success">/WEB-INF/page/message.jsp</result>
<resultname="input">/WEB-INF/page/addUser.jsp</result>
</action>
UserAction中有如下兩個處理方法:
public String add() throws Exception{
}
public String update() throws Exception{
}
要對add()方法實施驗證,校驗文件的取名爲:UserAction-user_add-validation.xml
要對update()方法實施驗證,校驗文件的取名爲:UserAction-user_update-validation.xml
(2).基於XML校驗的一些特色:
當爲某個action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml兩種規則的校驗文件時,系統按下面順序尋找校驗文件:
ActionClassName-validation.xml
ActionClassName-ActionName-validation.xml
系統尋找到第一個校驗文件時還會繼續搜索後面的校驗文件,當搜索到全部校驗文件時,會把校驗文件裏的全部校驗規則彙總,而後所有應用於action方法的校驗,若是兩個校驗文件中指定的校驗規則衝突,則只使用後面文件中的規則。
當action繼承了另外一個action,父類action的校驗文件會先被搜索到
假設UserAction繼承BaseAction
<actionname="user" class="cn.itcast.action.UserAction"method="{1}">
</action>
訪問上面的action,系統先搜索父類的校驗文件:BaseAction-validation.xml,BaseAction-user-validation.xml,接着搜索子類的校驗文件:UserAction-validation.xml,UserAction-user-validation.xml,應用於上面action的校驗規則爲這四個文件的總和
9.動態方法調用和使用通配符定義action
(1).在struts1中實現方法的動態調用:
<actionpath="/control/employee/manage" type="....DispatchAction"parameter="method"/>
</action>
/control/employee/manage?method=addUI
可是Action必須繼承DispatchAction
(2).在struts2中有兩種方式:
第一種:(struts2.1版本後就不建議使用了)是動態方法調用:若是Action中存在多個方法時,咱們能夠使用!+方法名調用指定方法,以下:
public class HelloWorldAction{
private String message;
....
public String execute()throws Exception{
this.message="個人第一個struts2應用";
}
public String other() throws Exception{
this.message="第二個方法";
return "success";
}
}
假設訪問上面的action的URL路徑爲:/struts/test/helloworld.action,要訪問action的other方法,咱們就能夠這樣調用:/struts/test/helloworld!other.action,若是不想使用動態方法調用,咱們能夠經過常量struts.enable.DynamicMethodInvocation關閉動態方法調用:
<constantname="struts.enable.DynamicMethodInvocation"value="false"/>
第二種:使用通配符定義action(推薦使用的)
<package name="itcast"namespace="/test" extends="struts-default">
<action name="helloworld_*"class="cn.itcast.action.HelloWorldAction" method="{1}>
<resultname="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
public class HelloWorldAction{
private String message;
....
public String execute()throws Exception{
this.message="個人第一個struts2應用";
}
public String other() throws Exception{
this.message="第二個方法";
return "success";
}
}
要訪問other()方法,能夠經過這樣的URL訪問:/test/helloworld_other.action
name="helloworld_*"後可根據多個*,method={1},'1'表示匹配*的位置
name="helloworld_*_*",method={2}:要訪問other()方法,能夠經過這樣的URL訪問:/test/helloworld_xxx_other.action
10.對action指定的方法進行校驗
手工編寫代碼實現對action指定方法輸入校驗:
經過validateXxx()方法實現,validateXxx()只會校驗action中方法名爲Xxx的方法,其中Xxx的第一個字母要大寫,當某個數據校驗失敗時,咱們應該調用addFieldError()方法往系統的fieldErrors添加校驗失敗信息,(爲了使用addFieldError()方法,action能夠繼承ActionSupport(),若是系統的fieldErrors包含失敗信息,struts2會將請求轉發到名爲input的result,在input視圖中能夠經過<s:fielderror/>顯示失敗信息.
validateXxx()方法使用例子:
public Stringadd() throws Exception{return "success";}
public voidvalidateAdd() {
if(username==null&&"".equals(username.trim()))this.addFieldError("username","用戶名不能爲空");
}
驗證失敗後,請求轉發至input視圖:<resultname="input">/WEB-INF/page/addUser.jsp</result>
在addUser.jsp頁面中使用<s:fielderror/>顯示失敗信息
11.對Action中全部方法進行輸入校驗
(1).在struts2中,咱們能夠實現對action的全部方法進行校驗或者對action的指定方法進行校驗
(2).對於輸入校驗struts2提供了兩種實現方法:一種是採用手工編寫代碼實現,另外一種是基於XML配置方式實現
(3).手工編寫代碼實現對action中全部方法輸入校驗:經過重寫validate()方法實現,validate()方法會校驗action中全部與execute方法簽名相同的方法,當某個數據校驗失敗時,咱們應該調用addFieldError()方法往系統的fieldErrors添加校驗失敗信息(爲了使用addFieldError()方法,action能夠繼承ActionSupport(),若是系統的fieldErrors包含失敗信息,struts2會將請求轉發到名爲input的result,在input視圖中能夠經過<s:fielderror/>顯示失敗信息.
validate()使用例子:
public voidvalidate(){
if(this.mobile==null||"".equals(this.mobile.trim())){this.addFieldError("username","手機號不能爲空")}else{if(Pattern.compile("^1[358]\\d{9}").matcher(this.mobile.trim()).matchers()){this.addFieldError("mobile","手機號的格式不正確");}}
}
驗證失敗後,請求轉發至input視圖:
<resultname="input">/WEB-INF/page/addUser.jsp</result>
在addUser.jsp頁面中使用<s:fielderror/>顯示失敗信息
12.多文件上傳
(1).多文件上傳,就是在一個文件上傳的基礎上,將屬性File變成數組類型File[]類型便可,同時該字段的名稱必需要和上傳頁面中屬性name的名稱同樣.
而後進行一次迭代,就能夠獲得全部的文件
13.防止表單重複提交
<s:token>標籤防止表單重複提交
第一步:在表單中加入<s:token/>
<s:form action="helloworld_other"method="post" namespace="/test">
<s:textfieldname="person.name"/><s:token/><s:submit/>
</s:form>
第二步:<action name="helloworld_*"class="cn.itcast.action.HelloWorldAction" method="{1}">
<interceptor-refname="defaultStack"/>
<interceptor-refname="token"/>
<resultname="invalid.token">/WEB-INF/page/message.jsp</result>
<result>/WEB-INF/page/result.jsp</result>
</action>
以上配置加入了"token"攔截器和"invalid.token"結果,由於"token"攔截器在會話的tlent與請求的token不一致時,將會直接返回"invalid.token"結果
在debug狀態,控制檯出現下面信息,是由於Action中並無struts.token和struts.token.name屬性,咱們不用關心這個錯誤
使用了<s:form/>標籤能夠不指定action的上下文標籤路徑,能夠經過命名空間實現.和前面的原理是同樣的,在路徑後面添加上sessionid號,只是這步操做不須要咱們本身獲得sessionid號,struts幫咱們操做.
在值棧中的對象,訪問無需添加'#'
14.訪問或添加幾個屬性
(1).訪問或添加request/session/application屬性,在struts2中的Action中的execute方法中沒有Servlet api(沒有響應的參數);
public String scope() throws Exception{
ActionContextctx=ActionContext.getContext();
ctx.getApplication().put("app","應用範圍");
ctx.getSession().put("ses","session範圍");
ctx.put("request","request範圍");
return "scope";
}
<body>
${applicationScope.app}
${sessionScope.ses}
${requestScope.req}
</body>
(2).獲取HttpServletRequest/HttpSession/ServletContext/HttpServletResponse對象:
方法一:經過ServletActionContext類直接獲取
public String rsa() throws Exception{
HttpServletRequest request =ServletActionContext.getRequest();
ServletContextservletContext=ServletContext.getServletContext();
request.getSession();
HttpServletResponseresponse=ServletActionContext.getResponse();
return "scope";
}
方法二:實現指定接口,由struts框架運行時注入:
public class HelloWorldAction implementsServletRequestAware,ServletResponseAware,ServletContextAware{
private HttpServletRequest request;
private ServletContext servletContext;
private HttpServletResponse response;
public voidsetServletRequest(HttpServletRequest req){
this.request.req;
}
public voidsetServletResponse(HttpServletResponse res){
this.response=res;
}
public voidsetServletContext(ServletContext ser){
this.servletContext=ser;
}
注意1和2的不一樣,一個不須要獲得對象,一個須要獲得對象,因此要區分兩個的應用場景
}
15.解決struts配置文件無提示問題
找到struts2.0.dtd文件便可,windows->preferences->MyEclipse->XML->XMLCatalog,點擊添加strut2.dtd
16.介紹struts2及struts2開發環境的搭建
(1).struts2是在webwork2基礎發展而來的,和struts同樣,struts2也屬於MVC框架,不過有一點你們須要注意的是:儘管struts2和struts1在名字上的差異不是很大,可是struts2和struts1在代碼編寫分割上幾乎是不同的,那麼既然有了struts1,爲什麼還要推出struts2,主要是由於有一下有點:
第一:在軟件設計上struts2沒有像struts1那樣跟Servlet api和struts api有着緊密的耦合,struts2的應用能夠不依賴於servlet api 和struts api,struts2的這種設計屬於無侵入式的設計,而struts1卻屬於侵入式設計,由於其的
execute()方法中的參數爲ActionMapping,ActionForm,HttpServletRequest,HttpServletResponse
第二:struts2提供了攔截器,利用攔截器能夠進行AOP編程,實現如權限攔截等功能
第三:struts2提供了類型轉換器,咱們能夠把特殊的請求參數轉換成須要的類型,在struts1中,若是咱們要實現一樣的功能,就必須向struts1的底層實現BeanUtils註冊類型轉換器才行
第四:struts2提供支持多種表現層技術,如:JSP,freeMarker,Velocity等
第五:struts2的輸入校驗能夠對指定方法進行校驗,解決了struts1長久之痛,struts1中的validate方法對全部的方法進行校驗
第六:提供了全局範圍、包範圍、和Action範圍的國際化資源文件管理實現.
(2).搭建struts2的環境和struts1是相同的,第一步導入相關包,第二步創建struts2的配置文件,第三步在web.xml中註冊struts2框架的配置
(3).所需的包:struts2-core-2.x.x.jar,xwork-2.x.x.jar(webwork的核心架包),ognl-2.6.x.jar
(4).struts2默認的配置文件爲struts.xml,該文件須要放在/web-inf/classes目錄下
(5).在struts1中,struts框架是經過servlet啓動的,在struts2中,struts框架是經過Filter啓動的,它在web.xml中的配置以下所示:能夠參照struts文件夾下的例子中拷貝,在strutsperpareExecuteFilter的init()方法中將會讀取類路徑下默認的配置文件struts.xml完成初始化操做,注意:struts2讀取到struts.xml的內容後,以javabean形式存放在內存中,之後struts2對用戶的每次請求處理將使用內存中的數據,而不是每次都讀取struts.xml文件
(6).自從struts2.1.3之後,下面的FilterDispatcher已經標註爲過期了,struts2.1.3後期版本爲StrutsPrepareAndExecuteFilter類
17.開發第一個應用
(1).在struts.xml中的配置:
<package name="itcast"namespace="/test" extends="struts-default">
<actionname="helloworld" class="cn.itcast.action.HelloWorldAction"method="execute">
<resultname="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
在struts2框架中使用包來管理Action,包的做用和Java中的類包是很是相似的,它主要用於管理一組業務功能相關的action,在實際應用中,咱們應該吧一組業務功能相關的Action放在同一個包下
配置包時必須指定name屬性,該name屬性能夠任意取名,但必須惟一,它不對應java的類包,若是其餘包要繼承該包,必須經過該屬性進行引用,包的namespace屬性用於定義該報的命名空間,命名空間做爲訪問該包下Action的路徑的一部分,如訪問上面例子的Action,訪問路徑爲:/test/helloworld.action,namespace屬性能夠不配置,對本例而言,若是不指定該屬性,默認的命名空間爲" "(空字符串).固然配置能夠減小重複的代碼,struts1中的重複代碼就能夠使用命名空間來解決
一般每一個包都應該繼承struts-default包,由於struts2不少核心的功能都是攔截器來實現的,如:從請求中把請求參數封裝轉到action、文件上傳和數據驗證等都是經過攔截器實現的,struts-defaul定義了這些攔截器和Result類型,能夠這麼說:當包繼承了struts-default才能使用struts2提供的核心功能,struts-default包是在struts2-core-2.x.x.jar文件中的struts-default.xml中定義,struts-default.xml也是struts2默認配置文件,struts2每次都會自動加載struts-default.xml文件,包還能夠經過abstract="true"定義爲抽象包,抽象包中不能包含action,能夠查看struts-default.xml文件中,就能夠看到定義了不少攔截器
<result></result>和struts1中的forward很類似,定義視圖
(2).public Stringexecute(){return 視圖的名稱;}注意到這個方法和struts1不一樣,沒有參數,返回類型也不一樣,這就下降了耦合性,非侵入式的編程了.
(3).在jsp中使用el表達式便可${message},message是Action中的一個方法getMessage()方法,而不是根據Action中的成員變量message
18.配置Action範圍國際化資源文件
(1).咱們也能夠爲某個action單獨制定資源文件,方法以下:在Action類所在的路徑,放置ActionClassName_language_country.properties資源文件,ActionClassName爲Action類的簡單名稱當查找指定key的消息時,系統會先從ActionClassName_language_country.properties資源文件查找,若是沒有找到對應的key,而後沿着當前包往上查找基本名爲package的資源文件,一直找到最頂層包,乳溝尚未找到對應的key,最後會從常量struts.custom.i18n.resources指定的資源文件中查找
(2).JSP中直接訪問某個資源文件
struts2爲咱們提供了<s:i18n>標籤,使用<s:i18n>標籤咱們能夠在類路徑下直接從某個資源文件中獲取國際化數據,而無需任何配置:
<s:i18n name="itcast">
<s:text name="welcome"/>
</s:i18n>
itcast爲類路徑下資源文件的基本名
若是要訪問的資源文件在類路徑的某個包下,能夠這樣訪問:
<s:i18nname="cn/itcast/action/package">
<s:text name="welcome">
<s:param>小張</s:param>
</s:text>
</s:i18n>
上面訪問cn.itcast.action包下基本名爲package的資源文件
19.配置包範圍的國際化資源文件
(1).在一個大型應用中,整個應用有大量的內容須要實現國際化,若是咱們把國際化的內容都放置在全局資源屬性文件中,顯然會致使資源文件變得過於龐大、臃腫,不便於維護,這個時候咱們能夠針對不一樣模塊,使用包範圍來組織國際化文件
方法以下:在java的包下放置package_language_country.properties資源文件,package爲固定寫法,處於該包及子包下的action均可以訪問該資源,當查找指定key的消息時,系統會先從package資源文件中查找,當找不到對應的key時,纔會從常量struts.custom.i18n.resources指定的資源文件中尋找.
20.配置國際化全局資源文件、輸出國際化信息
(1).準備資源文件,資源文件的命名格式以下:
baseName_language_country.properties
baseName_language.properties
baseName.properties
其中baseName是資源文件的基本名,咱們能夠自定義,可是language和country必須是java支持的語言和國家。如:
中國大陸:baseName_zh_CN.properties
美國:baseName_en_US.properties
(2).如今爲應用添加兩個資源文件:
第一個存放中文:itcast_zh_CN.properties
內容爲:welcom=歡迎來到傳智播客
第二個存放英語(美國):itcast_en_US.properties
內容爲:welcome=welcom to itcast
(3).對於中文的屬性文件,咱們編寫好後,應該使用JDK提供的native2ascii命令把文件轉換爲unicode編碼的文件,命令的使用方式以下:
native2ascii 源文件.properties 目標文件.properties,在MyEclipse6.6版本以及後面的版本會自動轉換.
(4).struts2有:全局範圍,包範圍,action範圍的資源文件
(5).配置全局資源與輸出國際化信息:
當準備號資源文件以後,咱們能夠在struts.xml中經過:struts.custom.i18n.resources常量把資源文件定義爲全局資源文件,以下:
<constantname="struts.custom.i18n.resources" vlaue="itcast"/>
itcast爲資源文件的基本名
後面咱們就能夠在頁面或在action中訪問國際化信息:
在JSP頁面中使用<s:text name=""/>標籤輸出國際化信息:
<s:textname="user"/>,name爲資源文件中的key
在Action類中,能夠繼承ActionSupport,使用getText()方法獲得國際化信息,該該方法的第一個參數用於指定資源文件中的key,
在表單標籤中,經過key屬性指定資源文件中的key,如:
<s:textfieldname="realname" key="user"/>
21.請求參數接受
(1).struts1中是使用ActionForm接受用戶的請求參數
(2).採用基本類型接受請求參數(get/post):
在Action類中定義與請求參數同名的屬性,struts2便能自動接受請求參數並賦予給同名屬性:請求路徑:http://localhost:8080/test/view.action?id=78
public classProductAction{
private Integerid;
public voidsetId(Integer id){//struts2經過反射技術調用與請求參數同名的屬性的setter方法獲取請求參數值
this.id=id;
}
public IntegergetId(){return id;}
}
(3).採用複合類型接受請求參數
請求路徑:http://localhost:8080/test/view.action?product_id=78
public class ProductAction{
private Product product;
public void setProduct(Product product){htis.product=product;}
public Product getProduct(){returnproduct;}
}
struts2首先經過反射技術調用Product的默認構造器建立product對象,而後再經過反射技術調用product中與請求參數同名的屬性的setter方法來獲取請求參數值
(4).關於struts2.1.6版本中存在一個Bug,及接受到的中文請求參數爲亂碼(以post方式提交),緣由是struts2.1.6在獲取並使用了請求參數後才調用HttpServletRequest的setCharacterEncoding()方法進行編碼設置,致使應用使用的就是亂碼請求參數,這個Bug在struts2.1.8中已經解決,若是你使用的是struts2.1.6,要解決這個問題,你能夠這樣作:新建一個Filter,把這個Filter放置在Struts2的Filter以前,而後再doFilter()方法中添加如下代碼:
public void doFilter(..){
HttpServletRequest req=(HttpServletRequest)request;
req.setCharacterEncoding("UTF-8");
filterchain.doFilter(request,response);
}
22.全局類型轉換器
自定義全局類型轉換器:將上面的類型轉換器註冊爲全局類型轉換器:在WEB-INF/classes下放置xword-conversion.properties文件,在properties文件中的內容爲:待轉換的類型=類型轉換器的全類名
對於本例而言,xwork-conversion.properties文件中的內容爲:
java.util.Date=cn.itcast.conversion.DateConverter
23.輸出帶有佔位符的國際化信息
(1).資源文件中的內容以下:
welcom={0}歡迎來到傳智播客{1}
在jsp頁面中輸出帶佔位符的國際化信息
<s:text name="welcom">
<s:param><s:propertyvalue="realname"/></s:param>
<s:param>學習</s:param>
</s:text>
在Action類中獲取帶佔位符的國際化信息,能夠使用getText(String key,String[] args)或getText(StringaTextName,List args)方法.
(2).佔位符就當是一個變量參數,能夠傳遞給定的參數值.
24.爲Action屬性注入值
struts2爲Action中的屬性提供了依賴注入功能,在struts2的配置文件中,咱們能夠很方便的爲Action中的屬性注入值,注意:屬性必須提供setter方法,
public class HelloWorldAction{
private String savePah;
public String getSavePath(){
return savePath;
}
public void setSavePath(String savePath){
this.savePath=savePath;
}
}
<package name="itcast"namespace="/test" extends="struts-default">
<action name="helloworld"class="cn.itcast.action.helloWorldAction">
<paramname="savePath">/images</param>
<resultname="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
上面經過<param>節點爲action的savePath屬性注入"/images";Action的變量的值,不能寫死,常常變換,須要經過配置來設置參數
25.爲應用指定多個配置文件
(1).在大部分應用中,隨着應用規模的增長,系統中的Action的數量也會大量增長,致使struts.xml配置文件變得很是臃腫,爲了不struts.xml文件過於龐大、臃腫,提升struts.xml文件的可讀性,咱們能夠講一個struts.xml配置文件分解成多個配置文件,而後再struts.xml文件中包含其餘配置文件,下面的struts.xml經過<include>元素指定多個配置文件:
<struts>
<includefile="struts-user.xml"/>
<includefile="struts-order.xml"/>
</struts>
經過這種方式,咱們就能夠將struts2的Action按模塊添加在多個配置文件中
26.文件上傳
第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar,這兩個文件能夠從http://commons.apache.org下載,在struts2.1之前的版本須要添加,之後的版本就不須要添加
第二步:把form表的enctype設置爲:"multipart/form.data",以下:
<form
enctype="multipart/form-data"action="${pageContext.request.contextPath}/xxx.action"method="post">
<inputtype="file" name="uploadImage">這個屬性的name必需要和類中File名稱同樣
</form>
第三步:在Action類中添加如下屬性
public class HelloWorldAction{
private File uploadImage;//獲得上傳文件;
private String uploadImageContentType;//獲得文件的類型
private String uploadImageFileName;//獲得文件的名稱
//這裏省略了屬性的get/set方法(可是要注意get/set方法是必須的)
public String upload() throws Exception{
String realpath =ServletActionContext.getServletContext().getRealPath("/images");
File file=new File(realpath);
if(file.getParentFile().exists())file.getParentFile().mkdirs();//目錄是否存在,不存在就建立
FileUtils.copyFile(uploadImage,newFile(file,uploadImageFileName));
return "success";
}
}
(1).若是文件不保存,struts2會把文件保存到本身的目錄中,可是當這個Action執行完後,該文件就會被刪除,因此咱們要將上傳的文件保存到硬盤上
(2).最好還要判斷如下,文件uploadImage是否爲空
(3).若是上傳大的文件,web都會失敗,像一些門戶網站上傳視頻,都是經過一個插件,能夠把這個插件當作一個程序,只是這個程序是經過Socket變成的,針對一個端口進行傳輸數據
27.指定struts2處理的請求後綴
(1).前面咱們都是默認使用.action後綴訪問Action,其實默認後綴是能夠經過常量"struts.action.extension"進行修改的,例如:咱們能夠配置struts2只處理以.do爲後綴的請求路徑
<struts>
<constantname="struts.action.extendsion" value="do"/>
</struts>
若是用戶須要制定多個請求後綴,則多個後綴之間以英文逗號","隔開,如:
<constantname="struts.action.extendsion" value="do,go"/>
(2).常量能夠在struts.xml或struts.properties中配置,建議在struts.xml中配置,兩種配置方式以下:
在struts.xml文件中配置常量:
<struts>
<constantname="struts.action.extendsion" value="do"/>
</struts>
在struts.properties中配置常量:
struts.action.extension=do
由於常量能夠在下面多個配置文件中進行定義,因此咱們須要瞭解struts2加載常量的搜索順序:
struts-default.xml
struts-plugin.xml
struts.xml
struts.properties
web.xml
若是在多個文件中配置了同一個常量,則後一個文件中配置的常量值會覆蓋前面文件中配置的常量值
(3).
第一:默認編碼集,做用於HttpServletRequest的setCharacterEncoding方法和freemarker、velocity的輸出:
<constantname="struts.i18n.encoding" value="UTF-8"/>
第二:該屬性指定須要struts2處理的請求後綴,該屬性的默認值是action,即全部匹配*.action的請求都由struts2處理,若是用戶須要指定多個請求後綴,則多個後綴之間以英文逗號(,)隔開
<constant name="strtus.action.extension" value=do"/>
第三:設置瀏覽器是否緩存靜態內容默認值爲true(生產環境下使用)開發階段最好關閉,否則看不到修改後的數據
<constantname="struts.serve.static.browserCache" value="false"/>
第四:當struts的配置文件修改後系統是否自動從新加載該文件默認值爲false(生產環境下使用),開發階段最好打開
<constantname="struts.configuration.xml.reload" value="true"/>
第五:開發模式下使用,這樣能夠打印出更詳細的錯誤信息
<constant name="struts.devMode"value="true"/>
第六:默認的視圖主題
<constantname="struts.ui.theme" value="simple"/>
第七:與spring集成時,指定由spring負責action對象的建立
<constantname="struts.objectFactory" value="spring"/>
第七:該屬性設置struts2是否支持動態方法調用,該屬性的默認值是true,若是須要關閉動態方法調用,則可設置該屬性爲false
<constantname="struts.enable.DynamicMethodInvocation"value="false"/>
第八:上傳全部文件的總大小限制
constantname="struts.mulitipart.maxSize" value="10701096"/>
28.自定義攔截器
(1).若是用戶登陸後能夠訪問action中的全部方法,若是用戶沒有登陸不容許訪問action中的方法,而且提示"你沒有權限執行該操做"
(2).
<interceptors>
<interceptorname="permission"class="cn.itcast.interceptor.PermissionInterceptor"/>
</interceptors>
<actionname="list_*" class="cn.itcast.action.HelloWorldAction"method="{1}">
<interceptor-refname="permission"/>
若是爲某一個Action定義一個攔截器,struts2中對Action的默認的不少攔截器都失去功能,因此要想作到一箭雙鵰,須要定義一個攔截器棧:
<interceptors>
<interceptorname="permission"class="cn.itcast.interceptor.PermissionInterceptor"/>
<interceptor-stackname="permissionStack">
<interceptor-refname="defaultStack"/>
<interceptor-refname="permission"/>
</interceptor-stack>
</interceptors>
由於struts2中如文件上傳,數據驗證,封裝請求參數到action等功能都是由系統默認的defaultStack中的攔截器實現的,因此咱們定義的攔截器須要引用系統默認的defaultStack,這樣應用才能夠使用struts2框架提供的衆多功能,若是但願包下的全部action都使用自定義的攔截器,能夠經過<default-interceptor-refname="permissionStack"/>把攔截器定義爲默認攔截器,注意:每一個包只能指定一個默認攔截器,另外,一旦咱們爲該包中的某個action顯示指定了某個攔截器,則默認攔截器不會起做用.
(3).系統默認的攔截器能夠到struts-default.xml中查看,不少功能.系統攔截器放在最前面,自定義的攔截器放在後面.
29.自定義類型轉換器
(1).struts2中提供了兩種類型轉換器:局部類型轉換器(只對某一個action起做用),全局類型轉換器(全部的action起做用)
(2).類型轉換器必須繼承DefaultTypeConverter最好用xwork2.jar中的,重寫converValue(Map<String,Object>context,Objectvalue,Class toType){
returnsuper.convertValue(context,value,toType);
}
其中第一個參數和ognl表達式,第二個參數是須要轉換類型的內容(是String數組,由於可能有多個值),第三個參數是須要轉換成什麼類型,要實現雙向轉換
(3).將上面的類型轉換器註冊爲局部類型轉換器:
在Action類所在的包下放置ActionClassName-conversion.properties文件,ActionClassName是Action的類名,後面的-conversion.properties是固定寫法,對於本例而言,文件的名稱應爲HelloWorldAction-conversion.properties.在properties文件中的內容爲:
須要轉換的屬性名稱=類型轉換器的全類名
對於本例而言,HelloWorldAction-conversion.properties文件中的內容爲:
createtime=cn.itcast.conversion.DateConverter
4、 Spring
1. @Autowired註解與自動裝配
@Autowired
private PersonDaopersonDao;
拿PersonDao與<bean id=""..../>中的id值進行比較,相同就找到了,即進行類型註解,固然也能夠過@Qualifier註解進行名稱進行註解.
自動裝配:
對於自動裝配,你們瞭解一下就能夠了,實在不推薦你們使用,例子:<bean id="" class=""autowire="byType"/>
autowire屬性取值以下:
byType:按類型裝配,能夠根據屬性的類型,在容器中尋找根該類型匹配的bean,若是發現多個,那麼將會拋出異常,若是沒有找到,即屬性值爲null
byName:按名稱裝配,能夠根據屬性的名稱,在容器中尋找跟該屬性名相同的bean,若是沒有找到,即屬性值爲null
construtor與byType的方式相似,不一樣之處在於它應用於構造器參數,若是在容器中沒有找到與構造器參數類型一致的bean,那麼將會拋出異常.
autodetect:經過bean類的自省機制,來決定是使用constructor仍是byType方式進行自動裝配,若是發現默認的構造器,那麼將使用byType方式.
2. @Resource註解完成屬性裝配
(1).前面講到了使用構造器注入,屬性的setter方法注入,這裏還能夠使用註解的方式對Field進行注入
(2).注入依賴對象能夠採用手工裝配或自動裝配,在實際應用中建議使用手工裝配,由於自動轉配會產生未知狀況,開發人員沒法預見最終的裝配結果
(3).手工裝配依賴對象,在這種方式中又有兩種編程方式
方式一:在XML配置文件中,經過在Bean節點下配置,如:
<bean id="orderService"class="cn.itcast.service.OrderServiceBean">
<construtor-arg index="0"type="java.lang.String" value="xxx"/>構造器注入
<property name="name"value="zhao"/>屬性的setter方法注入
</bean>
在XML中注入屬性,會給XML文件變得很臃腫.特別是對集合類型進行注入時,變得很臃腫.
方式二:
在java代碼中使用@Autowire或@Resoruce註解方式進行裝配,但咱們須要在XML配置文件中配置如下信息:
<beansxmlns="http://www.springframe.....
....
....
</beans>
這些配置項隱式註冊了多個對註釋進行解析處理的處理器:AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequireAnnotationBeanPostProcessor,每一個註解都有一個註解處理器,註解自己不幹活,是相對應的註解處理器在幹活
注:@Resource註解在spring安裝目錄的lib\j2ee\common-annotations.jar
(4).在java代碼中使用@Autowired或@Resource註解方式進行裝配,這兩個註解的區別是:
@Autowired默認按類型裝配,@Resource默認按名稱裝配,當找不到與名稱匹配的bean纔會按類型裝配.
@Autowired
private PersonDaopersonDao;//用於字段上
@Autowired
public voidsetOrderDao(OrderDao orderDao){//用於setter方法上
this.orderDao=orerDao;
}
@Autowired註解是按類型裝配依賴對象,默認狀況下,它要求依賴對象必須存在,若是容許null值,能夠設置它required的屬性爲false,若是咱們想使用按名稱裝配,能夠結合@Qualifier註解一塊兒使用,以下:
@Autowired@Qualifier("personDaoBean")
private PersonDaopersonDao;
@Resource註解和@Autowired同樣,也能夠標註在字段或屬性的setter方法上,但他默認按名稱裝配,名稱能夠經過@Resource的name屬性執行,若是沒有指定name屬性,當註解標註在字段上,即默認取字段的名稱做爲bean名稱尋找依賴對象,當註解標註在屬性的setter方法上,即默認取屬性名做爲bean名稱尋找依賴對象
@Resource(name="personDaoBean")
private PersonDaopersonDao;//用於字段上
注意:若是沒有指定name屬性,而且按照默認的名稱仍然找不到依賴對象時,@Resoruce註解會回退到按類型裝配,但一旦指定了name屬性,就只能按名稱裝配了.拿personDao與<bean id=" ".../>中的id是否相同,相同就找到,屬性的setter方法也不用寫,既方便,又優雅.
同時@Resource是j2ee提供的註解(建議使用),@Autowired是spring框架提供的.
3. Spring的三種實例化Bean的方式
(1).三種實例化bean的方式:
第一種:<bean id="orderService"class="cn.itcast.OrderServiceBean/>
第二種:使用靜態工廠方法實例化:
<bean id="personService" class="cn.itcast.service.OrderFactory"factory-method="createOrder"/>
public class OrderFactory{
public static OrderServiceBeancreateOrder(){
return new OrderServiceBean();
}
}
第三種:使用實例工廠方法實例化:
<beanid="personServiceFactory" class="cn.itcast.service.OrderFactory"/>
<bean id="personService"factory-bean="personServiceFactory"factory-method="createOrder"/>
public class OrderFactory{
public OrderServiceBean createOrder(){
return new OrderServiceBean();
}
}
4. Spring管理的Bean的生命週期
(1).Bean實例化是在Spring容器實例化時進行的,可是這是Singleton做用域中,實例化的時機是能夠更改的,lazy-init="true"延遲初始化,即更改成調用getBean()方法時進行初始化.同時也能夠在配置文件中設置全部的bean延遲初始化,其實這個標籤是不建議使用的.
(2).當把做用域改爲Prototype時,Bean實例化是在調用getBean()方法進行的
(3).能夠指定一個初始化方法:init-method="";即在bean實例化後執行的初始化方法.如數據庫的鏈接.容器經過反射技術調用的,同時還須要進行資源的釋放.destory-method="";即在bean被銷燬時執行的方法.
(4).關閉Spring容器:ctx.close()方法,bean此時被銷燬了
5.Spring自動掃描和管理bean
經過在classpath自動掃描方式把組件歸入spring容器中管理,前面的例子咱們都是使用XML的bean定義來配置組件,在一個稍大的項目中,一般會有上百個組件,若是這些組件採用xml的bean定義來配置,顯然會增長配置文件的體積,查找及維護起來也不太方便,spring2.5爲咱們引入了組件自動掃描機制,它能夠在類路徑底下尋找標註了@Componet、@Service、@Controller、@Reponsitory註解的類,並把這些類歸入進spring容器中管理,它的做用和在XML文件中使用bean節點配置組件是同樣的,要使用自動掃描機制,咱們須要打開如下配置信息:
<beansxmln="http://www.springframework.org/schema/beans"
....
....
</beans>
其中base-package爲須要掃描的包(含子包)
@Service用於標註業務層組件、@Controller用於標註控制層組件(如struts中的action)、@Repository用於標註數據訪問組件,即Dao組件,而@Component泛指組件,當組件很差歸類的時候,咱們能夠使用這個註解進行標註.同時也能夠經過註解@Scope("prototype")修改bean的做用域.@Service("personService")中的名稱必須和bean的名稱相同,只是開頭字母變成小寫了.固然這是默認設置,能夠修改的.
能夠使用註解的方式指定初始化方法,在初始化方法init()上添加註解@PostConstruct,一樣能夠指定銷燬方法destroy(),註解爲:@PreDestroy
這種掃描方式是很方便的,不少人都採用
即前面所說的功能都使用註解進行操做
6. SSH整合開發
hibernate核心安裝包下的:
hibernate3.jar
lib\required\*.jar
lib\optional\ehcache-1.2.3.jar
hibernate註解安裝包下的
lib\test\slf4j-log4j12.jar
Spring安裝包下的
dist\spring.jar
dist\modules\spring-webmvc-struts.jar
lib\jakarta-commons\commons-logging.jar、commons-dbcp.jar、commons-pool.jar
lib\aspectj\aspectjweaver.jar、aspectjrt.jar
lib\cglib\cglib-nodep-2.1.3.jar
lib\j2ee\common-annotations.jar
lib\log4j-1.2.14.jar
Struts:下載struts-1.3.8-lib.zip,須要使用到解壓目錄下的全部jar,建議把jstl-1.0.2.jar和standard-1.0.2.jar更換爲1.1版本,Spring中已經存在一個antlr-2.7.6.jar,因此把struts中的antlr-2.7.2.jar刪除,避免jar衝突
數據庫驅動jar
首先整合struts和hibernate而後再整合spring
(1).新建一個註解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.Field,ElementType.METHOD})
public @interface ItcastResource{
String name() default "";
}
固然還要編寫一個註解處理器.
private voidannotationInject(){
首先循環全部的bean對象,判斷其是否使用了註解
若是使用了註解,就進行屬性注入.
}
8.編碼解析Spring依賴注入的原理
(1).基本類型對象注入:
<bean id="orderService"class="cn.itcast.service.OrderServiceBean">
<constructor-arg index="0"type="java.lang.String" value="xxx"/>構造器注入
<property name="name"value="zhao"/>//屬性setter方法注入
</bean>
(2).注入其餘bean:
方式一:
<bean id="orderDao"class="cn.itcast.service.OrderDaoBean"/>
<bean id="orderService"class="cn.itcast.service.OrderServiceBean">
<property name="orderDao"ref="orderDao"/>ref是指被注入的對象personDao
</bean>
方式二:(使用內部bean,但該bean不能被其餘bean使用)
<bean id="orderService"class="cn.itcast.service.OrderServiceBean">
<property name="orderDao">
<beanclass="cn.itcast.service.OrderDaoBean"/>
</property>
</bean>
(3).依賴注入的內部原理
9.編碼解析Spring裝配基本屬性原理
也能夠對基本類型進行注入,類型轉換.
10.編碼剖析Spring管理bean的原理
使用dom4j讀取spring配置文件,使用反射技術便可
11.搭建和配置Spring與JDBC整合的環境
(1).配置數據源:
<bean
id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">
apache的數據源:BasicDataSource
<propertyname="driverClassName" value="org.gjt.mm.mysql.Driver"/>
<property
name="url" value="jdbc:mysql://localhost:3306/itcast?useUnicode=true&:characterEncoding=UTF-8"/>
<property name="username"value="root"/>
<property name="password"value="123456"/>
<property name="initialSIze"value="1"/>鏈接池啓動時的初始值
<property name="maxActive"value="500"/>鏈接池的最大值
<property name="maxIdle"value="2"/>最大空閒值
<property name="minIdle"value="1"/>最小空閒值
</bean>
配置事務:
採用註解的方式:
<bean
id="txManager"class="org.springframework.jdbc.datasoruce.DataSourceTransactionManager">
<property name="dataSource"ref="dataSource"/>
</bean>
<ts:annotation-driventransaction-manager="txManage"/>
12.搭建與測試spring的開發環境
(1).dist\spring.jar;
lib\jakarta-commons\commons-logging.jar
以上這兩個包是必須的
lib\aspectj\aspectjweaver.jar和aspectjrt.jar
lib\cglib\cglib-nodep-2.1.3.jar
以上這兩個包是用於切面編程(AOP)
lib\j2ee\common-annotations.jar
以上的這個包是JSR-250中的註解
(2).Spring項目既能夠在j2se中也能夠在j2ee中
(3).spring的配置文件模板能夠從spring的參考手冊或spring的例子中獲得,配置文件的取名能夠任意,文件能夠存放在任何目錄下,但考慮到通用性,通常放在類路徑下
(4).實例化spring容器經常使用方式:
第一種方式:在類路徑下尋找配置文件來實例化容器
ApplicationContextctx=new ClassPathXmlApplicationContext(new String[]{"beans.xml"});
第二種方式:在文件系統路徑下尋找配置文件來實例化容器
ApplicationContextctx=new FileSystemXmlApplicationContext(newString[]{"d:\\beans.xml"});//將路徑寫死了,通用性很差,不建議使用.
spring的配置文件能夠指定多個,能夠經過String數組傳入
(5).IOC:控制反轉:
public class PersonServiceBean{
private PersonDao personDao = newPersonDaoBean();
public void save(Person person){
personDao.save(person);
}
}
PersonDaoBean是在應用內部建立及維護的,所謂控制反轉就是應用自己不負責依賴對象的建立及維護,依賴對象的建立及維護是由外部容器負責的,這樣控制權就由應用轉移到外部容器,控制權的轉移就是所謂反轉.
(6).創建一個業務bean爲PersonServiceBean,放在cn.itcast.service.impl包中.面向接口編程,進行解耦,怎麼將業務bean交給spring管理,須要在beans.xml配置文件中:<bean id="" name=""class=""></bean>,其中id,name都是bean的名稱,可是id不能使用特殊字符,id自己就屬於XML屬性的,如:"/sfs/"就會出錯,可是name不會出錯,class屬性是bean類的路徑,須要操縱bean的時候,只需到spring容器中取出bean進行操做,而不須要實例化一個bean了:
ApplicationContext ctx=newClassPathXmlApplicationContext(new String[]{"beans.xml"});
PersonServiceBeanpersonService=(PersonServiceBean)ctx.getBean("personService");
personService.save();
(7).當在配置文件中沒有標籤的提示信息,須要手動添加schema文件,方法以下:windows->preferences->myeclipse->filesand editors->xml->xmlcatalog,點擊添加,在出現的窗口中的Key Type中選擇URL,在location中選"File System",而後在spring解壓目錄的dist/resources目錄中選擇spring-beans-2.5.xsd,回到設置窗口的時候不要急着關閉窗口,應該把窗口的Key Type改成Schema location,Key改成http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
13.配置Spring管理的bean的做用域
(1).Singleton:默認狀況下,bean是單例模式的,在每一個springIoc容器中一個bean定義只有一個對象實例,默認狀況下會在容器啓動時初始化bean,可是咱們能夠指定Bean節點的lazy-init="true"來延遲初始化bean,這時候,只有第一次獲取bean會才初始化bean,如:<bean id="xxx"class="cn.itcast.OrderServiceBean" lazy-init="true"/>
若是想對全部bean都應用延遲初始化,能夠在根節點beans設置default-lazy-init="true",以下:<beans default-lazy-init="true">
Prototype:每次從容器獲取bean都是新的對象
Request:在request的域中
Session:在Session的域中
Global Session:在全局的Session的域中
14.全面闡釋Spring及其各項功能
(1).Spring是一個開源的控制反轉(Inversion of control,IoC)和麪向切面(AOP)的容器框架,它的主要目的是簡化企業開發
(2).IOC:控制反轉:
public class PersonServiceBean{
private PersonDao personDao = newPersonDaoBean();
public void save(Person person){
personDao.save(person);
}
}
PersonDaoBean是在應用內部建立及維護的,所謂控制反轉就是應用自己不負責依賴對象的建立及維護,依賴對象的建立及維護是由外部容器負責的,這樣控制權就由應用轉移到外部容器,控制權的轉移就是所謂反轉.PersonServiceBean是業務邏輯層類,PersonDaoBean是Dao層類,在業務邏輯層類中控制和建立PersonDaoBean,這就是應用自己建立和維護了,可是有了spring容器後,PersonDaoBean的建立和維護是在容器中進行的,不須要PersonServiceBean進行管理了,控制權進行的反轉
(3).依賴注入:當咱們把依賴對象交給外部容器負責建立,那麼PersonServiceBean類能夠改爲以下:
public class PersonServiceBean{
private PersonDao personDao;
//經過構造器參數,讓容器把建立好的依賴對象注入進PersonServiceBean,固然也能夠使用setter方法進行注入
public PersonServiceBean(PersonDaopersonDao){
this.personDao=personDao;
}
public void save(Person person){
personDao.save(person);
}
}
所謂依賴注入就是指:在運行期,由外部容器動態的將依賴對象注入到組件中.
(4).爲什麼要使用spring:
第一:下降組件之間的耦合度,實現軟件各層之間的解耦:
Controller->Service->Dao
第二:能夠使用容器提供的衆多服務,如:事務管理服務,消息服務等,當咱們使用容器管理事務時,開發人員就再也不須要手工控制事務,也不須要處理複雜的事務傳播
第三:容器提供單例模式支持,開發人員不在須要本身編寫實現代碼
第四:容器提供了AOP技術,利用它很容易實現如權限攔截、運行期監控等功能
第五:容器提供的衆多輔助類,使用這些類可以加快應用的開發,如:JdbcTemplate,HibernateTemplate
第六:Spring對於主流的應用框架提供了集成技術,如集成Hibernate、JPA、Struts等,這樣更便於應用的開發.
(5).輕量級和重量級概念的劃分,其實劃分一個應用是否屬於輕量級仍是重量級,主要看他使用了多少服務,使用的服務越多,容器要爲普通的java對象的工做就越多,必然會影響到應用的發佈時間或者是運行性能,對於spring容器,它提供了不少服務,可是這些服務並非默認爲應用打開的,應用須要某種服務,還須要指明使用該服務,若是應用使用的服務不多,如:只是用了spring的核心服務,那麼咱們就能夠認爲此時應用屬於輕量級的,若是應用使用了spring提供的大部分服務,這時應用就屬於重量級,目前EJB容器就由於他默認爲應用提供了EJB規範中全部的功能,因此他屬於重量級
15.使用CGLIB實現AOP功能與AOP概念詳解
(1).使用cglib架包,構建代理,不須要被代理的對象須要實現接口
public class CGlibProxyFactory implementsMethodInterceptor{
private Object targetObject;
public Object createProxyIntance(ObjecttargetObject){
this.targetObject=targetObject;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.targetObject.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercep(Object proxy,Method method,Object[]args,MethodProxy methodProxy)throws Throwable{
returnmethodProxy.invoke(this.targetObject,args);
}
}
CGLIB能夠生成目標類的子類,並重寫父類非final修飾符的方法
16.使用JDK中的Proxy技術實現AOP功能
(1).使用在權限攔截上.
(2).被代理對象必須實現接口
public class JDKProxyFactory implementsInvocationHandler{
private Object targetObject;
public Object createProxyIntance(ObjecttargetObject){
returnProxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy,Methodmethod,Object[]args) throws Throwable{//環繞通知
PersonServiceBean bean=(PersonServiceBean)this.targetObject;
Object result=null;
if(bean.getUser()!=null){
//....advice(),調用方法前處理,叫作前置通知
try{
result=method.invoke(targetObject,args);
//....afteradvice();調用方法後處理,叫作後置通知
}catch(RuntimeException e){
//....exceptionadvice();調用方法出現例外後處理,叫作例外通知
}finally{
//....finallyadvice();調用方法最終都會處理,叫作最終通知
}
}
return result;
}
}
整個invoke方法叫作環繞通知.
(3).AOP中的概念:
Aspect(切面):指橫切性關注點的抽象即爲切面,它與類類似,只是二者的關注點不同,類是對物體特徵的抽象,而切面是橫切性關注點的抽象。
JoinPoint(鏈接點):所謂鏈接點是指那些被攔截到的點,在spring中,這些點指的是方法,由於spring只支持方法類型的鏈接點,實際上joinpoint還能夠是field或類構造器
Pointcut:切入點:所謂切入點是指咱們要對那些joinpoint進行攔截的定義
Advice(通知):所謂通知是指攔截到joinpoint以後所要作的事情就是通知,通知分爲前置通知,後置通知,異常通知,最終通知,環繞通知
Target(目標對象):代理的目標對象
Weave(織入):指將aspect應用到target對象並致使proxy對象建立的過程稱之爲織入
Introduction(引入):在不修改類代碼的前提下,Introduction能夠在運行期爲類動態的添加一些方法或Field
17.使用Spring的註解方式實現AOP
(1).要進行AOP編程,首先咱們要在Spring的配置文件中引入AOP命名空間:
<beansxmlns="http://www.springframework.org/schema/beans"
</beans>
Spring提供了兩種切面使用方式,實際工做中咱們能夠選用其中一種:一種是基於XML配置方式進行AOP開發,另一種是基於註解方式進行AOP開發
(2).基於註解方式聲明切面:首先啓動對@AspectJ註解的支持:
<aop:aspectj-autoproxy/>
<bean id="orderservice"class="cn.itcast.service.OrderServiceBean"/>
<bean id="log"class="cn.itcast.service.LogPrint"/>
</beans>
定義一個切面類(MyInterceptor):
@Aspect
public class MyInterceptor{
@Pointcut("execution(*cn.itcast.service..*.*(..))
//定義切入點,通配符:*指的是任何返回類型,..是指子包下也進行攔截,*:指的是攔截全部類,*:指定的是全部方法,..是指任意參數
private void anyMethod(){}//聲明一個切入點
@Before("anyMethod()")//定義一個前置通知,名稱爲切入點名稱
public void doAccessCheck(){
System.out.println("前置通知");
}
@AfterReturning("anyMethod()")//後置通知
public void doAfterReturning(){
System.out.println("後置通知");
}
@After("anyMethod()")//最終通知
public void doAfter(){
System.out.println("最終通知");
}
@AfterThrowing("anyMethod()");//例外通知
public void doAfterThrowing(){
System.out.println("例外通知");
}
@Around("anyMethod()")//環繞通知
public ObjectdoBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{
System.out.println("進入方法");
if(){//判斷用戶是否有權限,有權限就執行該方法.
Object result = pjp.proceed();
System.out.println("退出方法");
}else{
}
return result;
}
}
因此當出現例外通知時,後置通知是不執行的,即它們二者確定有一個不執行,一個執行.
須要將切面類交給spring管理:基於XML配置管理或者自動掃描管理
(3).想獲得參數:
@Before(anyMethod() &&args(userName)")//添加參數,只會攔截到對應的方法,即一個參數的方法.
public void doAccessCheck(String name){
System.out.println("前置通知");
}
(4).想獲得返回結果:
@AfterReturning("anyMethod()",returning="result")//後置通知
public void doAfterReturning(String result){
System.out.println("後置通知");
System.out.println(result);//打印返回結果
}
(5).獲得異常(例外):
@AfterThrowing("anyMethod()",throwing="e");//例外通知
public void doAfterThrowing(Exception e){
System.out.println("例外通知");
System.out.println(e);//打印例外
}
18.使用Spring配置文件實現AOP
(1).使用配置文件實現AOP,切面類只是個普通的類,其內部沒有任何註解
public class MyInteceptor{
public void doAccessCheck(){
System.out.println("前置通知");
}
public void doAfterReturning(){
System.out.println("後置通知");
}
public void doAfter(){
System.out.println("最終通知");
}
public void doAfterThrowing(){
System.out.println("例外通知");
}
public ObjectdoBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{
System.out.println("進入方法");
Object result=pjp.proceed();
System.out.println("退出方法");
return result;
}
}
(2).基於XML配置方式聲明切面
<bean id="orderservice"class="cn.itcast.service.OrderServiceBean"/>
<bean id="log"class="cn.itcast.service.LogPrint"/>
<aop:config>
<aop:aspect id="myaop"ref="log">
<aop:pointcut id="mycut"expression="execution(* cn.itcast.service..*.*(..))"/>
<aop:beforepointcut-ref="mycut" method="doAccessCheck"/>
<aop:after-returningpointcut-ref="mycut" method="doReturnCheck"/>
<aop:after-throwingpointcut-ref="mycut" method="doExceptionAction"/>
<aop:afterpointcut-ref="mycut" method="doReleaseAction"/>
<aop:aroundpointcut-ref="mycut" method="doBasicProfiling"/>
</aop:aspect>
</aop:config>
(3).對於表達式expression的細節:
返回值類型爲String:execution(java.lang.Stringcn.itcast.service..*.*(..))
第一個參數爲String:execution(java.lang.Stringcn.itcast.service..*.*(java.lang.String..))
返回值類型爲非void:execution(!void cn.itcast.service..*.*(..))
19.使用Spring配置文件實現事務管理
(1).
<aop:config>
<aop:pointcutid="transactionPointcut" expression="execution(*cn.itcast.service.*.*(..))"/>對指定的方法進行攔截
<aop:advisoradvice-ref="txAdvice"pointcut-ref="transactionPointcut"/>
</aop:config>
<tx:adviceid="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method
name="get*"read-only="true" 全部已get方法都不開啓事務propagation="NOT_SUPPORTED"/>
<tx:methodname="*"/>
</tx:attributes>
</tx:advice>
20.使用Spring註解方式管理事務與傳播行爲詳解
(1).只有當遇到RuntimeException時,事務進行回滾.Spring開啓的事務管理,當遇到運行期例外(unchecked),而(checked異常)是不進行事務的回滾的.
(2).固然也可修改這種狀況.把unckecked異常改爲不會進行回滾了:@Transactional(noRollbackFor=RuntimeException.class),
(3).@Transactional(propagation=Propagation.NOT_SUPPORTED);關閉事務,不開啓事務的.spring容器默認是打開事務的.固然還有其餘一些值:(事務的傳播行爲)
REQUIRED:業務方法須要在一個事務中運行,若是方法運行時,已經處在一個事務中,那麼加入到該事務,不然爲本身建立一個新的事務(容器的默認值)
NOT_SUPPORTED:聲明方法不須要事務,若是方法沒有關聯到一個事務,容器不會爲他開啓事務,若是方法再一個事務中被調用,該事務會被掛起,在方法調用結束後,原先的事務便會恢復執行
REQUIRESNEW:屬性聲明無論是否存在事務,業務方法總會爲本身發起一個新的事務,若是方法已經運行在一個事務中,則原有事務會被掛起,新的事務會被建立,直到方法執行結束,新事務纔算結束,原先的事務纔會恢復執行
MANDATORY:該屬性指定業務方法只能在一個已經存在的事務中執行,業務方法不能發起本身的事務,若是業務方法再沒有事務的環境下調用,容器就會拋出異常
SUPPORTS:這一事務屬性聲明,若是業務方法再某個事務範圍內被調用,則方法成爲該事務的一部分,若是業務方法再事務範圍外被調用,則方法再沒有事務的環境下執行
Never:指定業務方法絕對不能在事務範圍內執行,若是業務方法再某個事務中執行,容器會拋出異常,只有業務方法沒有關聯到任何事務,才能正常執行.
NESTED:若是一個活動的事務存在,則運行在一個嵌套的事務中,若是沒有活動事務,則按REQUIRED屬性執行,它使用了一個單獨的事務,這個事務擁有多個能夠回滾的保存點,內部事務的回滾不會對外部事務形成影響,它只對DataSourceTransactionManager事務管理器起效.Savepoint savepoint=conn.setSavepoint();conn.rollback(savepoint);
(4).readOnly值爲事務不能修改了.timeout是事務的超時時間,isolation數據庫中的隔離級別.
(5).數據庫系統提供了四種事務隔離級別供用戶選擇,不一樣的隔離級別採用不一樣的鎖類型來實現,在四種隔離級別中,Serializable的隔離級別最高,Read Uncommited的隔離級別最低,大多數據庫默認的隔離級別爲Read Commited,如SQLServer,固然也有少部分數據庫默認的隔離級別爲Repeatable Read,如MySql
Read Uncommited:讀未提交數據(會出現髒讀,不可重複讀和幻讀)
Read Commited:讀已提交數據(會出現不可重複讀和幻讀)
Repeatable Read:可重複讀(會出現幻讀)
Serializable:串行化
髒讀:一個事務讀取到另外一個事務未提交的更新數據
不可重複讀:在同一事務中,屢次讀取同一數據返回的結果有所不一樣,換句話說就是,後續讀取能夠讀到另外一事務已經提交的更新數據,相反,「可重複讀」在同一事務中屢次讀取數據時,可以保證所讀數據同樣,也就是,後續讀取不能讀到另外一事務已提交的更新數據.
幻讀:一個事務讀取到另外一個事務已提交的insert數據.
5、 Hibernate
1. Criteria查詢方式
(1).Criteria查詢方式(條件查詢):
Criteriac=s.createCriteria(User.class);
c.add(Restrictions.eq("name",name));//添加查詢條件,User中的name屬性的值是否等於"name"
List<User>list=c.list();
Useru=(User)c.uniqueResult();
2. hibernate的二級緩存配置與分析
(1).二級緩存:SessionFactory級共享:
實現爲可插拔,經過修改cache.provider_class參數來改變;hibernate內置了對EhCache.OSCache,TreeCaceh,SwarmCache的支持,能夠經過實現CacheProvider和Cache接口來加入Hibernate不支持的緩存實現
在hibernate.cfg.xml中加入:
<class-cacheclass="className" usage="read-only"/>或在映射文件的class元素加入子元素:<cache usage="read-write"/>
其中usage:read-only,read-write,nonstrict-read-write,transactional
Session的save(這個方法不適合native生成方式的主鍵),update.saveOrUpdate,list,iterator,get,load,以及Query,Criteria都會填充二級緩存,但只有(沒有打開查詢緩存時)Session的iterator,get,load會從二級緩存中取數據(iterator可能存在N+1次查詢)
Query,Criteria(查詢緩存)因爲命中率較低,因此hibernate缺省是關閉;修改cache,use_query_cache爲true打開對查詢的緩存,而且調用query.setCaheable(true)或criteria.setCacheable(true)
SessionFactory中提供了evictXXX()方法用來清除緩存中的內容
統計消息打開generate_statistics,用sessionFactory.getSatistics()獲取統計信息,獲取統計信息成本是很高的,消耗資源.對程序的調試是頗有幫助的,能夠看到session的初始化時間,打開多少次,關閉多少次等信息.
(2).相對user對象進行緩存:
<class-cacheclass="cn.itcast.hibernate.domain.User"usage="read-only"/>只讀方式,效率高,User類不會再改變了.可以保證併發.
(3).先到一級緩存中查找,找不到在到二級緩存中查找
3.Hibernate的攔截器和監聽器
(1).攔截器和事件
攔截器與事件都是hibernate的擴展機制,Interceptor接口是老的實現機制,如今改爲事件監聽機制,他們都是hibernate的回調接口,hibernate在save,delete,update等會回調這些查詢
(2).攔截保存的的事件:
實現SaveOrUpdateEventListener接口
public classSaveListener implements SaveOrUpdateEventListener{
public voidonSaveOrUpdate(SaveOrUpdateEvent event){
if(event.getObject()instantce of cn.itcast.hibernate.domain.User){
User user =(User)event.getObject();
System.out.println(user.getName().getFirstName());
}
}
}
配置文件中:
<eventtype="save">
<listenerclass="cn.itcast.hibernate.SaveListener"/>本身定義的監聽器,不一樣監聽器的註冊順序,輸出的結果也是不一樣的.
<listenerclass="org.hibernate.evetn.def.DefaultSaveOrUpdateEventListenter"/>hibernate缺省的監聽器,本身定義的監聽器會覆蓋缺省的,因此在這裏還要把缺省的監聽器註冊一下.
</event>
當保存user時,會監聽到.
4.hibernate的內部緩存分析
(1).第一級緩存是在session中,第二緩存是在sessionFactory
(2).Useruser=(User)s.get(userClass,id);
System.out.println(user.getClass());
user=(User)s.get(userClass,id);
只有一條select語句
(3).當session關閉時,緩存也就沒有數據了.
(4).緩存的做用主要用來提升性能,能夠簡單的理解成一個Map,使用緩存涉及到三個操做:把數據放入緩存、從緩存中獲取數據、刪除緩存中的無效數據
(5).一級緩存,Session級共享,save,update,saveOrUpdate,load,get,list,iterate,lock這些方法都會將對象放在一級緩存中,一級緩存不能控制緩存的數量,因此要注意大批量操做數據時可能形成內存溢出,能夠用evict,clear方法清除緩存的內容.
(6).只要有sql語句,就不會去緩存中拿數據,直接到數據庫中拿數據
(7).手工的對緩存中的數據進行清除.清除一條記錄:s.evict(user);清除全部的記錄s.clear();定時的清除能夠下降內存溢出的可能性.
(8).session的生命週期很短,只在一個web請求內
5.hibernate介紹與動手入門體驗
(1).模型不匹配:Java面嚮對象語言,對象模型,其主要概念有:繼承,關聯,多態等,數據庫是關係模型,其主要概念有:表,主鍵,外鍵等
(2).解決方法:第一種:使用JDBC手工轉換,第二種使用ORM框架來解決,主流的ORM框架有Hibernate、TopLink、OJB
(3).下載hibernate,將下載目錄/hibernate3.jar和/lib下的hibernate運行時必須的包
(4).配置文件hibernate.cfg.xml和hibernate.properties,XML和properties兩種,這兩個文件的做用同樣,提供一個便可,推薦XML格式,下載目錄/etc下是示例配置文件
能夠在配置文件制定:
數據庫的URL,用戶名,密碼,JDBC驅動類,方言等,啓動時Hibernate會在CLASSPATH裏找這個配置文件.映射文件(hbm.xml,對象模型和關係模型的映射),在/eg目錄下有完整的hibernate示例
(5).首先創建一個對象:
public class User{
private int id;
private String name;
private Date birthday;
//省略了get/set方法
}
編寫映射文件:User.hbm.xml
<hibernate-mappingpackage="cn.itcast.hibernate.domain">
<class name="User">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="birthday"/>
</class>
</hibernate-mapping>
在配置文件中hibernate.cfg.xml:
<hibernate-configuration>
<session-factory>
<propertyname="connection.url">jdbc:mysql://localhost:3306/jdbc</property>
<propertyname="connection.username">root</property>
<propertyname="connection.password"></property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<propertyname="hbm2ddl.auto"></property>
<mappingresource="org/hibernate/test/legacy/User.hbm.xml"/>
<class-cacheclass="org.hibernate.test.legacy.Simple" region="Simple"usage="read-write"/>
</session-factory>
</hibernate-configuration>
方言dialect就是哪一種數據庫.hibernate本身能夠創建表(hbm2ddl.auto)
(6).初始化:
Configuration cfg = new Configuration();
cfg.configure();
SessionFactory sf=cfg.buildSessionFactory();//SessionFactory至關於JDBC中DriverManager
Session s=sf.openSession();//工廠模式,生產connection
Transaction tx=s.beginTransaction();
User user = new User();
user.setBirthday(new Date());
user.setName("name");
s.save(user);
ts.commit();
s.close();
(7).hibernate默認會把事務自動提交功能關閉了,全部本身要手動打開,查看錶的結構命令:show create table user,看錶的引擎是否支持事務,查看引擎命令:show engines
(8).開發流程:
方式一:由Domain object->mapping->db
方式二:由DB開始,用工具生成mapping和Domain object
方式三:由映射文件
(9).hibernate管理的Domain對象類定義必需要符合JavaBean的定義規則:默認的構造方法(必須的),有無心義的標示符id(主鍵),非final的,對懶加載有影響
public class User{
private int id;
private String name;
private Date birthDay;
//get/set方法
}
10.編寫一個工具類進行初始化Hibernate
public final class HibernateUtil{
private static SessionFactorysessionFactory;
private HibernateUtil(){
}
static{//靜態代碼塊
Configuration cfg = new Configuration();
cfg.configure();//默認的傳入是hibernate.cfg.xml文件
sessionFactory = cfg.buildSessionFactory();
}
public static SessionFactorygetSessionFactory(){
return sessionFactory;
}
}
11.static void addUser(User user){//標準規範代碼
Session s=null;
Transaction tx=null;
try{
s=HibernateUtil.getSession();
tx.s.beginTransaction();
s.save(user);
tx.commit();
}catch(HibernateException e){
if(tx!=null)
tx.rollback();//不只要回滾,還有拋出異常
throw e;
}finally{
if(s!=null)
s.close();
}
}
6.hibernate配置文件中的配置項
(1).hibernate.cfg.xml和hbm.xml內容解釋:
第一:數據類型:<property name="name"type="java.lang.String"/> type能夠是hibernate、java類型或者你本身的類型(須要實現hibernate的一個接口)
第二:基本類型通常不須要在映射文件中說明,只有在一個java類型和多個數據庫數據類型相對應時而且你想要的和hiberante缺省映射不一致時,須要在映射文件中指明類型(如:java.util.Date,數據庫DATE,TIME,DATATIME,TIMESTAMP,hibernate缺省會把java.util.Date映射成DATATIME型),而若是你想映射成TIME,則你必須在映射文件中指定類型
第三:數據類型的對應關係
(2).Session是非線程安全的,生命週期短,表明一個和數據庫的鏈接,在B/S系統中通常不會超過一個請求;內部維護以及緩存和數據庫鏈接,若是session長時間打開,會長時間佔用內存和數據庫鏈接
(3).SessionFactory是線程安全的,一個數據庫對應一個SessionFactory,生命週期長,通常在整個系統生命週期內有效;SessionFactory保存着和數據庫鏈接的相關信息(user,password,url)和映射信息,以及Hibernate運行時要用到的一些信息.
7. Hibernate映射類型
serializable:序列化到數據庫中.
8.Hibernate中使用的集合類型
(1).集合映射(set,list,array,bag,map)
List<Employee>emps = new ArrayList<Employee>();
映射文件中:
<listname="emps">
</list>配置和set標籤是相同的,只是區分List,Set的區別
<list-indexcolumn="order_col"/>這一列是給hibernate使用的,須要記錄該員工是第幾個加進來的,即加入的順序.
(2).因爲懶加載的問題,Hibernate重寫了java中的集合類,使其具備懶加載的功能.因此在定義的時候,必需要定義成接口類型即List,Set,Map
9.hql的命名參數與Query接口的分頁查詢
(1).匿名參數:不使用佔位符了
String hql ="from User as user where user.name=:n";
query.setString("n",name);
不會依賴參數的位置
(2).Query接口中的方法
query.setFirstResult(0);
第一條記錄從哪開始,參數爲開始的位置
query.setMaxResult(10);
實現分頁功能
10.Hql與Criteria查詢的補充知識
HQL:查詢多個對象select art,user from Article art,User user where art.author.id=user.idand art.id=:id這種方式返回的是Object[],Object[0]:article,Object[1]:user;
11.Iterate查詢與N+1次查詢
(1).假設已經加入到了10個用戶
static void iterator(){
Session s=HibernateUtils.getSession();
Query q=s.createQuery("fromUser");
Iterator<User> users =q.iterate();
while(users.hasNext()){
System.out.println(users.next().getName().getFirstName());
}
}
首先把10個用戶的id都查詢出來,而後按照id去查詢詳細信息,這是會到一級緩存中查找,找不到在到二級緩存,找不到在到數據庫中查找.假設都到數據庫中查詢,那麼就進行了11次查詢,第一次把全部的id都查詢,而後再逐一按照id查詢進行10次,總共進行了11次,因此在使用時必定要當心,是否肯定一級緩存和二級緩存中有咱們想要查詢的數據,否則的話,性能就降低了
(2).在懶加載的狀況下,就會出現N+1次查詢,好比一對一:
首先查詢IdCard獲得id,而後再去訪問Person
Session s=HibernateUtil.getSession();
Query q=s.createQuery("fromIdCard");
List<IdCard> ics=q.list();
for(IdCard> ic:ics){
System.out.println(ic.getPerson().getName());
}
由於懶加載,每次訪問數據的時候,都進行查詢數據庫.
12.load方法的懶加載及原理分析
(1).Useruser=(User)s.load(userClass,id);
System.out.println(user.getClass());
就是說s.load(userClass,id)返回的是User的一個代理對象.便是User的子類.在session沒有關閉前,去訪問數據庫user.getName();可是這種方式很差,最好使用Hibernate.initialize(user);初始化懶加載.
(2).懶加載是將與數據庫的交互延遲,提升性能.load()方法,不會到數據庫查詢,只會返回一個User的一個子類.
(3).asm.jar,cglib.jar這兩個包實現懶加載,可以動態的修改內存中的字節碼.即動態的生成一個User的子類.
(4).employee.setUser(user);這是就能夠使用懶加載,創建employee和user之間個關聯,可是不須要去訪問數據庫的時候
(5).經過asm和cglib兩個包實現的,Domain是非final的,session.load懶加載
one-to-one懶加載:必須知足三個條件才能實現懶加載:第一:主表不能有constrained=true,因此主表沒有懶加載,第二:lazy!=false,第三:fetch=select;
one-to-many懶加載:第一:lazy!=false,第二:fetch=select
many-to-one:第一:lazy!=false,第二:fetch=select
many-to-many:第一:lazy!=false,第二:fetch=select
(6).可以懶加載的對象都是被改寫過的代理對象,當相關聯的session沒有關閉時,訪問這些懶加載對象(代理對象)的屬性(getId和getClass除外),hibernate會初始化這些代理,或用Hibernate.initialize(proxy)來初始化代理對象,當相關聯的session關閉後,再訪問懶加載的對象將出現異常.
(7).方法getId和getClass不須要訪問數據庫也是知道的,因此不是出現懶加載的初始化異常.
(8).表中的屬性也能夠使用懶加載的,只是須要在編譯後的內容進行處理,這種用途主要在字段是大文本類型時須要.
13.OpenSessionInView模式的代碼分析
(1).ThreadLocal類
private static ThreadLocal session=newThreadLocal();
線程級變量,做用域在一個線程內.
Session s=(Session)session.get();
if(s==null)}
s=getSession();
session.set(s);
}
當有一個web請求來時,服務器建立一個線程進行服務,將建立一個session,因此在這個線程內能夠訪問到session
(2).sessioncontext和事務邊界
用current_session_context_class屬性來定義context(用sessionFactory.getCurrentSession()來得到session),其值爲:
第一:Thread:ThreadLocal來管理Session實現多個操做共享一個Session,避免反覆獲取Session,並控制事務邊界,此時session不能調用close,當commit或rollback的時候session會自動關閉(connection.realease_mode:after_transaction).Opensession in view:在生成(渲染)頁面時保持session打開,前面所說的懶加載時,能夠保證session沒有關閉,能夠訪問到數據.
第二:由JTA事務管理器來管理事務(connection.release_mode:after_statement)
(3).用戶發送請求->web容器->doFilter(過濾器)->OpenSessionView->打開session,事務->ActionServlet(struts)的service方法->根據配置文件找到->Action(execute方法)->業務邏輯層(register方法)->Dao層(addUser方法)->返回,直到doFilter的commit,提交事務.在這個過程當中session都沒有關閉,能夠解決事務的邊界問題,解決懶加載的問題(即何時使用懶加載).缺點:延長事務,session的生命週期,session延遲關閉,那麼一級緩存不會釋放,長時間佔用內存.客戶端的網速比較慢,致使事務和session長時間不能關閉.即延遲關閉.會給服務器端形成很大的負載.
14.Session接口及getloadpersist方法
(1).因爲Session能夠管理多個數據庫表對應的多個實體對象,若是要查詢id爲1的實體對象,Session.get方法須要知道去哪一個數據庫表中查詢id爲1的記錄,因此,除了給get方法傳遞所要查詢的實體對象的id值外,還必須給get方法傳遞實體對象的類型,get方法才能知道去哪一個數據庫表中進行查詢
(2).經過類的類型能夠去hibernate.cfg.xml文件中查找到對應的表
(3).在配置文件中添加標籤<propertyname="show_sql">true</property>//能夠打印sql語句
(4).Useruser=(User)s.get(userClass,id);與User user=(User)s.load(userClass,id);的區別,load不會去訪問數據庫,只有第一次訪問時,纔會訪問數據庫.增長一條打印出user1的類名的代碼,就能夠看到load方法所返回的User子類的名稱了,該語句以下:
System.out.println(user1.getClass().getName());
(5).s.save(user)和s.persist(user);都是存儲數據,persist方法沒有sql語句,沒有開啓事務,save會回滾,persist不會回滾
15.Session與SessionFactory的多線程問題
Session內部封裝了一個connection對象,儘可能遲的建立鏈接,儘可能早的釋放鏈接
16.本地sql查詢與命名查詢
(1).使用Query接口
static list sql(){
Session s=HibernateUtil.getSession();
Query q = s.createSQLQuery("select * fromuser").addEntity(User.class);//查詢的結果是User對象
List rs=q.list();
for(User r:rs){
System.out.println(r.getName());
}
}
(2).不一樣的數據庫,本地的查詢語句是不一樣的,因此這種本地的查詢語句最好不要使用,兼容性和移植性很差.
(3).命名查詢:將查詢語句放在配置文件中,之後修改查詢語句只修改配置文件中的查詢語句就能夠了.
<queryname="getUserByBirthday">
<![CDATA[from User wherebirthday=:birthday]]>
</query>
這個定義能夠放到class標籤內部,不須要使用全名,只須要getUserByBirthday便可,可是在這個範圍內,不能出現重名,若是在外部,那就須要全名了,cn.itcast.hibernate.domain.User.getUserByBirthday
在配置文件中
static List namedQuery(){
Session s=HibernateUtil.getSession();
Queryq=s.getNamedQuery("getUserByBirthday");
q.setDate("birthday",new Date());
return q.list();
}
(4).hibernate能夠作到用Map代替Domain對象,存入到數據庫,可是這就符合ORM定義了,同時也能夠將數據庫中的內容轉換XML
17.多對多關聯關係的查詢
使用表之間的關聯join,效率低
18.多對多關聯關係的映射與原理分析
(1).多對多(teacher-student):在操做和性能方面都不太理想,因此多對多的映射使用較少,實際使用中最好轉換成一對多的對象模型;Hibernate會爲咱們建立中間關聯表,轉換成兩個一對多.
<set name="teacher"table="teacher_student">
<key column="teacher_id"/>
<many-to-many class="Student" column="student_id"/>
</set>
ER圖:teacher:id(PK);student:id(PK);teacher_student:teacher_id(PK,FK1),student_id(PK,FK2)
(2).
public class Teacher{
private int id;
private String name;
private Set<Student> students;
//省略get/set方法
}
public class Student{
private int id;
private String name;
private Set<Teacher> teachers;
//省略get/set方法
}
teacher的映射文件:
<class name="Teacher">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students" table="teacher_student">
<key cloumn="teacher_id"/>
<many-to-many class="Student"column="student_id">根據student_id去查詢學生的相關信息
</set>
</class>
同理student的映射文件類似.
(3).測試類:
Set<Teacher> ts=newHashSet<Teacher>();
Teacher t1=new Teacher();
t1.setName("t1 name");
Teacher t2=new Teacher();
t2.setName("t2 name");
ts.add(t1);
ts.add(t2);
Set<Student> ss=newHashSet<Student>();
Student s1=new Student();
s1.setName("s1");
Student s2=new Student();
s2.setName("s2");
t1.setStudents(ss);//創建關聯關係
t2.setStudents(ss);
ss.add(s1);
ss.add(s2);
s.save(t1);
s.save(t2);
s.save(s1);
s.save(s2);
在中間表中插入數據
19.多對一的懶加載分析
(1).查詢員工的信息時,是否須要部門的信息,默認的狀況下是懶加載的方式,怎樣判斷是否進行了懶加載,能夠經過打印出的sql語句中的查詢語句便可
(2).當IdCard中的id是主鍵也是外鍵,當id有值時,必定有一個person與之對應,因此能夠使用懶加載,先生成一個代理對象,當須要person的信息時,纔去查詢,反過來,由於person中的id只是個主鍵,知道person的id,IdCard中不必定有一個值與之對應,因此不使用懶加載的方式,而是直接去查詢數據庫,這就是查詢主表時不使用懶加載,查詢從表時使用懶加載.
(3).可是多對一的部門和員工,直接就是用了代理,depart.getEmps()獲取員工時,Hibernate中的集合把集合空對象和空集合是相同的概念.
20.多對一關聯關係的檢索與原理分析
(1).查詢操做(department表的查詢和之前同樣,只是employee表不同):
static Employee query(int empid){
Employee emp =(Employee)s.get(Employee.class,empid);
System.out.println("departname:"+emp.getDepart().getName());//獲得department的名稱.
return emp;
}
進行兩次查詢,首先根據id查詢employee表,獲得depart_id,在根據depart_id查詢department表.
21.多對一關聯關係的映射與原理分析
(1).多對一:映射文件:<many-to-one name="depart"column="depart_id"/>
ER圖中定義Employee主鍵(PK):id和外鍵(FK):depart_id,Department的主鍵id;
(2).創建Department類
public class Department{
private int id;
private String name;
//省略get/set方法
}
創建Employee類
public class Employee{
private int id;
private String name;
private Department depart;
//省略get/set方法
}
(3).映射文件:
<hibernate-mappingpackage="cn.itcast.hibernate.domain">
<class name="Emplyee">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="depart"colum="depart_id"/>
</class>
</hibernate-mapping>
不在使用標籤property,是對象類型depart,使用標籤<many-to-one>
經過反射能夠找到depart對應的映射文件,當depart_id與depart映射文件中的id相同時,就查找到了.也能夠使用屬性not-null="true",設置colum="depart_id"這列不爲空
(4).column="depart_id"不設置能夠,默認就是column="depart"
(5).staticDepartemnt add(){
//模板代碼,省略
Department depart = new Department();
depart.setName("depart name");
Employee emp = new Employee();
emp.setDepart(depart);//直接賦值就能夠了,只要在對象創建關係,數據庫中的表就創建關係了.
emp.setName("emp name");
s.save(depart);
s.save(emp);
return depart;
}
(6).當s.save(depart);與s.save(emp)兩條語句的順序調換,會多出現一條更新語句,由於首先存儲emp,當存儲到depart時,由於employee中定義了department,因此hibernate檢測到employee中的depart發生改變了,就進行了更新操做.此時是持久態
22. 分佈式緩存的分析
大型網站有多個服務器,即就有多個cache,每一個服務器對應一個cache,
23.關聯關係的級聯操做
(1).cascade和inverse:
Casade用來講明當對主對象進行某種操做時是否對其關聯的從對象也作相似的操做,經常使用的cascade:none,all,save-update,delete,lock,refresh,evict,replicate,persist,merge,delete-orphan(one-to-many),通常對many-to-one,many-to-many不設置級聯,在one-to-one和one-to-many中設置級聯
Inverse表「是否放棄維護關聯關係」(在Java裏兩個對象產生關聯時,對數據庫表的影響),在one-to-many和many-to-many的集合定義中使用,inverse="true"表示該對象不維護關聯關係;該屬性的值通常在使用有序集合時設置成false(注意hibernate的缺省值是false),one-to-many維護關聯關係就是更新外鍵,many-to-many維護關聯關係就是在中間表增減記錄
注:配置成one-to-one的對象不維護關聯關係.
24.緩存的原理與模擬分析
(1).第一我的讀的信息和後一我的讀的信息可能相同,那第二我的讀信息時可以加快速度了.
(2).第二人讀取信息時,就不是到數據庫中讀取了,能夠到緩存中讀取數據.
(3).使用緩存cache存取數據.
25.繼承_鑑別器與內鏈接器相結合
(1).子類的特有屬性不少,就拿一張表進行對應,特有屬性少的就和父類放在同一個表中,
(2).employee:id(PK),name,depart_id,type,skill;sales:employee_id(PK,FK),sales;
Skiller子類和Employee父類放在一塊兒,Sale類本身對應一張表.
(3).映射文件中只需按照前兩中方式進行改變.
26.繼承_每一個具體類映射一張獨立表
(1).沒有公共的屬性,全部的屬性都是本身特有的,在插入時候不須要涉及到多個表的關聯了,效率高.若是employee不是抽象的,會有employee表
(2).employee:id(PK),name,depart_id;skiller:id(PK),name,skill,depart_id;sales:id(PK),name,sell,depart_id;
(3).映射文件:
<union-subclass name="Skiller"table="skiller">
<property name="skill"/>
</union-subclass>
<union-subclass name="Sales"table="sales">
<property name="sell"/>
</union-subclass>
(4).在查詢的時候,多態查詢時,仍是要進行三種表的關聯查詢,可是插入只在一張表進行.
27.繼承關係_每一個類映射到一張表
(1).employee:id(PK),name,depart_id;sales:employee_id(PK,FK),sell;skiller:employee_id(PK,FK),skill;
(2).此時不須要鑑別器了,每一個子類對應一張表
(3).映射文件:
<joined-subclassname="Skiller" table="skiller">
<key column="employee_id"/>
<property name="skill"/>
</joined-subclass>
<joined-subclass name="Sales"table="sales">
<key column="employee_id"/>
<property name="sell"/>
</joined-subclass>
(4).插入子類時,相同的屬性插入到employee表中,本身特有的屬性插入到本身表中,若是插入一個技術員Skiller(name,skill)時skill插入skiller表中,name插入employee表中,這時就插入了兩張表.
(5).當查詢本身特有的屬性時,會關聯兩張表,當查找相同的屬性時,會關聯三張表.因此查詢時效率低.不要進行多態查詢,最好查詢具體的子類:
具體查詢:Employee emp = (Employee)s.getId(Skiller.class,id);
多態查詢:Employee emp = (Employee)s.getId(Skiller.class,id);
28.繼承關係_整個繼承樹映射到一張表
(1).public classSkiller extends Employee{
private String skill;
//省略get/set方法
}
public class Sales extends Employee{
private int sell;
//省略get/set方法
}
(2).employee表中的字段:id(PK),name,depart_id,type(區分不一樣類型的員工,又稱鑑別器),skill,sell
(3).這種方式當增長子類時,須要修改employee表結構.
(4).映射文件:
<class name="Employee"discriminator-value="0">
<id name="id">
<generator class="native"/>
</id>
<discriminator column="type"type="int"/>鑑別器,hibernate用來區分不一樣的子類.
<subclass name="Skiller"discriminator-value="1">
<property name="skill"/>
</subclass>
<subclass name="Sales"discriminator-value="2">
<property name="sell"/>
</subclass>
</class>
(5).將一棵繼承樹映射到一張表中,因此在查詢時,只對一張表進行操做,效率高,可是不靈活,當增長子類時,須要更改表結構,同時每一個字段不能設置成非空約束.
29.實體對象的三種狀態與saveOrUpdate方法
(1).Session的幾個主要方法:
第一:save,persist保存數據,persist在事務外不會產生insert語句
第二:delete:刪除對象
第三:update:更新對象,若是數據庫中沒有記錄,會出現異常
第四:get:根據ID查詢數據,會馬上訪問數據庫
第五:load:根據ID查詢,(返回的是代理,不會當即訪問數據庫)
第六:saveOrUpdate,merge(根據ID和version的值來肯定是save或update),調用merge你的對象仍是託管的
第七:lock(把對象編程持久對象,但不會同步對象的狀態)
(2).瞬時(transient):數據庫中沒有數據與之對應,超過做用域會被JVM垃圾回收器回收,通常是new出來且與session沒有關聯的對象
持久(persistent):數據庫中有數據與之對應,當前與session有關聯,而且相關聯的session沒有關閉,事務沒有提交;持久對象狀態發生改變,在事務提交時會影響到數據庫(hibernate能檢測到)
脫管(detached):數據庫中有數據與之對應,但當前沒有session與之關聯;託管對象狀態發生改變,hibernate不能檢測到.
(3).當關閉session時,持久態就變成了脫管狀態了,區分這三種狀態的兩個標準:是否與數據庫中記錄相對應,是否在session中.
(4).當在脫管的狀態時,更新的時候須要執行update的更新語句,由於不在session中.
(5).對象new是瞬時的,get(),load(),find(),iterate()等是持久的,瞬時狀態執行save(),saveOrUpdate()時變成持久的,當持久狀態執行delete()時變成瞬時的,當脫管狀態執行update(),saveOrUpdate(),lock()時變成持久狀態,當持久狀態執行evict(),close(),clear()時,持久狀態變成脫管狀態.
(6).瞬時對象的id沒有值,脫管對象的id是有值的.因此當沒有值時執行save()方法,當有值時執行update()方法.
30.實體類或屬性名與數據庫關鍵字衝突問題
使用Oracle時,user是個關鍵字,可能出現問題,將表名添加反引號.
31.使用Hibernate完成CRUD實驗的步驟說明
(1).實驗步驟:
第一步:設計domain對象User
第二步:設計UserDao接口
第三步:加入hibernate.jar和其依賴的包
第四步:編寫User.hbm.xml映射文件,能夠基於hibernate/eg目錄下的org/hibernate/auction/User.hbm.xml修改
第五步:編寫hibernate.cfg.xml配置文件,能夠基於hibernate/etc/hibernate.cfg.xml修改;必須提供的幾個參數:connection.driver_class、connection.url、connection.username、connection.password、dialect、hbm2ddl.auto
第六步:編寫HibernateUtils類,主要用來完成hibernate初始化和提供一個得到Session的方法
第七步:實現UserDao接口
32.事務的悲觀鎖和樂觀鎖
(1).悲觀鎖和樂觀鎖
悲觀鎖由數據庫來實現;樂觀鎖hibernate用version和timestamp來實現,悲觀鎖就至關於寫鎖,當本身在操做時,別人不能進行任何操做,
(2).可能多我的來讀取同一個數據源,可能後一我的修改後的結果覆蓋前一我的修改的結果,存在併發問題
(3).悲觀鎖是不可取的,咱們給每條記錄添加一個版本號,當同時操做數據源時,判斷版本號,若是版本號不符合,就不進行更新.假設剛開始版本號爲0,同時來兩我的進行操做,判斷版本號是否爲0,若是爲0,就進行操做,操做完後版本號加一,那第二我的就發現版本號不等於0,就不會進行操做了,也不會覆蓋前一我的進行的操做.
(4).在映射文件中:
<versionname="ver"/>該標籤必須在id標籤的下面,便是id的子標籤.
(5).版本號的類型是整型的,也能夠是日期型的
(6).
Session s1=HibernateUtil.getSession();
Transactiontx1=s1.beginTransaction();//第一個線程操做事務
User user1=(User)s1.get(User.class,id);
Session s2 =HibernateUtil.getSession();
Transactiontx2=s2.beginTransaction();//第二個線程操做事務
User user2=(User)s2.get(User.class,id);
user1.getName().setFirstName("firstName1");
user2.getName().setFirstName("firstName2");
tx2.commit();//線程二先提交,成功了
tx1.commit();//線程一提交不成功.由於版本號不同.
33.事務與事務邊界的相關知識
(1).一個SessionFactory對應一個數據庫,由JDBC實現
(2).事務的控制應該在業務邏輯層實現.可是事務的對象是在DAO層,那麼在業務邏輯層中調用事務的對象,就出現了耦合,因此要解決這個耦合,就需藉助第三方架包了EJB,Spring
34.完善HibernateUtil類及hql查詢入門
(1).HQL:面向對象的查詢語言,與SQL不一樣,HQL中的對象名是區分大小寫的(除了Java類和屬性其餘部分不區分大小寫),HQL中查的是對象而不是和表,而且支持多態;HQL主要經過Query來操做,Query的建立方式:Query q=session.createQuery(hql);
from Person
from User as userwhere user.name=:name//其中User是類不是表名,user是別名
form User as userwhere user.name=:name and user.birthday<:birthday
(2).Criteria:是一種比HQL更面向對象的查詢方式;Criteria的建立方式:Criteria crit=session.createCriteria(DomainClass.class);
簡單屬性條件如:
criteria.add(Restrictions.eq(propertyName,value)),criteria.add(Restrictions.eqProperty(propertyName,otherPropertyName))
(3).public staticvoid add(Object entity){//可以保存全部對象
Session s=null;
Transactiontx=null;
try{
s=HibernateUtil.getSession();
tx.s.beginTransaction();
s.save(entity);
tx.commit();
}catch(HibernateExceptione){
if(tx!=null)
tx.rollback();//不只要回滾,還有拋出異常
throw e;
}finally{
if(s!=null)
s.close();
}
}
同理更新,刪除同時一樣的道理
(4).執行HQL語句
Session s=null;
try{
s=HibernateUtil.getSession();
Stringhql="from User as user where user.name=?";
Queryquery=s.createQuery(hql);
query.setString(0,name);//替換佔位符
Listlist=query.list();//JDBC中的executQuery()相似
for(Useruser:list{
System.out.println(user.getName());
}
//Object obj=query.uniqueResult();當肯定返回值只有一個的時候,使用這種方法.當查詢有多個結果時,會出現異常
}finally{
if(s!=null)
s.close();
}
支持多態,查詢的話,子類對應數據庫表也被查詢,若是from Object的話,會把數據庫中的表都查一遍,由於全部的類都是Object的子類.
35.一對多關聯關係的映射與原理分析
(1).在Department的角度上是否是一對多了,在Department中定義:
privateSet<Employee> emps;//一個department中有多個員工
(2).映射文件:
<classname="Department">
<idname="id">
<generatorclass="native"/>
</id>
<propertyname="name"/>
<setname="emps">用set屬性進行映射
<keycoluem="depart_id"/>設置外鍵
<one-to-manyclass "Employee"/>
</set>
</class>
(3).System.out.println("empsize:"+depart.getEmps().size());
打印出department中全部的employee人數.
(4).首先添加employee,在添加到department,即告訴employee屬於哪一個department;多兩條更新語句.
Set<Employee>emps = new HashSet<Employee>();
emps.add(emp1);
emps.add(emp2);
depart.setEmps(emps);
告訴department有哪些employee
emp1.setDepart(depart);
emp2.setDepart(depart);
(5):ER圖:Deparment:id(PK);Employee:id(PK),depart_id(FK1);
36.一對多和多對多的懶加載分析
(1).對於one-to-one懶加載方式體現出的效率不是很明顯,查詢身份證號時,把person的信息也查詢出來,沒有查詢太多的信息,對效率的影響不是很大
(2).對於one-to-many懶加載方式就體現的很明顯的了,當咱們查詢部門的詳細信息時,可能把該部門的全部員工都查詢出來,由於一個部門可能有不少員工,因此這時效率就明顯下降了.
(3).缺省的是懶加載,當depart.getEmps()時,纔會查詢員工的信息,由於java中的set集合沒有懶加載的功能,當咱們的代碼只是獲取集合代理對象的引用,比沒有調用該集合代理對象的方法,因此,hibernate在這裏還用不着去查詢數據庫來填充集合代理,所以不會拋出"代理未初始化"的異常,若是將代碼改成depart.getEmps().size(),就能夠看到異常了.
(4).對於many-to-many方式懶加載也很重要,由於涉及到三張表的查詢.因此也須要懶加載的功能.
37.一對一的懶加載分析
(1).one-to-one在查詢主對象的時候默認狀況下不使用懶加載,使用一個關聯查詢.可是在查詢從對象的時候使用了懶加載.
(2).constrain=true是創建外鍵約束
(3).lazy="proxy",使用懶加載,默認的值也是proxy,還有false,true的取值
(4).fetch="join",使用什麼方式去抓取,默認值爲select,join是一次查詢(表的鏈接),select是兩次查詢.當lazy="proxy"時,fetch="join"是無效的,它們倆之間的設置是互斥的.
38.一對一外鍵關聯關係的映射與原理分析
(1).一對一:基於外鍵的one-to-one,能夠描述爲多對一,加上unique="true"約束<one-to-onename="idCard" property-ref="person"/><many-to-onename="person" column="person_id" unique="true"not-null="true"/>區別於多對一.只需將外鍵設置爲惟一.
(2).對於IdCard的映射文件,其的id不是外部生成的,而是自增加的.
<generatorclass="native"/>對於Person的映射文件:<one-to-one name="idCard"property-ref="person"/>
39.一對一主鍵關聯關係的檢索
(1).查詢主對象:
Personp=(Person)get(Person.class,id);
System.out.println(p.getIdCard().getId());
理論上是兩次查詢,可是實際只進行了一次查詢,使用了表之間的關聯join,效率上比兩次查詢高
查詢從對象:
IdCardidCard=(IdCard)get(IdCard.class,id);
System.out.println(idCard.getPerson().getId());
理論上和實際上都進行了兩次查詢
40.一對一主鍵關聯關係的映射與原理分析
(1).基於主鍵的one-to-one(person的映射文件)
<id name="id">
<generatorclass="foregin"><paramname="property">idCard</param></generator>
</id>
<one-to-one name="idCard"constrained="true"/>
(2).對象模型:
public class Person{
private int id;
private String name;
private IdCard idCard;
//省略get/set方法
}
public class IdCard{
private int id;
private String name;
private Person person;
//省略get/set方法
}
(3).Person的映射文件:
<class name="Person">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<one-to-one name="idCard"/>
</class>
IdCard的映射文件:
<class name="IdCard">
<id name="id">
<generator class="foregin">主鍵是由外部獲得,不是本身獲得的
<paramname="property">person</param>IdCard的id是由person獲得的
</generator>
</id>
<property name="name"/>
<one-to-one name="person" constrained="true"/>添加約束,配置外鍵.
</class>
idcard中的主鍵是person中的外鍵
(4).測試代碼:
IdCard idCard = new IdCard();
Person p=new Person();
p.setName("p1");
p.setIdCard(idCard);
idCard.setPerson(p);
s.save(p);
s.save(idCard);
IdCard中的id是由Person獲得的.只有主對象(person)存在,從對象(idcard)存在.
(5).ER圖:person:id(PK);IdCard:id(PK,FK)
41.組件關聯關係的映射與原理分析
(1).組件映射(User-Name):關聯的屬性是個複雜類型的持久化類,但不是實體即:數據庫中沒有表與該屬性對應,但該類的屬性要之久保存的
<component name="name"class="com.test.hibernate.domain.Name">
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
當組件的屬性不能和表中的字段簡單對應的時候能夠選擇實現:
org.hibernate.usertype.UserType或
org.hibernate.usertype.CompositeUserType
(2).用戶名name是個對象類型
public Class Name{
private String firstName;
private String lastName;
//get/set方法省略
}
想過使用一對一.一對多,多對一均可以,一我的只能有一個名字,一我的能夠有多個名字.這是數據庫中確定有兩張表:User和Name,可是如今Name的內容很小,不想設計成一個實體,不想在數據庫中對應一張表,由於它過小了,此時就是用組件相關聯,將用戶user和名字name設計到同一張表中
6、 JDBC
1.DAO設計思想與搭建骨架
(1).創建Domain包,在包中創建一個實體對象(bean).
public class User{
private int id;
private String name;
private Date birthday;//java.util.Date
private float money;
//生成對應的get/set方法,省略
}
(2).定義Domain接口:
public interface UserDao{
public void addUser(User user);
public User getUser(int userid);
public void update(User user);
public void delete(User user);
public User findUser(StringloginName,String password);
}
這個接口是給service層使用的.
(3).實現UserDao接口
public class UserDaoImpl implements UserDao{
public void addUser(User user){};
public User getUser(int userid){};
public void update(User user){};
public void delete(User user){};
public User findUser(StringloginName,String password){};
}
(4).在UserDaoImpl中拋出的異常進行包裝,定義一個異常類
(5).工廠模式:UserDao userDao = DaoFactory.getInstance().getUserDao();
2.Java的動態代理及使用該技術完善鏈接代理
(1).前面說到的靜態代理模式,有點麻煩,由於須要實現接口Connection的全部方法.
(2).public classMyConnectionHandler implements InvocationHandler{
private Connection realConnection;
MyConnectionHandler(){
}
Connectionbind(Connection realConn){//經過此方法將鏈接傳進來
ConnectionwarpedConnection = (Connection)Proxy.newProxyInstance(this.getClass().getClassLoader(),newClass[]{Connection.class},this);//動態的編寫一個類,這個類實現Connection接口,最終會把Connection的全部方法都交給InvocationHandler處理器處理,在內存中直接產生一個字節碼.
returnwarpedConnection;
}
public Objectinvoke(Object proxy,Method method,Object[] args){
if("close".equals(method.getName())){//是close方法
this.dataSource.connectonsPool.addList(this.warpedConnection);
}
returnmethod.invoke(this.realConnection,args);
}
}
這就是動態代理模式,無論是動態的,仍是靜態的,最終到底都是關心操做Connection的方法.
3.JdbcTemplate類中的其餘各個查詢方法
(1).Spring的JdbcTemplate
第一:查詢帶有參數,和行映射方法:
public ObjectqueryForObject(String sql,Object[]args,RowMapper rowMapper),使用自定義的UserRowMapper完成映射
一個RowMapper的經常使用實現BeanPropertyRowMapper,該實現可將結果集轉換成一個Java Bean(字段名與Java Bean屬性名不符合規範,可用別名處理)返回一條記錄.
第二:public List query(String sql,Object[]args,RowMapperrowMapper)返回多條記錄
第三:public int queryForInt(String sql)(如:selectcount(*) from user),其餘結果好比String可用queryForObject方法向下轉型
public MapqueryForMap(String sql,Object[]args)返回不是對象類型的Map(key:字段名或別名,value:列值);當查詢的結果不是一個對象時,就是用一個Map進行存放結果.查詢共多少條記錄,最大值,最小值等信息時,當返回的是String類型時,就是用queryForObject(String sql);只是要對返回類型進行轉換.
第四:public List queryForList(String sql,Object[]args)返回多個Map
4.JDBC的理論概述
(1).JDBC(Java數據庫鏈接)由一些藉口和類構成的api,j2se的一部分,由java.sql,javax.sql包組成
(2).應用程序、JDBC API、數據庫驅動及數據庫之間的關係:
應用程序-->JDBC-->MySql Driver,Oracle Driver,DB2Driver--->MySql,ORacle,DB2
5.jdbc中數據類型與日期問題
(1).rs.getInt("id"),getString("name"),rs.getDate("birthday"),rs.getFloat("money")),不一樣的類型的獲取數據.
(2).java.sql.Date是繼承java.util.Date,java.util.Date是日期和時間的,而java.sql.Date只有日期,而沒有時間.
(3).不能將java.util.Date賦給java.sql.Date,因此:newjava.sql.Date(birthday.getTime()));這樣就能夠將java.util.Date轉換成java.sql.Date,java.sql.Date直接賦給java.util.Date能夠的.
(6).st.executeUpdate(sql),帶參數的方法是Statement的,不帶參數的方法是PreperedStatement的
6.JTA分佈事務的簡要介紹
(1).跨多個數據源的事務,使用JTA容器實現事務,分紅兩個階段提交
javax.transaction.UserTransactiontx=(UserTransaction)ctx.lookup("jndiName");
tx.begin();//connection1,connection2(可能來自不一樣的數據庫)
tx.commit()//tx.rollback();
(2).tomcat不支持這種容器.weblogic能夠支持.
(3).第一階段:向全部的數據庫提交事務的請求,當有事務回滾的請求,全部的數據庫都回滾,第二階段:當沒有回滾請求,就進行提交事務.
(4).分佈式事務處理.
7.Statement的sql注入問題
(1).SQL注入:PreparedStatement和Statement:
在sql中包含特殊字符或SQL的關鍵字(如:'or 1 or')時,Statement將出現不可預料的結果(出現異常或查詢的結果不正確),可用PreparedStatement來解決
PreperedStatement(從Statement擴展而來)相對Statement的優勢:
第一:沒有SQL注入的問題
第二:Statement會使數據庫頻繁編譯SQL,可能形成數據庫緩衝區溢出
第三:數據庫和驅動能夠對PreperedStatement進行優化(只有在相關聯的數據庫鏈接沒有關閉的狀況下有效)
PreparedStatement是Statement的子接口.
(2).
PreparedStatementps=null;//預處理接口,須要進行預處理,因此在構造的時候就須要SQL語句了,ps=conn.prepareStatement(sql);而Statement是在查詢的時候須要SQL語句.
Stringsql="select id,name from user where name=?";?問號是佔位符
ps.setString(1,name);將傳過來的name參數替換第一個佔位符?,在此過程當中,將name進行的處理,將特殊符號去除,當執行查詢時,不須要SQL語句了,否則會報錯,rs=ps.executeQuery();
(3).創建鏈接最消耗時間的,當程序執行屢次時,PreperedStatement比Statement除去創建鏈接的時間,前者效率高.
8.編寫一個基本的鏈接池來實現鏈接的重複使用
(1).鏈接池常用到插入和刪除,因此使用LinkedList,
public class MyDataSource{
private LinkedList<Connection>connectionsPool = new LinkedList<Connection>();
public MyDataSource(){
for(int i=0;i<10;i++){//開始時建立10個鏈接
this.connectionsPool.addLast(this.createConnection());
}
}
public Connection createConnection() {//建立鏈接
returnDriverManager.getConnection(url,user,password);
}
public Connection getConnection(){//獲取鏈接
return this.connectionPool.removeFirst();
}
public void free(Connection conn){//釋放鏈接
this.connectionsPool.addList(conn);
}
}
獲得鏈接並非重複的.想重複的拿取鏈接,建立的鏈接數n<用戶取鏈接數m,便可.
(2).
private static int initCount=5;//定義初始化鏈接數
private static int maxCount=10;//最大鏈接數
private static int currentCount=0;//當前建立的鏈接數
(3).爲了保證併發操做,須要在獲取鏈接中同步:
synchronized(connectionsPool){
if(this.connctionPool.size()>0)//鏈接池中還有鏈接
return this.connectionsPool.removeFirst();
if(this.currentCount<maxCount)//當前鏈接數沒有超過最大鏈接數,能夠接着建立鏈接,
return this.createConnection();
throw new SQLException("已經沒有鏈接了");//超過了最大鏈接數,拋出異常.
}
9.編寫一個簡單的JDBC的例子
(1).鏈接數據的步驟:
第一步:註冊驅動(只作一次)
DriverManager.registerDriver(newcom.mysql.jdbc.Driver());
第二步:創建鏈接(Connection)
Connectionconn=DriverManager.getConnection("jdbc:mysql://localhost:3305/jdbc","root","");沒有密碼
第三步:建立執行SQL的語句(Statement)
Statementst=conn.createStatement();
第四步:執行語句
ResultSet rs =st.executeQuery("select * from user");
第六步:處理執行結果(ResultSet)
while(rs.next()){//遍歷行
System.out.println(rs.getObject(1)+'\t'+rs.getObject(2));//第一列,第二列
}
第七步:釋放資源
rs.close();//關閉資源和打開資源的順序是相反的
st.close();
conn.close();
10.參數的元數據信息
(1).
Connection conn=JdbcUtils.getConnection();
PreparedStatementps=null;
ResultSet rs=null;
ps.conn.prepareStatement(sql);//sql中可能含有參數(佔位符),Object[]params存儲參數,能夠動態的查看sql中含有哪些參數.
ParameterMetaDatapmd=ps.getParameterMetaData();
intcount=pmd.getParameterCount();//獲得參數的個數
for(inti=1;i<count;i++){
System.out.println(pmd.getParameterClassName(i));//獲得參數的類名
System.out.println(pmd.getParameterType(i));//獲得參數的類型
ps.setObject(i,parames[i-1]);//遍歷替換參數
}
String sql ="select * from user where name=? and birthday<? and money>?";
直接返回的類型都是String,VARCHAR
11.分析jdbc程序的編寫步驟和原理
(1).鏈接是經過底層的TCP/IP協議進行的
(2).註冊驅動:
方式一:Class.forName("com.mysql.jdbc.Driver");
推薦這種方式,不會對具體的驅動類產生依賴,類加載到內存中,會調用靜態代碼塊:
static{
try{
DriverManager.registerDriver(new Driver());
}catch(SQLException e){
throws RuntimeException();
}
}
方式二:DriverManager.registerDriver(newcom.mysql.jdbc.Driver());
會形成DriverManager中產生兩個同樣的驅動,並會對具體的驅動類產生依賴,其內部定義了一個Vector列表,將多個驅動存放到Vector中
方式三:System.setProperty("jdbc.drivers","driver1:driver2");
雖然不會對具體的驅動類產生依賴;但註冊不太方便,因此不多使用,能夠註冊多個驅動
(3).方式一接受的是一個字符串,方式二接受的是一個驅動類,因此具備依賴關係
(4).建立鏈接:
Stringurl="jdbc:mysql://localhost:3394/jdbc";
格式:jdbc:子協議:子名稱//主機名:端口/數據庫名
String user="root";
String password="";
Connectionconn=DriverManager.getConnection(url,user,password");
(5).釋放資源:數據庫創建鏈接的個數也是有限制的,當數據庫建立了多個鏈接,數據庫可能運行的很慢,可能致使數據庫崩潰,佔用系統資源.
12.分析在實際項目中該如何應用JDBC
(1).三層架構:
表示層:基於web的jsp、servlet、struts、webwork、spring web MVC等,基於客戶端的swing,swt等
業務邏輯層:Pojo(service,manager),Domain,session EJB、spring
數據訪問層:JDBC,IBatis,Hibernate,JDO,Entity Bean
層與層之間用接口隔離
13.規範和封裝JDBC程序代碼
(1).規範的代碼:
Stringurl="jdbc:mysql://localhost:2332/jdbc";
String user="root";
String password="";
Statement st=null;
ResultSet rs=null;
Connecton conn=null;
try{
Class.forName("com.mysql.jdbc.Driver");
conn=DriverManager.getConnection(url,user,password);
st = conn.createStatement();
rs=st.executeQuery("select * fromuser");
}finally{
try{
if(rs!=null)
rs.close();
}finally{if(st!=null)
try{
st.close();
}finally{
if(conn!=null)
conn.close();
}
}
}
}
(2).設計一個工具類:
public final class JdbcUtils{
private static Stringurl="jdbc:mysql://localhost:2332/jdbc";
private static String user="root";
private static String password="";
private JdbcUtils(){//不容許實例化
}
static{//驅動只註冊一次
try{
Class.forName("com.mysql.jdbc.Driver");
}catch(ClassNotFoundException e){
throw new ExceptionInitializerError(e);
}
}
public static Connection getConnection(){//建立鏈接
returnDriverManager.getConnection(url,user,password);
}
public static void free(ResultSetrs,Statement st,Connection conn){//釋放資源
try{
if(rs!=null)
rs.close();
}catch(SQLException e){
e.printStackTrace();
}finally{if(st!=null)
try{
st.close();
}catch(SQLException e){
e.printStackTrace();
}finally{
if(conn!=null)
conn.close();
}
}
}
}
}
14.將Dao中的修改方法提取到抽象父類中
(1).對於代碼的重構,焦點就是將代碼變化的部分和不變的部分分離開來.一個是sql語句不一樣,參數不一樣,提取一個超類,相同的部分,放到超類中,不一樣的部分由子類實現.
publci abstract class AbstractDao{
public void update(String sql,Object[]args){
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
conn=JdbcUtils.getConnection();
ps=conn.prepareStatement(sql);
for(int i=0;i<args.length;i++){//用args參數列表,更新數據
ps.setObject(i+1,args[i]);
}
}
//args就是參數列表,
}
public class UserDaoImpl extendsAbstractDao{
public void update(User user){
String sql="update user setname=?,birthday=?,money=?,where id=?";
Object[] args=new Object[]{user.getName(),user.getBirthday(),user.getMoney(),user.getId()};
super.update(sql,args);//調用父類的update方法.
}
}
15.可更新和對更新敏感的結果集
(1).
st=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,Result.CONUR_UPDATABLE);
在讀取數據時,能夠更改數據,可更新結果集.
(2)
while(rs.next()){
rs.getObject("name");
rs.getObject("money");
String name= rs.getString("name");
if("lisi".equals(name)){
rs.updateFloat("money",200f);
rs.updateRow();//更新行
}
}
(3).這種方式不經常使用,在查詢時,把數據更改了,給人一種不明確感.
在查詢結果集時,更新數據時數據庫能不能感知到數據更新了.數據庫敏不敏感SENSITIVE
16.利用結果集元素數據將查詢結果封裝爲map
(1).將查詢結果放在map中
key:列的名稱
value:列的值
(2).
Connection conn=JdbcUtils.getConnection();
ps=conn.prepareStatement(sql);
rs=ps.executeQuery();
ResultSetMetaData rsmd =rs.getMetaData();//獲取結果集的元數據
int count= rsmd.getColumnCount();//結果有多少列
for(int i=1;i<=count;i++){//循環遍歷列
System.out.println(rsmd.getColumnClassName(i));//每一列的類型名
System.out.println(rsmd.getColumnName(i));//每一列的名稱
System.out.println(rsmd.getColumnLabel(i));//每一列的別名
}
這裏就能夠準確的獲得每一列的類型,而不像前面的都是String類型.
String[]colName=new String[count];//存放每一列的名稱
Map<String,Object> data=null;
while(rs.next()){//按行循環
data = new HashMap<String,Object>();
for(int i=0;i<colNames.length;i++){//按列循環
data.put(colNames[i],rs.getObject(colNames[i]));//根據類名獲得列的值
}
}
靈活性很是高.可以按照各類方式查詢
16.如何使用開源項目DBCP
(1).dbcpconfig.properties數據源的配置文件:
鏈接配置:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=root
初始化鏈接:
initialiSize=10
最大鏈接數量:
maxActive=50
最大空閒鏈接://不一樣的時間段建立的鏈接不一樣,可能出現空閒鏈接.
maxIdle=20
最小空閒鏈接:
minIdle=5
超過等待時間以毫秒爲單位 6000毫秒/1000等於60秒
maxWait=60000
//當沒有鏈接可取的時候,讓當前線程等待一段時間,在去拿鏈接
JDBC驅動創建鏈接時附帶的鏈接屬性,屬性的格式必須爲這樣:[屬性名=property;],注意:"user" 與"password"兩個屬性會被明確地傳遞,所以這裏不須要包含它們(url後面攜帶的值)
connectionProperties=userUnicode=true;characterEncoding=gbk
指定由鏈接池所建立的鏈接的自動提交狀態
defaultAutoCommit=true
driver default指定由鏈接池所建立的鏈接的只讀(read-only)狀態,若是沒有設置該值,則"setReadOnly"方法將不被調用,(某些驅動並不支持只讀模式,如:Informix)
defaultReadOnly=
driver default指定 由鏈接池所建立的鏈接事務級別(TransactionIsoation),可用值爲下列之一(詳情可見javadoc)NONE,READ,UNCOMMITTED,READ_COMMITTE
defaultTransactionIsolation=READ_UNCOMMITTED
(2).DBCP是apache的開源項目.實現了DataSource接口.Data Base Connection Pool,修改代碼須要重新編譯,打包,修改配置文件只須要重啓便可.
(3).
Properties prop = new Properties();
InputStream is =JdbcUtils.class.getClassLoader().getResource.AsStream("dbcp.property");//讀取property文件
prop.load(is);
private static DataSoruce myDataSource=null;
myDataSource=BasicDataSourceFactory.createDataSource(prop);
(4).DataSource用來取代DriverManager來獲取Connection;經過DataSource得到Connection速度快;經過DataSource得到Connection都是已經被包裹過的(不是驅動原來的鏈接),它的close方法已經被修改了;通常DataSource內部會用一個鏈接池來緩存Connection,這樣能夠大幅度提升數據庫的訪問速度;鏈接池能夠理解成一個可以存放Connection的Collection;咱們的程序只和DataSource打交道,不會直接訪問鏈接池.
(5).使用dbcp須要的三個包:common-dbcp.jar,common-collections.jar,common-pool.jar
17.使用JDBCTemplate工具類簡化對象查詢
(1).Spring框架中提供了一個JdbcTemplate工具類,JdbcTemplate類對JDBC API進行了很好的封裝,這個類就像咱們本身對JDBC進行封裝同樣,只是代碼更健壯和功能更強大而已,咱們之後在實際項目中能夠使用JdbcTemplate類來徹底替換直接使用JDBC API,這與直接使用JDBC API沒有太大的性能區別,使用JdbcTemplate類須要額外從spring開發包中導入spring.jar和commons-logging.jar包
(2).JdbcTemplate的設計思想和前面的MyDaoTemplate類是相同的
static User findUser(String name){
JdbcTemplate jdbc = newJdbcTemplate(JdbcUtils.getDataSource());//參數是拿到一個數據源
String sql = "select id,name from userwhere name=?";
Object[]args=new Object[]{name};
jdbc.queryForObject(sql,args,newRowMapper(){//行映射器
public Object mapRow(ResultSet rs,introwNum){
User user = new User();
user.setId(rs.getInt("id"));
return user;
}
});
return null;
}
//這裏能夠不須要實現行映射器,能夠用類代替:
new BeanPropertyRowMapper(User.class);便可
18.使用JdbcTemplate實現Dao和用工廠模式靈活切換實現
(1).
public class UserDaoSpringImpl implementsUserDao{
private SimpleJdbcTemplatesimpleJdbcTemplate
= new SimpleJdbcTemplate(JdbcUtils.getDataSource());
//增長用戶
public void addUser(User user){
String sql = "insert into user(name,money,birthday)values(:name,:money,:birthday);
KeyHolder keyHolder = newGeneratedKeyHolder();
SqlParameterSource param = new BeanPropertySqlParameterSource(user);
this.simpleJdbcTemplate.getNamedParameterJdbcOperations().update(sql,param,keyHoler);
user.setId(keyHolder.getKey().intValue());
}
}
//增長user的代碼減小了太多了.
delete,update等方法都是大同小異
//刪除用戶
public void delete(User user){
String sql = "delete from user whereid=?";
this.simpleJdbcTemplate.update(sql,user.getId());
//使用了可變參數的特性,不須要複雜的Map存儲參數
}
//查詢用戶
public User findUser(StringloginName,String password){
//如何簡化返回查找集
String sql = "selectid,name,money,birthday from user where name=?";
returnthis.simpleJdbcTemplate.queryForObject(sql,ParameterizedBeanProertyRowMapper.newInstance(User.class),userId);
}
//更新用戶
public void update(User user){//如何替換佔位符?
String sql ="update user setname=?,birthday=?,money=? where id=?";
this.simpleJdbcTemplate.update(sql,user.getName(),user.getBirthday(),user.getMoney(),user.getId());
//這裏也能夠使用bean屬性的參數源;
}
代碼量大大減小了.
19.使用JDBC的批處理功能
(1).和數據庫打交道的成本是很高的,當須要發送多條sql語句時,成本更高了,這時就須要使用批處理技術,將多條查詢語句打成一個包.
(2).
for(int i=0;i<10000;i++){
ps.setString();
ps.setName();
ps.addBatch();//把一條更新語句增長到包中
}
int[] a = ps.executeBatch();//執行批處理,不是ps.executeUpdate();
(3).首先將語句打包時,並非包越大越好,若是包過大的話,可能形成內存溢出,因此可能將一個打包在分紅幾個小包進行發送,不一樣的數據庫,包的最適合大小是不一樣的.
(4).並非全部的批處理都能提升性能,這和不一樣的數據庫以及數據庫驅動決定的.
(5).Hibernate就是用了批處理技術,可是它進行了一些優化技術.
20.使用JDBC調用的存儲過程
(1).存儲過程常常用在之前的兩層結構中,如今的三層結構已經就用不到了
(2).CallableStatement(從PreparedStatement繼承來的)
java代碼:
CallableStatement cs=null;
String sql="{calladdUser(?,?,?,?)}";
cs=conn.prepareCall(sql);
//替換參數
cs.registerOutParameter(4,Types.INTEGER);
cs.setString(1,"ps name");
cs.setDate(2.new java.sql.Date(System.currentTimeMills()));
cs.setFloat(3,100f);
cs.executeUpdate();
int id = cs.getInt(4);
System.out.println(id);
存儲過程:
create procedure 'jdbc'.'addUser' (in pnamevarchar(45),in birthday date,in money float,out pid int)
//in:輸入參數,out:輸出參數
begin
insert intouser(name,birthday,money)values(pname,birthday,money);
select last_insert_id() into pid;
//last_insert_id()是一個函數,最後一次插入的id號
end $$
21.使用SimplejdbcTemplate和泛型技術簡化代碼
(1).
public class SimpleJdbcTemplateTest{
static SimpleJdbcTemplate simple = newSimpleJdbcTemplate(JdbcUtils.getDataSource());
static <T> T find(String nameClass<T> clazz){
String sql = "selectid,name,money,birthday from user where name=? and money=?";
User user =
simple.queryForObject(sql,ParameterizedBeanPropertyRowMapper.newInstance(User.class),name,100f);
}//使用了可變參數功能,沒有使用了參數數組,參數Map;使用泛型,將查詢的類型也當作是參數傳遞過來.
}
(2).它的內部也是包裝了NamedParameterJdbcOperations類,當將對象可變參數變成數組後,剩下的工做都交給NamedParameterJdbcOperations類作.
simple.getNamedParameterJdbcOperations()獲取NamedParameterJdbcOperations對象
simple.getJdbcOperations()獲取JdbcTemplate對象
22.使用策略模式對模板方法設計模式進行改進
(1).對於不一樣的查詢語句,返回的結果集可能不一樣,只要一個name,可是把全部的信息都查詢出來了,這就要求不一樣的映射結果.
public StringfindUserName(int id){
Stringsql="select name from user where id=?";
Object[]args=newObject[]{id};
}
protected ObjectrowMapper(ResultSet rs){//重新覆蓋rowMapper方法
returnrs.getString("name");
}
這種方式可能致使有多少條不一樣的查詢語句,就須要覆蓋多少次rowMapper方法.
(2).java中是不容許傳遞方法的,可是能夠傳遞一個類,接口
根據不一樣的sql中的內容,查詢的列不一樣,如:
select name fromuser:能夠獲得name一列
select id,namefrom user:能夠獲得id,name這兩列
selectid,name,money from user:能夠獲得id,name,money這三列.
public classMyDaoTemplate{
public Objectfind(String sql,Object[]args,RowMapper rowMapper){
obj =rowMapper.mapRow(rs);//映射的過程由一個接口去作
}
}
public interfaceRowMapper{//定義一個行映射器接口
public ObjectmapRow(ResultSet rs);
}
public classUserDaoImpl2{
MyDaoTemplate template= new MyDaoTemplate();
public UserfindUser(String loginName,String password){
Stringsql="select id,name,money,birthday from user where name=?";
Object[] args =new Object[]{loginName};
Object user =this.template.find(sql,args,new UserRowMapper());
retrun (User)user;
}
}
classUserRowMapper implements RowMapper{
public ObjectmapRow(ResultSet rs){//行映射器
User user = newUser();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setMoney(rs.getFloat("money"));
return user;
}
}
//當須要不一樣的查詢結果集,只需實現RowMapper接口就好了(能夠使用匿名內部方式實現)
(3).這是一種策略模式,根據不一樣的功能,調用不一樣的方法(策略),實現類組合的方式(在UserDaoImpl2類中定義一個MyDaoTemplate類)實現的,模板模式是根據繼承的方式實現的.
23.使用模板方法設計模式處理DAO中的查詢方法
publc abstractclass AbstractDao{
public Object find(String sql,Object[]args){//相同的部分在父類中實現
Connectionconn=null;
PreparedStatementps=null;
ResultSet rs=null;
conn=JdbcUtils.getConnection();
ps=conn.prepareStatement(sql);
for(inti=0;i<args.length;i++){
ps.setObject(i+1,args[i]);
}
rs=ps.executQuery();
Object obj-null;
while(rs.next()){
obj=rowMapper(rs);
}
return obj;
}
abstract protectedObject rowMapper(ResultSet rs);//父類中不知道具體的查詢結果集.放到子類實現該方法.
}
public classUserDaoImpl extends AbstractDao{
public UserfindUser(String loginName,String password){//不變的部分放到子類實現.
Stringsql="select id,name,money,birthday from user where name=?";
Object[] args =new Object[]{loginName};
Object user = super.find(sql,args);
return (User)user;
}
@Override
protected ObjectrowMapper(ResultSet rs){//在子類中知道查詢結果有幾列
User user=newUser();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setMoney(rs.getFloat("money"));
user.setBirthday(rs.getDate("birthday"));
return user;
}
}
假設如今有一個帳戶AccountDao
public classAccountDaoImpl extends AbstractDao{