1) 選擇工做空間 workspace 選擇一個文件夾存放程序(代碼) 不要用中文和空格javascript
2) 新建一個java 工程(Project)css
3) 建包 建類html
alt + / : 代碼自動補齊,須要配置的java
打開preferences(首選項), 搜keys,打開快捷鍵配置面板mysql
搜 alt+/ ,取消綁定程序員
搜 content assist ,取消原先的綁定,配置成alt+/web
ctrl+1: 錯誤自動修復, 注意,放行的紅叉是可修復的,圓形的是不可修復的面試
導包的三種方式:ajax
1)將類名敲完整,按下alt+/ 進行補齊,會自動導包算法
2) ctrl+1,修正錯誤
3) ctrl+shift+o 整理包,導入須要的包,去掉多餘的導包語句
ctrl+shift+f : 代碼格式化
ctrl+2,L 自動聲明變量
shift+enter 光標直接進入下一行
ctrl+alt+方向鍵(向上向下) 複製行
alt+ 方向鍵(向上向下) 移動當前行
1) 能夠對程序進行調試
在行的開頭雙擊左鍵打斷點,用debug方式來運行程序,程序就會停留在斷點位置
F5 跳入(step into) 跳入方法內部 F6 跳過, 讓當前行運行 F7跳出 返回上一層程序
resume 直接運行到下一個斷點
2) 查看源代碼建議使用斷點調試的方式,注意當前運行環境須要包含源碼
1) 寫一個java類, 聲明測試方式
修飾符爲 public void ,
在方法前面加註解,(@Test)
此方法就能夠進行測試了(交給junit運行)
2) 須要在測試方法前作初始化工做
寫一個靜態方法 public static void init()
在方法前加註解 @BeforeClass
3) 須要在測試後釋放資源
寫一個靜態方法
在方法前加註解 @AfterClass
1) 導入類的靜態屬性
import static java.lang.System.out;
out.println("haha");
2) 導入類的靜態方法
import static java.lang.Math.*; // 導入Math類的全部靜態成員
int num = abs(-10);
String [] arr = {"a", "b", "c"}; //數組的靜態定義方式,只試用於數組首次定義的時候
// 傳統方式
for(int i=0; i<arr.length; i++) {
// i依次表示數組的角標
String s = arr[i];
System.out.println(s);
}
System.out.println("-------------------------------------");
// 在jdk5中咱們可使用加強for循環迭代
// 加強for循環括號裏寫兩個參數,第一個是聲明一個變量,變量類型必須是數組元素的類型
// 第二個就是須要迭代的容器
// for循環會循環容器的length次, 每次都將容器的第n-1個元素賦值給聲明的變量
for(String s : arr) {
// 循環體, 執行arr.length
// 每次都將arr中的第n-1個元素給s
System.out.println(s); //
}
int --> Integer
byte --> Byte
short --> Short
long --> Long
char --> Character
double --> Double
float --> Float
boolean --> Boolean
1) Integer x = 1; x = x + 1; 經歷了什麼過程? 裝箱à 拆箱 à 裝箱
2) 爲了優化,虛擬機爲包裝類提供了緩衝池, Integer池的大小 -128~127 一個字節的大小
3) String池
Java爲了優化字符串操做 提供了一個緩衝池
面試題:
String s = 「abc」 和 String s = new String(「abc」) 的區別
String s = new String(「abc」) 建立了幾個對象
String s = 「a」 + 「b」 + 「c」 + 「d」 建立了幾個對象
String s1 = 「a」 String s2 = 「b」 String s3 = s1 + s2; s3==」ab」?
/*1. String s = "abc", 虛擬機首先會檢查String池裏有沒有"abc"對象(經過equals方法)
// 若是有,直接返回引用,若是沒有,會在池裏建立一個「abc」對象,並返回引用
String s1 = "abc";
String s2 = "abc";
System.out.println(s1==s2); // result: true
*/
/* 2. String str = new String("abc");
無論緩衝池是否有"abc", 都會在堆內存建立一個"abc"對象,返回引用
// 此時,負責檢查並維護緩衝池,其實堆內存的對象是緩衝池中"abc"對象的一個拷貝
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1==s2); // result: false
*/
/* 3. String s = "a" + "b" + "c" + "d"; java編譯器有個合併已知量的優化功能
// 在編譯階段就把"a" + "b" + "c" + "d" 合併爲 」abcd「
String s = "a" + "b" + "c" + "d";
// String s = "abcd";
System.out.println(s=="abcd");// result: true
*/
/* 4. String s1 = "a"; String s2 = "b"; String s3 = s1 + s2;
// String是常量,不能相加的,java如何實現的?
StringBuilder sb = new StringBuidler(s1);
sb.append(s2);
s3 = sb.toString();
也就是說實際上s3是方法返回的String對象
凡是方法返回的字符串對象都是在堆內存的
*/
String s1 = "a";
String s2 = "b";
String s3 = s1 + s2; // 堆內存的對象
System.out.println(s3=="ab");// result: false
List list = new ArrayList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
// 傳統方式1
/* 1.得到迭代器
Iterator iter = list.iterator();
// 2.循環判斷迭代器是否有下一個
while(iter.hasNext()) {
String str = (String) iter.next(); // 將迭代器的指針移向下一個,並將迭代當前指向的元素返回
System.out.println(str);
}
*/
// 傳統方式2
for(Iterator iter=list.iterator(); iter.hasNext(); ) {
String s = (String) iter.next();
System.out.println(s);
}
System.out.println("--------------------------------");
// 加強for循環, 沒有使用泛型的集合能不能使用加強for循環迭代?能
for(Object obj : list) {
String s = (String) obj;
System.out.println(s);
}
Map map = new HashMap();
map.put("a", "aaa");
map.put("b", "bbb");
map.put("c", "ccc");
// 傳統方式迭代1
// 1. 得到全部的key
Set keys = map.keySet();
// 2.迭代keys得到全部的key
Iterator iter = keys.iterator();
while(iter.hasNext()) {
String key = (String) iter.next(); // a b c
// 3.根據key得到對應的value
String value = (String) map.get(key);
System.out.println(key + "=" + value);
}
System.out.println("---------------------------------");
// 傳統方式2,必須掌握這種方式
// 1.得到全部的鍵值對Entry對象
Set entrys = map.entrySet();
// 2.迭代出全部的entry
iter = entrys.iterator();
while(iter.hasNext()) {
Map.Entry entry = (Entry) iter.next();
// 分別得到key和value
String key = (String) entry.getKey();
String value = (String) entry.getValue();
System.out.println(key + "=" + value);
}
System.out.println("-------------------------------------");
System.out.println("加強for循環迭代,");
// 加強for循環迭代,
// 原則上map集合是沒法使用加強for循環來迭代的,
// 由於加強for循環只能針對實現了Iterable接口的集合進行迭代
// Iterable是jdk5中新定義的接口,就一個方法iterator方法
// 只有實現了Iterable接口的類,才能保證必定有iterator方法
// java有這樣的限定是由於加強for循環內部仍是用迭代器實現的
// 而實際上,咱們能夠經過某種方式來使用加強for循環
for(Object obj : map.entrySet()) {
// obj 依次表示Entry
Map.Entry entry = (Entry) obj;
System.out.println(entry.getKey() + "=" + entry.getValue());
}
// 在使用迭代器迭代集合的過程當中,不能對集合進行增刪操做
@Test
public void test4() {
List list = new ArrayList();
list.add("wangwu");
list.add("zhangsan");
list.add("lisi");
Iterator iter = list.iterator();
while(iter.hasNext()) {
String name = (String) iter.next();
if("wangwu".equals(name)) {
// 從集合中刪掉
//list.remove(name);
// 迭代過程當中刪除元素須要調用迭代器的方法
iter.remove(); // 刪除我迭代的集合被我迭代的最後一個元素
}
}
// 1 2 4
System.out.println(list.size());
}
@Test
public void test5() {
List list = new ArrayList();
list.add("aa");
list.add("bb");
// 使用ListIterator迭代器
ListIterator listIterator = list.listIterator();
while(listIterator.hasNext()) {
listIterator.next();
// 迭代過程當中增長元素
listIterator.add("cc");
}
System.out.println(list.size());
}
//在使用加強for循環時,不能對元素進行賦值
int[] arr = {1,2,3};
for(int num : arr) {
num = 0;
}
System.out.println(arr[1]);
1) jdk5中方法的形參能夠定義爲可變參數,傳入實參個數可變
// 設計一個方法求n個數的和
public static int getSum(int... arr) {
// 可變參數在方法中仍被看作一個數組
int sum = 0;
for(int num : arr)
sum += num;
return sum;
}
2)Arrays.asList爲例演示傳入不一樣參數的狀況
// list長度爲3
List list = Arrays.asList("a","b","c");
// list長度爲1, 由於考慮1.4語法
String[] arr = {"a","b","c"};
List list = Arrays.asList(arr);
// 同時符合1.4和1.5的語法,此時會優先考慮1.4的語法
// 緣由是有了新功能要保證之前的代碼不出錯,向後兼容
// 如今就須要將arr做爲一個元素存入集合
Object obj = arr;
List list2 = Arrays.asList(obj); // 此時只符合1.5的語法,不符合1.4的語法,沒有歧義
List list3 = Arrays.asList(new Object[]{arr}); // 優先考慮1.4,因此數組會拆開
//System.out.println(list3.size());
// 基本數據類型數組只符合1.5的語法
int[] nums = {1,2,3};
list = Arrays.asList(nums);
System.out.println(list.size());
問題:對象的某個屬性的值不能是任意的,必須爲固定的一組取值其中的某一個
解決辦法:
1) 在setGrade方法中作判斷,不符合格式要求就拋出異常
2) 直接限定用戶的選擇,經過自定義類模擬枚舉的方式來限定用戶的輸入
寫一個Grade類,私有構造函數,對外提供5個靜態的常量表示類的實例
3) jdk5中新定義了枚舉類型,專門用於解決此類問題
4) 枚舉就是一個特殊的java類,能夠定義屬性、方法、構造函數、實現接口、繼承類
//枚舉類就是一個java類,也能夠聲明屬性,方法,構造函數
public enum Grade4 {
A("90-100"),B("80-89"),C("70-79"),D("60-69"),E("0-59");
private String value;
private Grade4(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
//枚舉類就是一個java類, 也能夠繼承抽象和實現接口
public enum Grade5 {
// 抽象類不能建立實例對象
A("90-100"){
// new了一個Grade5的子類實例
public String toLocaleString() {
return "優";
}
}
,B("80-89"){
// new了一個Grade5的子類實例
public String toLocaleString() {
return "良";
}
}
,C("70-79"){
// new了一個Grade5的子類實例
public String toLocaleString() {
return "中";
}
}
,D("60-69"){
// new了一個Grade5的子類實例
public String toLocaleString() {
return "差";
}
}
,E("0-59"){
// new了一個Grade5的子類實例
public String toLocaleString() {
return "不及格";
}
};
private String value;
private Grade5(String value) {
this.value = value;
}
public String getValue() {
return value;
}
// 對外提供一個方法,返回枚舉的本地信息
// 一個方法不知道如何實現,能夠定義爲抽象的
public abstract String toLocaleString();
}
l 練習:請編寫一個關於星期幾的枚舉WeekDay,要求:
Class對象封裝了一個java類中定義的成員變量、成員方法、構造方法、類名、包名等
得到class對象的三種方式和區別:
// 1. 根據給定的類名來得到 用於類加載
String classname = "cn.itcast.reflect.Person"; // 來自配置文件
Class clazz = Class.forName(classname); // 此對象表明Person.class
// 2. 若是拿到了對象,不知道是什麼類型 用於得到對象的類型
Object obj = new Person();
Class clazz1 = obj.getClass(); // 得到對象具體的類型
// 3. 若是是明確地得到某個類的Class對象 主要用於傳參
Class clazz2 = Person.class;
// 在java中全部的類型都會對應一個Class對象 int Integer
Class intClazz = int.class;
Class intarrClazz = int[].class;
Class voidClazz = void.class;
反射就是得到一個java類的各個組成部分
// 反射類的成員方法
Class clazz = Person.class;
Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
method.invoke();
// 反射類的構造函數
Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})
con.newInstance(params...)
// 反射類的屬性
Field field = clazz.getField(fieldName);
field.setAccessible(true);
field.setObject(value);
到底框架是什麼? 框架就是將開發中大量重複的代碼集中起來寫個通用的程序
框架就是用反射來實現的, 框架須要如今的類調用未來寫的類
框架是未來的程序員調用的,框架不能實現完整的功能,框架只是一些一些通用的代碼;
框架要依賴未來寫的類來運行.
如今寫的類要調用未來寫的類,咱們先針對接口進行調用,未來的類須要實現接口,那麼方法就固定了
可是未來寫的類的類名咱們沒法獲知,這時就須要調用者經過配置文件告訴框架具體的類名
1) 泛型是一種可變化的類型, 類型不肯定,須要調用者來指定
2) 用途:
一個類的多個成員方法用到的參數類型或返回值類型都是未知的類型,但又須要是同一個類型,就可將方法的
參數類型定義爲泛型,此泛型必須在類上先予以聲明才能在方法中使用
一個方法的多個參數和返回值須要是同一個類型,也能夠用泛型來解決,在方法返回值前面聲明泛型
泛型的細節:
1) 泛型到底表明什麼類型取決於調用者傳入的類型,若是沒傳,默認是Object類型
2) 使用帶泛型的類建立對象時, 等式兩邊指定的泛型必須一致
緣由: 編譯器檢查對象調用方法時只看變量,然而程序運行期間調用方法時就要考慮對象具體類型了
3) 等式兩邊能夠在任意一邊使用泛型 在另外一邊不使用 (考慮向後兼容)
3. 泛型的基本概念
以List<E>爲例:<>念着typeof 例, List<String> 就是 List typeof String
List<E>中的E稱爲類型參數變量 方法定義參數形式參數
List<Integer>中的Integer稱爲實際類型參數
整個List<E>稱爲泛型類型 GenericType
整個List<Integer>稱爲參數化的泛型類型
4. 泛型的使用
1)使用帶泛型的類時,在建立對象時能夠爲泛型指定實際類型參數,指定的具體類型至關於給泛型傳參
2)子類在繼承父類的時候,能夠爲父類定義的泛型指定實際類型參數
class B<T>
class A extends B<String>
經過子類A得到的父類類型就是一個參數化的類型
3)調用方法時傳入參數的具體類型將做爲方法中泛型的實際類型
Lesson 2 Servlet(超級重要)
用 java 語言開發動態的web資源,接下來就是介紹如何開發動態的web資源
對於java程序員而言,所謂動態web資源就是à能夠運行在服務器上的java程序
開發人員寫好一個java類,到底有哪些方法tomcat服務器是不可能知道的
tomcat服務器欲執行咱們編寫的java類, 就須要知道咱們的java類有哪些方法,而後在適當的時間調用這些方法, 因此咱們在寫的java程序要想運行在服務器上,就必需要實現一個特殊的接口 Servlet.java
interface Servlet { ... }
Servlet接口中就定義了能夠被tomcat服務器調用的java方法
一般來說,咱們將實現了Servlet接口的java類稱之爲 Servlet
編寫好的Servlet須要在web.xml文件中作配置, 才能供外界訪問
3.1 寫一個java類實現Servlet接口
package cn.itcast.servlet;
import java.io.*;
import javax.servlet.*;
public class HelloWorldServlet extends GenericServlet
{
// 實現 service 方法
public void service(ServletRequest request,ServletResponse response)
throws ServletException,java.io.IOException {
// 向瀏覽器輸出一句話
PrintWriter out = response.getWriter();
out.write("hello world!!!");
}
public void init()throws ServletException {
// 初始化 servlet 時被調用
System.out.println("init()");
}
public void destroy() {
// 摧毀 servlet 時被調用
System.out.println("destroy()");
}
}
3.2. 導入 servlet jar包
set classpath=%classpath%;D:\apache-tomcat-6.0.20\lib\servlet-api.jar
3.3. 編譯帶包的類
javac -d . HelloWorldServlet.java
3.4. 將包拷貝至 day05/WEB-INF/classes 目錄下 --> 發佈 web 工程
3.5. 在 web.xml 文件中作映射
<!-- 作servlet映射 -->
<!-- servlet元素用於給一個類起別名 -->
<servlet>
<servlet-name>HelloWorldServlet</servlet-name>
<servlet-class>cn.itcast.servlet.HelloWorldServlet</servlet-class>
</servlet>
<!-- servlet-mapping元素用於將一個Servlet映射到url -->
<!—url必須以/開頭,/ 表示當前web應用即上下文路徑 -->
<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/HelloWorldServlet</url-pattern>
</servlet-mapping>
注意: servlet 對象一旦建立就會駐留在內存中,爲全部的請求服務,
servlet 對象何時銷燬? 直到服務器關閉時或web應用被移除才銷燬
3.6. Servlet 執行流程圖
4.1. 建一個 web project
4.2. 在src下建包,建立一個java類實現Servlet接口
4.3 在 Webroot\WEB-INF\web.xml 作 servlet 映射
4.4 配置 tomcat 服務器
window--> preferences --> tomcat6.x
4.5 將web工程發佈至tomcat 服務器
發佈的web應用名稱能夠配置: web工程右鍵 選properties-->myeclipse-->web
默認狀況使用工程名做爲發佈後的web應用名
4.6 啓動tomcat服務器運行程序
通常來說咱們開發一個Servlet會去繼承 HttpServlet
在 eclipse 下開發Servlet 能夠直接新建一個Servlet, 覆寫 HttpServlet 的 doGet和doPost方法
繼承 HttpServlet 的緣由是: HttpServlet實現了service方法,將ServletRequst和ServletResponse
強轉爲子類 HttpServletRequest和HttpServletResponse,讓咱們用起來更加方便,同時,在service方法中,它判斷了請求方式,根據請求方式來調用 doGet 和 doPost
1. * 號統配符
一個Servlet能夠映射爲多個路徑
在映射 Servlet 路徑時可使用‘/*’ 或 ‘*.擴展名’ 的形式
注意: 二者不能同時使用
/* 具備較高的優先級
2. load-on-startup 元素
<servlet>元素下能夠配置< load-on-startup>子元素,
配置方式以下:
<load-on-startup>1</load-on-startup>
若是一個Servlet配置了該項,web容器會在web應用被加載時就初始化該Servlet,數字越小則越先初始化
3. tomcat\conf\web.xml
服務器下全部web 應用中的web.xml 都會自動繼承該文件中全部的配置
http://localhost:8080/day05/a.html a.html是資源名
上面的url訪問的url在web.xml文件中並無配置
此時會去訪問缺省的Servlet,在tomcat\conf\web.xml文件中就配置了一個缺省的DefaultServlet
DefaultServlet幫咱們去web應用下讀取 a.html 文件,並打給瀏覽器,若是沒有發送 404 頁面
也就說,咱們經過ie訪問服務器訪問的都是 Servlet
4. Servlet線程安全問題
解決Servlet線程安全問題: 加上同步的鎖(lock)
實現SingleThreadModel接口的Servlet
服務器會作判斷,當有請求過來,若是Servlet對象忙着呢,服務器會再建立一個Servlet對象爲用戶
提供服務,若是Servlet閒置,就直接提供服務
這樣的方式其實是迴避了線程安全問題, 單線程訪問Servlet, 這樣的方式不可取
Lesson 3
1、 WEB 服務器
WEB在英語中即表示網頁的意思,它用於表示Internet主機上供外界訪問的資源以及超連接所組成的鏈表
放在internet網上供外界訪問的文件或程序被稱爲web資源
web資源被分爲:
靜態web資源: html、css、jpg
動態web資源:Servlet、Jsp
思考問題: 從一臺計算機的 IE 瀏覽器如何去訪問另外一臺計算機中的文件
3.1 兩臺計算機是如何實現通信的?
IP地址(計算機的惟一標識)
IPV4 4個字節的整數,每一個字節以 點號 隔開 192.168.1.100 每一個字節的取值 0~255
在計算機中程序會綁定在某一個端口 0~65535 儘可能用 1024 以上的
鏈接一臺計算機就須要輸入 ip 地址和端口號
做爲接收方, 應該綁定ip,監聽指定的端口
3.2 在本地寫程序添加一個服務,供別人來訪問, 假設監聽 8888 端口
3.3 編碼實現了一個本地服務器程序
做用: 管理本地的資源,只要將html頁面放到指定的目錄下,外界就能夠訪問了
3.4 安裝服務器的目的: 開發好的web資源能夠發佈到服務器上,這樣外界就能夠經過瀏覽器訪問了
源程序: MyServer.java
// ServerSocket 對象能夠監聽端口
ServerSocket serversocket = new ServerSocket(6666);
while(true) {
Socket socket = serversocket.accept(); // 等待客戶端的鏈接請求,一旦有請求過來,就結束阻塞,返回客戶端對象
// 一旦有客戶來訪問, 就另開一個新線程去提供服務, main線程繼續等待下一個客戶的鏈接
new Thread(new MyService(socket)).start();
}
MyService.java
// 提供服務
InputStream in = socket.getInputStream();
Thread.sleep(200);
int len = in.available(); // 估計此流不受阻塞能讀取的字節數
byte[] buffer = new byte[len];
in.read(buffer);
String request = new String(buffer);
// 截取第一行
String firstLine = request.substring(0, request.indexOf("\n"));
String uriName = firstLine.split(" ")[1];
OutputStream out = socket.getOutputStream();
// 根據須要訪問的資源建立 File 對象
File file = new File("src" + uriName);
if(!file.exists()) {
out.write("對不起!您訪問的資源不存在!別瞎搞!!".getBytes());
out.close();
return ;
}
// 從文件讀, 往瀏覽器寫
FileInputStream fis = new FileInputStream(file);
buffer = new byte[1024];
while ((len = fis.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
socket.close();
1. 使用 tomcat6.0.20.rar 文件解壓即完成安裝
2. tomcat 就是一個java程序,必定會用到 jre
因此須要配置環境變量 java_home 配置成jdk的安裝目錄 c:\jdk1.6
tomcat的啓動是經過 startup.bat 文件, 實際上 startup.bat 中是去調用
catalina.bat 文件, 並且是經過 %catalina_home%\bin\catalina.bat 去找
因此爲了保證服務器啓動正常, 須要配置 catalina_home 環境變量爲 tomcat的安裝目錄
3. tomcat 的目錄結構
bin : 存放一些執行文件
conf : 存放服務器的配置文件
lib : 存放tomcat 所依賴的 jar 文件
logs: 存放日誌文件
temp: 存放臨時文件
webapps: web applications 存放全部的web應用程序(web資源)
work: tomcat 的工做目錄, jsp翻譯成的Servlet就在這個目錄下
4. web應用
多個web資源存放在一個目錄下即爲一個web應用(web應用程序、web工程)
web 應用的目錄結構
靜態web資源直接放在目錄下
java 類放在classes目錄下
web.xml 文件負責管理web應用下全部的web資源
全部jar包放在lib目錄下
一個web應用(服務器上一個目錄) 須要供外界訪問的路徑,須要映射虛擬目錄
在 tomcat6 中,放在webapps下的web應用,服務器會自動作映射(將文件夾名稱做爲虛擬路徑)
對於 webapps 目錄外的web應用須要手動映射虛擬路徑
1.1. 在 server.xml 文件能夠配置
<host>
<Context path=」/itcast」 docBase=」f:\itcast」 />
</host>
1.2. 在 %tomcat目錄%\conf\catalina\localhost 下寫一個 xml文件
文件名就是 虛擬目錄
<Context docBase=」f:\itcast」 />
多級目錄配置 aaa#bbb.xml 對應 /aaa/bbb
若是文件名 是 ROOT.xml 那就是配置了缺省的web應用, 訪問時不須要輸入 虛擬目錄
Web.xml的做用: 管理 web 應用下全部的web資源
通俗地講,一個web應用下全部的web資源如何被外界訪問都須要在此文件下作映射
包括我們後面學的Servlet jsp 都須要在這個文件中作映射
實驗: 配置web應用的 首頁
在 web 應用下新建目錄 WEB-INF ,在此目錄下 新建 web.xml 文件
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<welcome-file-list>
<welcome-file>itcast.html</welcome-file>
</welcome-file-list>
</web-app>
一個完整 url
http:\\www.sina.com:80\itcast\index.jsp
協議名 主機名 端口號 資源名 (uri)
IE 訪問服務器的原理,
在IE中輸入的地址中包含域名,域名就須要被翻譯成服務器的IP,才能訪問到服務器
新建一個 web 應用
配置成缺省的web應用
配置首頁
配置tomcat監聽端口爲80
在 windows 中註冊主機名
服務器容許在一個IP上配置多個主機,即虛擬主機
http://www.sina.com:80/index.html
中的 www.sina.com 起了兩個做用:
找DNS服務器,做爲域名被解析爲IP
經過Host頭告訴服務器訪問的主機名
配置方式: 在 server.xml 文件中配置Host元素,須要指定name(主機名)、appBase(默認web應用存放目錄)
appBase目錄下全部的web應用,tomcat會自動映射虛擬路徑
<Host name="www.sohu.com" appBase="F:\sohu\webapps" />
作實驗: 給新配置的虛擬主機配置缺省的web應用
<Host name="www.sohu.com" appBase="F:\sohu\webapps" >
<Context path="" docBase="F:\sohu\webapps\abc" />
</Host>
http://www.sohu.com/abc/a.html
訪問一個 a.html 靜態web資源, IE 作了什麼事
1) 將 www.sohu.com 做爲域名發送給DNS , 解析成 IP 地址, 訪問一臺服務器
2) 發送 Host 頭(www.sohu.com),告訴服務器我要訪問的虛擬主機 ,服務器拿着Host頭找匹配的Host元素
3) 將abc做爲虛擬目錄,告訴服務器我要訪問的web應用 ,服務器拿着 abc 找匹配的web應用
4) 將 a.html 做爲資源名, 告訴服務器我要訪問的 web 資源, 服務器拿着 a.html 去web.xml文件中找映射
虛擬目錄對應是一個web應用的目錄,因此虛擬目錄也被咱們稱做web應用路徑(web應用的上下文contextpath)
1. tcp/ip 協議: 網絡通訊協議(鏈接)
ip 協議 : 127.0.0.1 ip地址對應一臺計算機 (互聯網層)
tcp 高級協議: 三次握手, 發送請求、返回響應、傳輸數據 (傳輸層)
2. http 協議是創建在 tcp協議的基礎之上 (應用層)
3. Http協議的版本 (w3c)
Http1.0 : 創建鏈接,發送一次請求就斷開
Http1.1 :創建鏈接,能夠無限次發送請求
http請求消息內容: 包括一個請求行、若干消息頭、以及實體內容,其中的一些消息頭和實體內容都是可選的,消息頭和實體內容之間要用空行隔開。
請求行
POST /itcast/ HTTP/1.1
消息頭
Accept: image/gif, image/x-xbitmap, */*
Referer: http://localhost:8080/itcast/
Accept-Language: zh-CN,en-GB;q=0.8,ar-YE;q=0.7,ja-JP;q=0.5,de-CH;q=0.3,en-US;q=0.2
Content-Type: application/x-www-form-urlencoded
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB6.5; CIBA)
Host: localhost:8080
Content-Length: 33
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=B0B3FB4FFB0315B3D3C620548DD4E1EB
空一行
消息體
username=zhangsan&password=123456
1. 請求行 GET /itcast/a.html HTTP/1.1
GET 爲請求方式 : get方式的請求參數直接跟在url後面,例如:/itcast/a.html?username=aaa&password=111
POST方式: post方式請求參數放在消息體中傳輸,相對安全,get大小限制1K,post 無數據量限制
2. 請求消息頭 : IE瀏覽器用於向服務器說明狀況的(瀏覽器使用環境)
Accept: text/html,image/* 說明瀏覽器接受的數據類型
Accept-Charset: ISO-8859-1 說明瀏覽器使用的字符編碼
Accept-Encoding: gzip,compress 說明瀏覽器支持的壓縮格式
Accept-Language: en-us,zh-cn 說明瀏覽器的語言環境
Host: www.it315.org:80 說明瀏覽器要訪問的主機名
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT 文件的修改事件,用於作緩存
Referer: http://www.it315.org/index.jsp 說明請求來自哪裏,防盜鏈 (作實驗)
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) 說明瀏覽器內核
Cookie 向服務器發送Cookie
Connection: close/Keep-Alive 說明鏈接狀態
Date: Tue, 11 Jul 2000 18:23:51 GMT 客戶端計算機時間
3. 實體內容(消息體)
瀏覽器向服務器發送的數據,例如上傳的文件、提交的表單等
http響應消息的內容包括: 一個狀態行(類比 http請求信息的」請求行」)、若干消息頭、以及實體內容 ,其中的一些消息頭和實體內容都是可選的,消息頭和實體內容之間要用空行隔開。
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=GB18030
Content-Length: 766
Date: Thu, 07 Jul 2011 15:40:02 GMT
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
hello
</body>
</html>
1. 狀態行 HTTP/1.1 404 Not Found
協議版本: 目前廣泛採用的都是http1.1
響應狀態碼: 說明服務器的響應狀態
經常使用狀態碼
狀態碼 |
含義 |
100~199 |
表示成功接收請求,要求客戶端繼續提交下一次請求才能完成整個處理過程 |
200~299 |
表示成功接收請求並已完成整個處理過程,經常使用200 |
300~399 |
爲完成請求,客戶需進一步細化請求。例如,請求的資源已經移動一個新地址,經常使用30二、307和304 |
400~499 |
客戶端的請求有錯誤,經常使用404 |
500~599 |
服務器端出現錯誤,經常使用 500 |
200: 一切OK
302\307 請求重定向,你訪問我,我通知你訪問另外一個資源
304 通知瀏覽器去讀緩存
404 找不到資源 ,客戶端的請求有錯誤
500 服務器程序出錯(服務器端的程序拋異常了)
2. 響應消息頭 服務器向瀏覽器說明狀況(處理結果)
Location: http://www.it315.org/index.jsp 通知瀏覽器去訪問另外一個資源
Server:apache tomcat 說明服務器
Content-Encoding: gzip 通知瀏覽器數據的壓縮格式
Content-Length: 80 通知瀏覽器發送數據的長度
Content-Language: zh-cn 通知瀏覽器語言環境
Content-Type: text/html; charset=GB2312 通知瀏覽器文件的格式和編碼
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT 告訴瀏覽器文件的修改時間
Refresh: 1;url=http://www.it315.org 通知瀏覽器自動刷新
Content-Disposition: attachment; filename=aaa.zip 通知瀏覽器如下載的方式打開資源
Set-Cookie:SS=Q0=5Lb_nQ; path=/search 發cookie
Expires: -1//3種禁止緩存的頭字段
Cache-Control: no-cache
Pragma: no-cache
Connection: close/Keep-Alive 鏈接狀態
Date: Tue, 11 Jul 2000 18:23:51 GMT 系統時間
3. 實體內容(響應消息體)
通常爲服務器發送給ie瀏覽器的頁面數據
1. https 是一種加密協議 能保證數據的安全
2. 不對稱加密 對稱加密
3. https
1) 製做數字證書
keytool -genkey -alias tomcat -keyalg RSA
2) 將證書拷貝至 tomcat\conf
3) 修改server.xml 文件 配置https 鏈接器
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="conf\.keystore" keystorePass="123456" />
4) 重啓服務器 打開ie訪問
https://localhost:8443
5) ie 中安裝證書
繼續瀏覽
查看證書
安裝證書
刪除證書:
Lesson 5
做用 : 封裝 Servlet 初始化參數
1. 能夠在 web.xml 文件中 Servlet 元素下 爲Servlet配置初始化參數
<init-param>
<param-name>name</param-name>
<param-value>aaaa</param-value>
</init-param>
2. web 容器在初始化Servlet時,會將初始化參數封裝到一個 ServletConfig 對象中,傳給init方法
3. 咱們在Servlet 中覆寫 init方法,就能夠得到ServletConfig
4. 父類 GenericServlet 中定義了一個成員變量用於記住此對象,並提供了 getServletConfig 方法
咱們能夠直接調用此方法 得到 config對象
5. 再調用 getInitParameter(name) 方法得到想要配置項
// 指定編碼
// 得到ServletConfig 對象
ServletConfig config = getServletConfig();
String encoding = config.getInitParameter("encoding");
System.out.println("encoding=" + encoding);
1. ServletContext對象表明整個web應用
2. ServletContext對象是一個「域」對象(即:能夠存儲數據的對象)
ServletContext對象的內部維護了一個map集合, key是String類型 value是Object類型
class ServletContext {
private Map<String, Object> map ;
}
3. ServletContext 做爲域對象, 多個Servlet 能夠共享數據
Servlet6
// 1. 得到ServletContext 對象
ServletContext context = getServletContext();
// 2. 存入域
context.setAttribute(「name」, 「zhangsan」);
Servlet7
// 得到 context 域, getAttribute
String name = (String) getServletContext().getAttribute("name");
4.獲取web應用的初始化參數
getContext().getInitParameter(「name」);
5. 統計一個web應用的訪問量
在 context 域中維護一個count變量
訪問Servlet時,取出變量 加1
6. 實現請求轉發
實現請求轉發須要用到 轉發對象 RequestDispatcher,RequestDispatcher有一個 forward 方法能轉發請求
7.1. 讀取web工程下的資源文件
// 得到絕對路徑
String realPath = ServletContext.getRealPath(相對web應用的路徑);
注意URL url = ServletContext.getResource(); web的url
// 得到與文件關聯的流
InputStream in=
ServletContext.getResourceAsStream(「WEB-INF/classes/config.properties」;
7.2 讀取java工程下的文件
// 不能相對虛擬機目錄 不能用絕對路徑
// 只能類加載的方式讀
// 得到 流
ClassLoader classLoader = Demo.class.getClassLoader();
InputStream in = classLoader.getResourceAsStream("a.txt");
// 得到絕對路徑
URL url = Demo.class.getClassLoader().getResource("a.txt");
String path = url.getPath();
類加載方式 缺點
1) 不能讀取類路徑之外文件
2) 因爲須要加載到內存,不能讀大文件
3) web工程中若是用類加載的方式讀
類加載實際上讀取的是內存中加載的文件,此時將讀不到硬盤上資源文件的修改
解決辦法: 經過絕對路徑去讀硬盤上的文件 避開內存的文件
例如: Demo.Class.getClassLoader().getResource("a.txt").getPath()
HttpServlet 的 Service()方法中的代碼
// 調用方法
long lastModified = getLastModified(req);
// 若是爲 -1 ,就直接放行,給最新的
if (lastModified == -1) {
doGet(req, resp);
}
// 方法返回不是-1
else {
// 讀取IE發送的頭If-Modified-Since
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
// 拿着客戶端的時間頭和方法的返回值比較
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
// 方法的返回值大於ie發送過來的時間頭
// 從新向瀏覽器發送了一個時間頭
maybeSetLastModified(resp, lastModified);
// 放行, 發送頁面
doGet(req, resp);
} else {
// 方法的返回值沒有大於ie發送過來的時間頭
// 發送 304 狀態碼,讓用戶去讀緩存
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
Lesson 6
1) 在計算機中數據以二進制的形式進行存儲的, 數據的傳輸也是通二進制的形式
2)須要存字符,會出現字符與字節之間的轉換 (輸入字符 讀到的字符)
3) 字符和字節之間如何實現轉換? 都是經過查碼錶
4) 字符到字節是編碼,字節到字符是解碼, 編碼和解碼用到了不一樣碼錶就會出現亂碼問題
1) 拿到亂碼基本上都是因爲解碼錯誤致使的, 解決辦法,從新編碼再解碼
2) 之後將文件交給解析器,出現亂碼,首先想到是否通知別人文件的編碼格式
Servlet對象 一旦建立就駐留在內存,
Request和response對象,web容器會針對每次用戶的請求建立一個request和response
請求結束,響應發給ie。 Request和response對象就會當即被銷燬
1. response getWriter方法得到字符流,用於向瀏覽器輸出字符數據
文件名中文亂碼問題
因爲文件名是跟着 content-disposition 頭髮送給瀏覽器的
凡是http頭的數據都會通過 url 編碼, 編成全世界通用的符號,這樣傳輸過程當中纔不會亂碼
發送 302 狀態碼和 location 頭
ie向瀏覽器發送了兩次請求 第一次直接請求資源,第二次重定向的頁面
地址欄是有變化
4. getWriter 和 getOutputStream 不能同時使用,有衝突
response的流咱們不用關閉,交給web容器去管理
1. request 幫助咱們取獲取 ie 瀏覽器發送給 服務器的請求消息 (請求行 消息頭 消息體)
2. request 得到請求參數: getParameter() 和 getParameterValues()
Request 中文參數亂碼的解決方法:
1.表單提交post方式下 : request.setCharacterEncoding(「utf-8」); //指定處理消息體的解碼方式
2.表單提交get方式下 :
1) 手動解決亂碼問題
byte[] bytes = username.getBytes("iso-8859-1");
String str = new String(bytes, "utf-8");
2) 在 %tomcat%\conf\server.xml 中找到當前鏈接器元素 Connector
配置URIEncoding 屬性 它用於指定web容器的url解碼方式
3) 配置useBodyEncodingForURI 屬性 指定爲 true
用於讓tomcat按照處理消息體的編碼方式處理消息頭
超連接提交參數:也是使用 get 方式提交,(和表單的區別是,表單提交,ie會自動進行url編碼)
而超連接提交,ie不進行url編碼,此時咱們須要將超連接中的中文參數手動進行url編碼.
同理: 在服務器端的處理方式和 表單 get 方式的處理同樣
Servlet負責處理用戶的請求,處理完的結果交給jsp來作顯示(在Servlet中不輸出任何數據,全部數據的顯示都交給jsp)
得到RequestDispatcher對象作請求轉發,
1.請求轉發時, 須要將處理結果存入request域 帶給 jsp
2.請求轉發時, web容器(如:tomcat)會清空response中的數據
3.轉發後, 沒法向response輸入數據
面試題: 請求轉發forward和包含include的區別
Forward的時候,web容器會清空response的數據,而且轉發以後,當前Servlet就沒法再寫入數據了
Include的時候,web容器會讓兩個Servlet的數據都寫入response
1) 用戶發送請求(提交表單,超連接)
2) Servlet 作處理, 將數據(響應結果)封裝到 javabean對象
3) 將 javabean 對象存入request域
4) 請求轉發給 jsp
5) jsp 頁面中 從request域取出數據 作顯示
Web資源訪問的流程
1. 客戶端(IE)發送請求給服務器
2. 用一個 Servlet 來響應
客戶的需求基本上分爲兩種:
1) 查看數據
請求轉發給jsp作顯示
2) 用戶登錄\購買商品 完成某一個特定的功能 結果都是成功或失敗
請求重定向到一個目標資源
面試題: 請求轉發和請求重定向的區別?
1) 請求重定向地址欄有變化 請求轉發地址欄無變化
2) 請求重定向客戶端向服務器發送兩次請求 請求轉發發送一次請求
3) 應用場景:
一件事情作完了,應該作第二件事情了,就請求重定向 (用戶登錄、購物)
Servlet處理完了,讓jsp作顯示,用請求轉發(mvc設計模式)
讀取web中的文件 用 context 讀取,嚴禁用絕對路徑,統統以」/」開頭
原則: 就看路徑是給瀏覽器用的仍是給服務器用的,
給瀏覽器用的,要加上web應用的名稱
給服務器用的,斜線」/」即表示:當前web應用(/表示web應用,不用加web應用的名稱)
各類場景:
1) 請求轉發: 地址是給服務器用的: /表示web應用,不用加web應用的名稱 /a.jsp
web.xml文件中用到了路徑: 地址是給服務器用的, 不須要加web應用 /b.jsp /servlet/Servlet1
2) 請求重定向: 路徑給瀏覽器用的,因此須要加上web應用名稱 /day06/a.jsp
3) 超連接: 路徑給瀏覽器用的,因此須要加上web應用名稱 /day06/a.jsp
4) 表單的action提交: 路徑給瀏覽器用的,/day06/a.jsp
5) img標籤的src屬性: 路徑給瀏覽器用的,/day06/a.jpg
6) script src: 路徑給瀏覽器用的 /day06/js/a.js
記住:路徑 以「/」開頭,請求轉發不加web應用名,其餘的所有都要加
Lesson 7
Cookie cookie = new Cookie(key, value);
Response.addCookie(cookie);
// 得到用戶發送的全部Cookie
Cookie[] cookies = request.getCookies(); // 若是沒發Cookie過來,爲null
// 遍歷 找到lastAccessTime
String lastAccessTime = null;
for(int i=0; cookies!=null&&i<cookies.length; i++) {
String name = cookies[i].getName(); // 一個Cookie的name
if("lastAccessTime".equals(name)) {
// 找到了, 幾下value
lastAccessTime = cookies[i].getValue();
}
}
cookie默認狀況下在當前瀏覽器進程有效,若是想讓cookie長期駐留客戶機的硬盤,就須要指定時間
cookie.setMaxAge(time) time以秒爲單位
setMaxAge(0) 通知瀏覽器刪除該Cookie
兩個頁面
顯示全部的商品(從數據庫找出商品顯示),顯示最近的記錄(讀取客戶發送的Cookie中存的id,找出書顯示)
CookieDemo2
顯示商品的詳細信息,並向客戶端發送Cookie,難點若是產生新的Cookie
將客戶端帶過來的Cookie分割後存入集合,根據不一樣的狀況將新的id加進去,迭代集合用分隔符串起來
CookieDemo3
request對象有個方法是getSession()
首先看瀏覽器是否發送了Cookie (JSESSIONID) ,若是發了,拿着id去內存中找對應的session對象返回;
若是沒有發Cookie或者沒找到對應的Session對象,建立一個新的Session對象
getSession( boolean create)
create 爲true的時候,若是內存有session就返回,沒有就建立新的
create爲false的時候,意味着只查找不建立,有就返回,沒有不建立,返回null;
若是想讓多個瀏覽器共享一個session,咱們就須要人工發送Cookie,並設置Cookie的有效時間
3.若是瀏覽器禁用Cookie,那麼Session也玩不起來了
要想讓session好用,就須要在頁面跳轉時發送sessionid
有一個技術url重寫:
重寫後的地址會在原有的url地址的基礎上加上JSESSIONID
超連接或表單提交的地址,
重寫方式以下:String newUrl = response.encodeURL(url);
請求重定向的地址,
重寫方式以下:String newUrl = response.encodeRedirectURL(url);
簡單購物車的實現 ListServlet BuyServlet ListCartServlet
用戶登陸 Login.jsp LoginServlet indes.jsp LogoutServlet
防止表單重複提交(2種方法)
1.能夠用js來作,可是隻能增長用戶的體驗,不能徹底防止壞人
2.用session作防表單重複提交
FormServlet HandleFormServlet TokenProcessor
html: 靜態web資源,DefaultServlet讀取html文件,經過response輸出給IE瀏覽器
Servlet:動態web資源,web容器(Servlet引擎)解析web.xml文件,找到url對應的java類
經過反射建立Servlet對象,調用service方法
Class.forName(「cn.itcast.servlet.Servlet1」).newInstance();
jsp: 動態web資源
jsp頁面的執行過程: (重要)
1.jsp頁面在第一次被訪問的時候,web容器(jsp引擎)會將jsp翻譯成一個Servlet,而後調用servlet的service方法
2.jsp翻譯後的Servlet會被放到 %tomcat安裝目錄%\work\Catalina\localhost\webcontext
3.當jsp頁面被再次訪問的時候,web容器會去直接調用Servlet的service()方法,因此一般來說jsp只是在第一次被訪問的時候比較慢
4.若是jsp頁面作了修改,此時web容器會從新翻譯jsp
實際上jsp就是Servlet,只是提供了一種比較直觀的書寫方式,由於寫jsp就像在寫Html
jsp中能夠寫 java 代碼, 有兩種jsp中寫java代碼的方法:
1) jsp 腳本表達式
內容會被放到 out.print()裏面,輸出給瀏覽器
格式: <%=new Date() %>
2) jsp腳本片斷
內容會原封不動地被翻譯到 Servlet 的service方法中
<%
// java 代碼
%>
1) Servlet 適合寫java代碼,由於Servlet就是一個java類
在開發中使用Servlet對用戶發送的請求進行處理並作出響應
2) jsp 適合作數據美化,做爲 數據顯示模板
由於jsp頁面直接書寫HTML標籤
3) 項目中的web層一般使用mvc設計模式 Servlet+jsp+javabean
其中, Servlet作控制器,處理用戶請求
jsp做爲顯示模板
javabean 做爲封裝數據的實體
4) 如何養成一種良好的編碼風格
在Servlet中應避免作任何的數據輸出
在 jsp 中應避免去直接書寫java代碼, 而實際上要作到這點很難, 因此須要用到 el 和 jstl
在 jsp 頁面中全部的 html 標籤部分被稱做模板元素,用於對整個網頁進行佈局
jsp腳本有三種形式
1) 腳本表達式: 被翻譯到 out.print() 方法中
<%=new Date() %>
2) 腳本片斷: 被翻譯到 service()方法中
<%
for(int i=0; i<10; i++) {
System.out.println(i);
}
%>
3) jsp 聲明 : 被翻譯到 service()方法外, 用於:寫成員變量\成員方法\靜態代碼塊
<%!
private String name;
static {
.......
}
%>
Include指令: 用於包含一個頁面
taglib指令: 用於引入標籤庫文件
page指令: 用於向jsp引擎說明jsp的頁面狀況
page指令: 通常都放在頁面的開頭,可是無論放在哪都對整個頁面起做用
page指令: 經常使用的主要有一下幾個:
1) import 導包, 導入多個包時須要以「,」隔開 也能夠分做多條指令書寫
2) session 說明是否使用session
默認值爲true,被翻譯的Serlvet中會自動得到Session
若是將該值指定爲false 被翻譯的Servlet的service方法中將不獲取session
3) errorPage 指定錯誤跳轉的頁面
在 web.xml 文件中一樣能夠配置錯誤頁面
可根據異常類型或錯誤編碼進行配置error-page
4) pageEncoding
1.通知jsp引擎在翻譯jsp的過程當中以什麼編碼方式來解碼jsp文件
2.通知 Servlet引擎 response編碼方式,至關於 response.setContentType()
說白了-à 只要給jsp指定了 pageEncoding=」utf-8」, jsp引擎在翻譯jsp時, 就會自動加上一句
response.setContentType(「text/html;charset=utf-8」)
擴展 : jsp 亂碼問題(only in tomcat5)
在 tomcat6 之後jsp就沒有亂碼問題了, 若是是使用tomcat5 纔會出現亂碼問題
jsp 亂碼解決 à告訴jsp引擎jsp頁面是什麼碼,這樣翻譯纔不會錯;告訴response用什麼碼編碼再發給瀏覽器
對象變量名 ( 可直接在jsp中使用 ) |
對象類型 |
config |
ServletConfig |
application |
ServletContext |
response |
HttpServletResponse |
request |
HttpServletRequest |
session |
HttpSession |
out |
JspWriter |
page |
this |
exception |
Throwable(不是每一個jsp都有) |
pageContext |
PageContext |
JspWriter類型, 帶緩衝的字符流 (包裝流) BufferedWriter
對response.getWriter() 進行了包裝,提供了緩衝區,默認大小8KB
寫入該流的數據最終會被刷新到 response , 調用response.getWriter().write() 方法
Question: 什麼狀況下 JspWriter 會將數據刷新?
1) 緩衝區寫滿(默認大小爲8kb, 能夠在page指令中經過 buffer屬性設置緩衝區大小)
2) jsp 頁面結束(此流會被關閉,數據刷新到底層的流)
注意:
1) jsp中輸出數據儘可能使用 out, 不要使用response直接得到流輸出
緣由在於: 寫入 out 的數據會先進緩衝區,再刷新到response; 若是兩個都用,會致使後寫的數據顯示在前面
2) jsp 頁面結束時, 會自動調用 response.getWriter() 將數據刷新
因此在jsp中不要調用 getOutputStream(), 固然也不方便作文件下載
結論: 在jsp中用out輸出數據
主要功能: 用於得到其餘8大隱式對象
這樣作的意義:
欲移除jsp中的java代碼,就須要將java代碼寫到一個java類的成員方法中,而後想辦法在jsp
頁面中調用該方法,以達到代碼複用的目的.
因爲jsp中的java代碼不免會訪問8個隱式對象,由於這些對象都是和web開發相關的對象,因此要移除這部分java代碼就須要」將8個對象傳遞給java類」的方法,
爲了方便,咱們一般的作法是:只傳遞一個pageContext對象過去,這樣在方法中就能夠經過該對象很輕鬆地得到其餘8個對象了
pageContext 也是一個域對象,但只在當前jsp頁面有效
重點:
1) 默寫9個對象, (具體描述9個對象怎麼用)
2) 理解pageContext對象的意義 (得到其餘8個對象)
Btw: pageContext 有個特殊的方法 findAttribute()
範圍由小到大: page(jsp有效) request(一次請求) session(一次會話) application(當前web應用)
page : PageContext對象
request : request對象
session : session對象
application : ServletContext對象
生命週期: 就是指對象的建立到銷燬的期間
page: jsp 頁面被執行,生命週期開始,jsp 頁面執行完畢 ,生命週期結束
request : 用戶發送一個請求,開始,服務器返回響應,請求結束,生命週期結束
session : 用戶打開瀏覽器訪問,(getSession方法被調用來)建立session ---(開始)
當session超時或被聲明失效,該對象生命週期結束 ---(結束)
application: web應用加載的時候建立(開始), web應用被移除或服務器關閉,對象銷燬(結束)
2.1. 什麼是域? 爲何把這4個對象叫作域對象呢?
域: 即範圍的意思
web中的域對象:能夠存儲對象,在做用範圍內均可以取到,其內部是Map集合的實現 Map<String, Object>
class PageContext {
private Map map = new HashMap();
private HttpSession session;
……
}
四種域對象的做用範圍 ( 重要 ):
page: 只在當前jsp頁面有效
request: 只在當前請求有效, 每次請求分別對應不一樣的request域對象
session : 只在一次會話中有效,會話結束就沒法取到數據了 (特殊狀況,發送Cookie)
// session: 默認狀況下,同一個瀏覽器來訪問有效(發送同一個sessionid)
application : 在一個web應用中有效 (只要服務器不關,web應用不移除就能夠取數據)
四個域對象的範圍由小到大排列依次爲: page < request < session < application
原則: 四個域對象在選擇的時候,能用範圍小的毫不用範圍大的
page: 數據只是暫時存放在集合中,若是要在jsp頁面的其餘地方使用,須要用page(頁面中自定義的map)
ps: 何時須要用map了,就用page
request:數據只是作顯示的,看完了就沒用了,就存request域
ps: 請求轉發, Servlet 產生的處理結果(數據) 交給jsp顯示,
session: 數據給用戶看完了,一會還要用,會話結束了就沒用了
ps :用戶登錄,用戶信息發給客戶端看,看完了,一會訪問別的頁面還要看用戶信息
ps: 購物車,購物成功了,給用戶看購物車,待會隨時能夠查看購物車
ps: 請求重定向,由於是兩次請求,第一次請求的數據,第二次請求還要看
application : 數據給一個用戶用完了,別人還要用
ps: 聊天室,聊天記錄,須要給全部的用戶看
ps: 統計網站在線人數,全部人看到的應該是一個數
總結: 須要定義Map不如用page,
請求轉發Servlet帶給jsp的數據存request
請求重定向帶過去的數據存Session,
全局的數據存application
5、 jsp 細節
只有當jsp頁面指定的page指令isErrorPage爲true時,纔有exception隱式對象
Jsp註釋:
jsp 出錯:
1) 被翻譯的Servlet不能編譯,語法錯,這時會報告是由於jsp中的哪行致使不能編譯
2) 翻譯的Servlet 在運行期間出現異常, 報告是jsp的哪行致使的異常
此時會進一步報告致使異常的緣由,在Servlet中的哪行出現異常
2. jsp 映射 也是經過servlet 元素
6、內省( introspect )
1)必須有無參構造函數
2)屬性必須私有,咱們稱爲字段
3)必須提供標準的get和set方法
例: name 字段 的getter: String getName() settter: void setName(String name)
Jdk中的api : PropertyDescriptor類操做Bean的屬性
核心類 BeanUtils
setProperty(bean, name, value)
copyProperties(target, source);
BeanUtils的做用:
1.能夠支持String到8種基本數據類型轉換
2.其餘引用數據類型都須要註冊轉換器 ConvertUtils.register(Converter, Class)
爲了移除jsp頁面的java代碼,sun公司提供了一些內置的標籤à咱們稱爲jsp標籤,或jsp動做元素
1. <jsp:include> 至關於 RequestDispatcher 對象的頁面引入
dispatcher.include 用於實現 Servlet包含
dispatcher.forward 用於實現servlet轉發
forward 在轉發時, web容器會清空response中的數據,轉發以後就沒法向response寫入數據
動態引入方式: 在程序運行期間引入,jsp被翻譯成兩個servlet (check?)
靜態引入方式: include指令也能實現頁面的引入,將兩個jsp翻譯成一個Servlet
包含和被包含的jsp頁面指令不能發生衝突(其中,import和pageEncoding能夠衝突)
2. <jsp:forward> 實現請求轉發
結合 <jsp:param> 標籤傳參,自動進行url編碼,編碼的方式參照request編碼
3. <jsp:useBean id class scope> 內省
反射建立javabean,以id做爲key存入指定的域
其實在建立以前,會先去域中找,找到了則不建立
4. <jsp:setProperty> 設置屬性值
<jsp:setProperty name=」user」 property=」username」 value=」zs」 />
<jsp:setProperty name=」user」 property=」username」 param=」username」 />
<jsp:setProperty name=」user」 property=」*」 /> 批量
5. <jsp:getProperty> 得到屬性值
8、web開發模式
Sun公司針對web開發提供了兩種模式
Model1: jsp+javabean 只適合小型應用
Model2: servlet+jsp+javabean MVC (重要)
Lesson 13: EL表達式 和 JSTL標籤
1. el 全名爲Expression Language。它是一種數據訪問語言(不能寫if語句,for循環等邏輯語句),簡稱: el 表達式
2. EL 能實現以下功能:
1) 使用變量訪問web域中存儲的對象 ${user}
2) 訪問javabean的屬性 ${user.address.city}
3) 執行基本的邏輯運算
4) 直接使用隱式對象
5) 調用 el 函數
3. el 表達式用在哪裏
1) 在 jsp 頁面直接輸出數據
2) 在標籤中使用el直接爲屬性賦值
4. el 表達式獲取數據
1.在jsp頁面使用el表達式能夠輕鬆地得到web域中的對象
2.並對 javabean 、 數組、 list 、 map 進行取值
5. el表達式不能寫if,for等邏輯語句,因此須要對web域中的list和map集合進行迭代就須要結合 jstl 迭代標籤
JSTL是sun公司開發的一套標籤庫
1. JSTL標籤的做用: 使用JSTL能夠在頁面中實現一些簡單的邏輯,從而替換頁面中的腳本代碼
2. 如何在頁面中使用JSTL標籤? 在頁面中使用JSTL標籤需完成如下2個步驟:
1) 導入jstl.jar和standerd.jar這兩個JSTL的jar文件。
2) 在JSP頁面中使用 <%@ taglib=」」 uri=「」 prefix=「」 %> 元素導入標籤庫。
3. 最經常使用的 jstl 標籤爲 forEach 和 if 標籤
<c:foreach var=」」 items=」」>
<c:if test=」」>
6. el表達式雖然不能進行邏輯判斷和寫for循環,可是el表達式中能夠進行邏輯運算
7. el表達式中的保留關鍵字
隱含對象名稱 |
描 述 |
pageContext |
對應於JSP頁面中的pageContext對象 (用於獲取JSP的8個隱式對象.最主要用途:${pageContext.request.contextPath} |
pageScope |
表明page域中用於保存屬性的Map對象 |
requestScope |
表明request域中用於保存屬性的Map對象 |
sessionScope |
表明session域中用於保存屬性的Map對象 |
applicationScope |
表明application域中用於保存屬性的Map對象 |
param |
表示一個保存了全部請求參數的Map對象 (應用:表單回顯) |
paramValues |
表示一個保存了全部請求參數的Map對象,它對於某個請求參數,返回的是一個string[] |
header |
表示一個保存了全部http請求頭字段的Map對象 |
headerValues |
同上,返回string[]數組。注意:若是頭裏面有「-」 , 例Accept-Encoding,則要headerValues[「Accept-Encoding」] |
cookie |
表示一個保存了全部cookie的Map對象 |
initParam |
表示一個保存了全部web應用初始化參數的map對象 |
el表達式的11個隱式對象的具體用途 ( 除了pageContext,其他10個都是map類型 )
1. pageContext 做用: 得到servlet上下文路徑 (web應用名稱)
最經常使用代碼: ${pageContext.request.contextPath }
2. pageScope、requestScope、sessionScope、applicationScope
做用: 分別對應每一個域所對應的那個map,能夠準確地得到四個域中的對象,用於取值
3. param、paramValues 做用: 得到請求參數,通常用於作表單的回顯
4. header、headerValues 做用: 得到請求消息頭
5. cookie 做用: 得到瀏覽器發送的cookie
Cookie也是map集合,key是cookie的name value是對應的cookie對象
6. initParam 做用: 得到web 初始化參數
1. 咱們能夠開發自定義函數,用於調用java類的方法
案例: 對頁面輸出的內容進行 html 轉義
實現步驟:
1) 編寫一個java類, 定義一個靜態方法
去tomcat的例子中拷貝一個現成的
%tomcat安裝目錄%\webapps\examples\WEB-INF\classes
2) 編寫標籤庫描述符(tld)文件,在tld文件中描述自定義函數
找個現成的修改一下
%tomcat安裝目錄%\webapps\examples\WEB-INF\jsp2
3) 在jsp頁面中導入標籤庫 便可使用函數
<%@ taglib prefix="myfn" uri="/WEB-INF/myfn.tld" %>
<%@ taglib prefix="myfn" uri="http://www.itcast.cn/myfn" %>
帖子的內容 : ${myfn:transfer(requestScope.data) }
2. sun 公司針對經常使用的字符串處理在jstl技術中提供了el函數中
Lesson 17
1、自定義標籤入門
jsp頁面做爲顯示的模板,應儘可能使用頁面標籤來實現,避免寫java代碼
若是在jsp頁面寫java代碼,首先會令jsp頁面難以閱讀,不利於頁面排版,其次,做爲頁面美化人員有可能會看不懂java代碼,若是將java代碼替換成標籤,那麼只要是懂html標籤的人都能看得懂
移除jsp頁面中的java代碼其實很簡單,只須要寫一個java類實現Tag接口,在java類中實現jsp頁面的代碼,在tld標籤描述文件中針對標籤和java類進行映射。
案例: 實現標籤顯示來訪者的ip
實現步驟:
1) 寫一個類 IpTag 實現 Tag 接口
2) 在 WEB-INF 目錄下 新建tld文件, 對標籤進行描述
3)在 jsp 頁面進行調用
2、傳統標籤 (已過期,可是框架中仍是會有不少傳統標籤)
執行流程:
1)建立對象 初始化
2)調用setPageContext方法傳入表示jsp頁面的pageContext
3)若是有父標籤調用 setParent 方法傳入父標籤
4)調用 setter 方法爲屬性賦值
5)調用 doStartTag 方法
6)執行標籤體 可選
7)調用 doEndTag 方法
8)調用release方法釋放資源
Tag.EVAL_BODY_INCLUDE 執行標籤體
Tag.SKIP_BODY 跳過標籤體
Tag.EVAL_PAGE 執行剩餘頁面
Tag.SKIP_PAGE 跳過剩餘頁面
1) IterationTag 接口
增長doAfterBody()方法,在方法體執行完畢時被調用
增長返回值EVAL_BODY_AGAIN 用於執行再次執行標籤體
實現此接口能夠實現循環執行標籤體
通常經過繼承默認實現類BodyTagSupport來實現該標籤
2) BodyTag 接口
增長setBodyContent(BodyContent b)方法傳入標籤體
咱們能夠經過該方法得到標籤體 從而對標籤體進行操做
tld 是 taglib descriptor的縮寫, tld文件爲標籤庫描述文件
經過 tag元素描述一個標籤
<tag>
<description>Outputs Hello, World</description>
<name>helloWorld</name>
<tag-class>cn.itcast.tag.HelloWorldTag</tag-class>
<body-content>empty</body-content>
</tag>
其中 name用於指定標籤的名稱(name怎麼寫,在jsp頁面怎麼寫)
tag-class用於指定標籤對應的java類
body-content用於指定標籤對之間能夠輸入的內容類型
empty: 表示空標籤,即不能有標籤體 (經常使用)
JSP: 標籤體不爲空,能夠寫jsp腳本
Scriptless: 不能寫jsp腳本,但能夠寫表達式和el (經常使用)
Tagdependent: 將原始內容提交 忽略el表達式等運算
須要在頁面標籤中使用屬性需完成如下步驟:
1) 在標籤類中定義一個屬性<attribute>
2) 爲屬性添加 setter 方法
3) 在tld文件中對屬性進行描述
實現:
<tag>元素的<attribute>子元素用於描述自定義
標籤的一個屬性,自定義標籤所具備的每一個屬性
都要對應一個<attribute>元素 。
<attribute>
<description>description</description>
<name>aaaa</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>ObjectType</type>
</attribute>
attribute的子元素
元素名 |
是否必須指定 |
描 述 |
description |
否 |
用於指定屬性的描述信息。 |
name |
是 |
用於指定屬性的名稱。屬性名稱是大小寫敏感的,而且不能以jsp、_jsp、java和sun開頭。 |
required |
否 |
用於指定在JSP頁面中調用自定義標籤時是否必須設置這個屬性。其取值包括true和false,默認值爲false,true表示必須設置,不然能夠設置也能夠不設置該屬性。 |
rtexprvalue |
否 |
rtexprvalue是runtime expression value(運行時表達式)的英文簡寫,用於指定屬性值是一個靜態值或動態值。其取值包括true和false,默認值爲false,false表示只能爲該屬性指定靜態文本值,例如"123";true表示能夠爲該屬性指定一個JSP動態元素,動態元素的結果做爲屬性值,例如JSP表達式<%=value %>。 |
type |
否 |
用於指定屬性值的Java類型。 |
實現將標籤體循環執行指定次數的標籤 Loop
因爲傳統標籤使用三個標籤接口來完成不一樣的功能,顯得過於繁瑣,不利於標籤技術的推廣, SUN公司爲下降標籤技術的學習難度,在JSP 2.0中定義了一個更爲簡單、便於編寫和調用的SimpleTag接口來實現標籤的功能
咱們能夠經過繼承 SimpleTagSupport 類來實現SimpleTag接口
方法詳解:
setJspContext方法
用於把JSP頁面的pageContext對象傳遞給標籤處理器對象
setParent方法
用於把父標籤處理器對象傳遞給當前標籤處理器對象
getParent方法
用於得到當前標籤的父標籤處理器對象
setJspBody方法
用於把表明標籤體的JspFragment對象傳遞給標籤處理器對象
doTag方法
用於完成全部的標籤邏輯,包括輸出、迭代、修改標籤體內容等。
在doTag方法中能夠拋出javax.servlet.jsp.SkipPageException異常,用於通知WEB容器再也不執行JSP頁面中位 於結束標記後面的內容,這等效於在傳統標籤的doEndTag方法中返回Tag.SKIP_PAGE常量的狀況。
Question: 方法的調用順序?
開發防盜鏈標籤
開發<c:if>標籤
開發<c:if><c:else>標籤
choose when otherwise
開發迭代標籤
Foreach
開發html轉義標籤
打包標籤庫
Lesson 9
jdbc : Java Database Connectivity
sun公司爲了統一對數據庫的操做,定義了一套api,稱之爲jdbc
這套api徹底由接口組成,咱們在編寫程序的時候針對接口進行調用
這些接口交給數據庫廠家去實現, 不一樣的數據庫廠商會提供不一樣的實現類,這些實現類被咱們稱做數據庫的驅動
步驟:
建 user 表 user.sql
create database day12 character set utf8 collate utf8_general_ci;
use day12;
create table users(
id int primary key auto_increment,
name varchar(40),
password varchar(40),
email varchar(60),
birthday date
)character set utf8 collate utf8_general_ci;
insert into users(name,password,email,birthday)
values('zs','123456','zs@sina.com','1980-12-04');
insert into users(name,password,email,birthday)
values('lisi','123456','lisi@sina.com','1981-12-04');
insert into users(name,password,email,birthday)
values('wangwu','123456','wangwu@sina.com','1979-12-04');
mysql-connector-java-5.0.8-bin.jar
// 1. 註冊數據庫的驅動
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// 2. 創建與mysql數據庫的鏈接 用到 jdbc api
String url = "jdbc:mysql://localhost:3306/day11";
String user = "root";
String password = "root";
Connection conn = DriverManager.getConnection(url, user, password);
// 3. 建立用於發送sql語句的 Statement 對象
Statement stmt = conn.createStatement();
// 4. 編寫一句 sql
String sql = "select * from users";
// 5. 發送sql, 得到結果集
ResultSet rs = stmt.executeQuery(sql);
// 6. 處理結果集
System.out.println("id | name | password | email | birthday");
while(rs.next()) {
// 有第一行
int id = rs.getInt("id"); // 經過列名取值比較直觀
String name = rs.getString("name");
String psw = rs.getString("password");
String email = rs.getString("email");
Date birthday = rs.getDate("birthday");
System.out.println(id + " | " + name + " | " + psw + " | " + email + " | " + birthday);
}
// 7. 關閉鏈接 釋放資源
rs.close();
stmt.close();
conn.close();
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
上面的語句會致使註冊兩次驅動
緣由在於,查看Driver類的源碼會發如今靜態代碼塊中完成了註冊驅動的工做,
也就是說註冊驅動其實很簡單,只須要加載驅動類便可
Class.forName(「com.mysql.jdbc.Driver」);
Connection conn = DriverManager.getConnection(url, user, password);
其中:url, 至關於數據庫的訪問地址,程序員經過url指定須要訪問的數據庫
jdbc:mysql:[]//localhost:3306/test?參數名:參數值
其中jdbc爲主協議,mysql爲子協議,localhost爲主機名,3306爲端口號,test爲數據庫名
url的後面能夠跟參數,經常使用的參數有:user=root&password=root&characterEncoding=UTF-8
若是url地址後面跟了user和password,建立Connection對象時將沒必要再次傳入值
Connection conn = DriverManager.getConnection(url);
補充: 若是訪問的localhost:3306,url 可省寫爲jdbc:mysql:///test
經常使用方法有:
createStatement():建立向數據庫發送sql的statement對象。
prepareStatement(sql) :建立向數據庫發送預編譯sql的PrepareSatement對象。
prepareCall(sql):建立執行存儲過程的callableStatement對象。
setAutoCommit(boolean autoCommit):設置事務是否自動提交。
commit() :在連接上提交事務。
rollback() :在此連接上回滾事務。
execute(String sql):用於向數據庫發送任意sql語句
executeQuery(String sql) :只能向數據發送查詢語句。
executeUpdate(String sql):只能向數據庫發送insert、update或delete語句
addBatch(String sql) :把多條sql語句放到一個批處理中。
executeBatch():向數據庫發送一批sql語句執行。
存儲的形式就是一種表格的形式,一樣是列+行,說白了就和咱們在 dos 命令行窗口查詢的結果同樣
遍歷方式:
一開始遊標指向結果集第一行, 也就是表頭
經過 next 將遊標移向下一行, 若是沒有下一行,該方法會返回false
得到當前行的數據須要調用get方法:
get(int index)得到第幾列 列數從1開始
get(String columnName) 根據列名得到值 (經常使用)
數據庫的數據類型與java中數據類型的對應關係
ResultSet對象的經常使用方法
next():移動到下一行
previous():移動到前一行
absolute(int row):移動到指定行
beforeFirst():移動resultSet的最前面。
afterLast() :移動到resultSet的最後面。
因爲數據庫的資源很是寶貴,因此用完了必定要記得釋放資源
特別是Connection對象,由於數據容許的併發訪問鏈接數量每每都比較有限
在java程序中,咱們應該將最終必需要執行的代碼放到finally當中
釋放資源的代碼
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt!=null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
1. 編寫程序對User表進行增刪改查操做
2.防止 sql 注入
在 service 層進行邏輯判斷
使用PreparedStatement對象
3. 編寫工具類對 jdbc 程序進行優化
將得到鏈接和釋放資源的代碼寫到通用的工具類中
實現一個簡單的員工信息管理系統,練習對員工表的crud
字段名 |
說明 |
類型 |
id |
編號 |
varchar(40) |
name |
員工姓名 |
varchar(20) |
gender |
性別 |
varchar(4) |
birthday |
出生日期 |
date |
idcard |
身份證號 |
varchar(20) |
degree |
學歷 |
varchar(20) |
entrydate |
入職日期 |
date |
position |
部門 |
varchar(40) |
department |
職位 |
varchar(20) |
web開發的兩種模式
model1: jsp+javabean 只適合小型應用
model2: servlet+jsp+javabean mvc
jsp 的做用只是顯示的模板,因此在mvc中jsp應該禁止外界直接方法,藏在 web-inf下面
web應用的分層框架
案例實現步驟:
1. 搭建開發環境
1) 建工程 建包
2) 導入須要jar包
BeanUtils 相關jar包
jstl 標籤
mysql 的驅動
3) 建立數據庫和表
create database day12_employee;
use day12_employee;
create table employee
(
id varchar(40) primary key,
name varchar(20),
gender varchar(6),
birthday date,
idcard varchar(20),
degree varchar(20),
entrydate date,
department varchar(20),
position varchar(40)
);
2. 設計bean
Employee.java
3. 實現dao(不是很理解..須要弄清楚)
4. 實現service
簡單service 原封不動調用dao的方法
5. 實現web層
首頁
在數據量較大的狀況下,咱們會數據分做多頁顯示,讓用戶瀏覽起來更加的方便,能夠根據頁碼去翻閱每一頁的數據
說到分頁,通常都會馬上想到先將數據存入集合,再將數據分做多頁顯示,這樣的作法當然能夠,但是一旦數據量較大的話就會形成內存的溢出,再者說,大部分用戶的瀏覽習慣都是隻看前三頁,若是數據總共有100頁,那就徹底不必將數據所有從數據庫中查出來了,因此一個廣泛的實現方式都是根據用戶須要瀏覽的頁碼,從數據庫中查詢一頁的數據供用戶瀏覽
分頁的目的就是爲了更加合理地作頁面顯示,因此首先要解決的就是頁面須要顯示哪些數據
一般來說,頁面上會顯示當前頁的信息、當前第幾頁、總共多少頁、頁碼、上一頁、下一頁等信息
咱們可使用一個 Page 對象來封裝頁面須要實現的數據
在service中計算出 Page 對象所需的數據
Service中須要的一些來自於數據庫的數據就找 Dao 索取
1)根據需求設計Page對象
2)dao的實現
兩個方法:
int getTotalRecord(); // 得到總記錄數
List getPageData(int start, int len); // 得到分頁數據
3)service 實現
Page getPage(int pageNum); // 計算分頁數據
4)Servlet 得到頁面提交的 pageNum
找service計算出分頁數據 Page
轉發給jsp 作分頁顯示
所謂事務,就是針對數據庫的一組操做(多條sql)
位於同一個事務的操做具有同步的特色,也就是要麼都成功,要麼都失敗
在實際中,咱們的不少操做都是須要由多條sql來共同完成的,例如,A帳戶給B帳戶轉帳就會對應兩條sql
update account set money=money-100 where name=‘a’;
update account set money=money+100 where name=‘b’;
假設第一條sql成功了,而第二條sql失敗了,這樣就會致使a帳戶損失了100元,而b帳戶並未獲得100元
若是將兩條sql放在一個sql中,當第二條語句失敗時,第一條sql語句也一樣不會生效,這樣a帳戶就不會有任何的損失----------------à這就是事務
默認狀況下,咱們向數據庫發送的sql語句是會被自動提交的,開啓事務就是至關於關閉自動提交功能,改成手動提交,咱們只須要將提交事務的操做放在最後一個操做,這樣一來,若是在提交事務以前出現異常,因爲沒有執行提交操做,事務中未提交的操做就會被回滾掉
account.sql
create table account(
id int primary key auto_increment,
name varchar(40),
money float
)character set utf8 collate utf8_general_ci;
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
aaa 給 bbb 轉帳 100元
update account set money=money-100 where name='aaa';
// 異常退出
update account set money=money+100 where name='bbb';
// 查詢結果
select * from accont;
若是開啓事務就能夠避免剛纔的錯誤發生
// 開啓事務
start transaction;
// 若是操做執行完畢,咱們須要將事務提交
commit;
// 還能夠手動回顧事務中全部未提交的事務
rollback;
命令行作實驗得出結論:
若是開了一個事務, 又敲了一次 start transaction 再次開啓事務, 前一個事務會自動提交
在使用 jdbc 操做數據庫時,須要使用 Connection 對象對事務進行管理
// 開啓事務
Connection.setAutoCommit(false); //設置自動提交爲false
// 回滾事務
Connection.rollback();
//提交事務
Connection.commit();
在 jdbc 程序中咱們還能夠設置回滾點, 讓事務回顧到指定的回滾點,而不是自動回滾全部未提交的操做
須要將程序中的異常捕獲,在catch語句塊中回滾事務,在finally中提交事務
注意 : 將 Commit 操做放在 finally 中是爲了保證提交未回滾的事務操做
事務有四大特性,通常來說,判斷一個數據庫是否支持事務,就看數據庫是否支持這四個特性
l 原子性(Atomicity)
原子性是指事務是一個不可分割的工做單位,事務中的操做要麼都發生,要麼都不發生。
l 一致性(Consistency)
事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態。
l 隔離性(Isolation)
事務的隔離性是多個用戶併發訪問數據庫時,數據庫爲每個用戶開啓的事務,不能被其餘事務的操做數據所幹擾,多個併發事務之間要相互隔離。
l 持久性(Durability)
持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即便數據庫發生故障也不該該對其有任何影響。
因爲數據庫是多線程併發訪問的,因此很容易出現多個線程同時開啓事務的狀況
多線程開事務容易引發 贓讀、不可重複讀、幻讀 的狀況發生
贓讀:dirty read
是指一個事務讀取了另外一個事務未提交的數據,這是至關危險的。
設想一下,A要給B轉帳100元購買商品, 若是A開啓了一個事務作了轉帳的工做
update account set money=money+100 while name=‘b’;
update account set money=money -100 while name=‘a’;
A先不提交事務,通知B來查詢
這時B來查詢帳戶,因爲會讀到A開啓的事務中未提交的數據,就會發現A確實給本身轉了100元,
天然就會給A發貨,A等B發貨後就將事務回滾,不提交,此時,B就會受到損失
不可重複讀:non-repeatable read
是指事務中兩次查詢的結果不一致,緣由是在查詢的過程當中其餘事務作了更新的操做 update
例如,銀行作報表,第一次查詢A帳戶有100元,第二次查詢A帳戶有200元,緣由是期間A存了100元
這樣就會致使一行屢次統計的報表不一致
不可重複讀和髒讀的區別是:
髒讀是讀取前一事務未提交的髒數據,不可重複讀是在事務內重複讀取了別的線程已提交的數據。
有的時候你們會認爲這樣的結果是正確的,沒問題
咱們能夠考慮這樣一種狀況,好比銀行程序須要將查詢結果分別輸出到電腦屏幕和寫到文件中,結果在一個事務中針對輸出的目的地,進行的兩次查詢不一致,致使文件和屏幕中的結果不一致,銀行工做人員就不知道以哪一個爲準了。
幻讀:phantom read 又名虛讀
是指在一個事務內兩次查詢中數據筆數不一致
幻讀和不可重複讀有些相似,是指兩次查詢過程當中,其餘事務作了插入記錄的操做,致使記錄數有所增長
insert
例如: 銀行作報表統計account表中全部用戶的總額時,此時總共五個帳戶,總金額爲500元,
這時有一個新的帳戶產生了,而且存了100元,這時銀行再統計會發現賬戶總金額爲600元了,
形成虛讀一樣會使銀行遇到一樣的困惑
實驗發現不會出現虛讀
來自網絡的解釋:幻讀只是在理論上會發生的一種狀況,而現實操做中並非必定會發生
爲了不多線程開事務引起的問題,咱們須要將事務進行隔離
事務有四種隔離級別,不一樣的隔離級別能夠防止不一樣的錯誤發生
serializable:可串行化,能避免髒讀、不可重複讀、幻讀狀況的發生
repeatable read:可重讀,能避免髒讀、不可重複讀狀況的發生
read committed:讀取提交的內容,可避免髒讀狀況發生
read uncommitted:讀取未提交的內容最低級別,避免不了任何狀況
操做:
//設置事務隔離級別 set transaction isolation level
//查詢當前事務隔離級別 select @@tx_isolation
查詢看到的都是快照
位於事務中的屢次查詢,若是隔離級別設置爲repeatable read,
那麼屢次查詢讀的就是一個快照說白了就是不更新快照
傳統的開發模式下,Servlet處理用戶的請求,找Dao查詢數據,dao會建立與數據庫之間的連接,完成數據查詢後會關閉數據庫的連接。
這樣的方式會致使用戶每次請求都要向數據庫創建連接而數據庫建立鏈接一般須要消耗相對較大的資源,建立時間也較長。假設網站一天10萬訪問量,數據庫服務器就須要建立10萬次鏈接,極大的浪費數據庫的資源,而且極易形成數據庫服務器內存溢出、宕機。
解決方案: 就是數據庫鏈接池
鏈接池就是數據庫鏈接對象的一個緩衝池
咱們能夠先建立10個數據庫鏈接緩存在鏈接池中,當用戶有請求過來的時候,dao沒必要建立數據庫鏈接,而是從數據庫鏈接池中獲取一個,用完了也沒必要關閉鏈接,而是將鏈接換回池子當中,繼續緩存
使用數據庫鏈接池能夠極大地提升系統的性能
jdbc針對數據庫鏈接池也定義的接口java.sql.DataSource,全部的數據庫鏈接池實現都要實現該接口
該接口中定義了兩個重載的方法
Connection getConnection()
Connection getConnection(String username, String password)
數據庫鏈接池實現思路
1) 定義一個類實現java.sql.DataSource接口
2) 定義一個集合用於保存Connection對象,因爲頻繁地增刪操做,用LinkedList比較好
3) 實現getConnection方法,在方法中取出LinkedList集合中的一個鏈接對象返回
注意:
返回的Connection對象不是從集合中得到,而是刪除
用戶用完Connection,會調用close方法釋放資源,此時要保證鏈接換回鏈接池,而不是關閉鏈接
重寫close方法是難點,解決方案: 裝飾設計模式、動態代理
一般咱們把DataSource的實現,按其英文含義稱之爲數據源,數據源中都包含了數據庫鏈接池的實現。
一些開源組織提供了數據源的獨立實現,經常使用的有:
DBCP 數據庫鏈接池
C3P0 數據庫鏈接池
DBCP 是 Apache 軟件基金組織下的開源鏈接池實現
tomcat服務器就是使用DBCP做爲數據庫鏈接池
使用DBCP數據源,須要導入兩個jar包
Commons-dbcp.jar:鏈接池的實現
Commons-pool.jar:鏈接池實現的依賴庫
DBCP核心 API
BasicDataSource
數據源實現
BasicDataSourceFactory
用於建立數據源的工廠類
方法1: 直接建立對象,設置參數
BasicDataSource bds = new BasicDataSource();
// 設置鏈接數據庫須要的配置信息
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUrl("jdbc:mysql://localhost:3306/jdbc3");
bds.setUsername("root");
bds.setPassword("root");
// 設置鏈接池的參數
bds.setInitialSize(5);
bds.setMaxActive(10);
ds = bds
方法2: 經過工廠類建立對象,讀取配置文件
try {
Properties prop = new Properties();
// 讀配置文件
InputStream in =
JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
prop.load(in);
ds = BasicDataSourceFactory.createDataSource(prop);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
#鏈接設置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc3
username=root
password=root
#<!-- 初始化鏈接 -->
initialSize=5
#最大鏈接數量
maxActive=10
#<!-- 最大空閒鏈接 -->
maxIdle=10
#<!-- 超時等待時間以毫秒爲單位 6000毫秒/1000等於60秒 -->
maxWait=60000
#JDBC驅動創建鏈接時附帶的鏈接屬性屬性的格式必須爲這樣:[屬性名=property;]
#注意:"user" 與 "password" 兩個屬性會被明確地傳遞,所以這裏不須要包含他們。
connectionProperties=useUnicode=true;characterEncoding=gbk
#指定由鏈接池所建立的鏈接的自動提交(auto-commit)狀態。
defaultAutoCommit=true
#driver default 指定由鏈接池所建立的鏈接的只讀(read-only)狀態。
#若是沒有設置該值,則「setReadOnly」方法將不被調用。(某些驅動並不支持只讀模式,如:Informix)
defaultReadOnly=
#driver default 指定由鏈接池所建立的鏈接的事務級別(TransactionIsolation)。
#可用值爲下列之一:(詳情可見javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
c3p0是一個開源的jdbc鏈接池,咱們熟悉的 Hibernate和 Sprint 框架使用的都是該數據源
方法1:直接建立對象,設置參數
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc3");
cpds.setUser("root");
cpds.setPassword("root");
cpds.setInitialPoolSize(5);
cpds.setMaxPoolSize(15);
方法2:讀取配置文件
ComboPooledDataSource cpds = new ComboPooledDataSource("itcast");
配置文件爲c3p0-config.xml 該文件須要放在類路徑下
<c3p0-config>
<default-config>
<!—- 默認配置 –->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">15</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc3</property>
<property name="user">root</property>
<property name="password">root</property>
</default-config>
<named-config name="xwh">
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">15</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc3</property>
<property name="user">root</property>
<property name="password">root</property>
</named-config>
</c3p0-config>
元數據,能夠理解爲描述數據的數據
jdbc中的元數據是指數據庫、表、列的定義信息
ResultSetMetaData對象表示結果集 ResultSet對象的元數據
得到該對象:
ResultSetMetaData metaData = rs.getMetaData();
經常使用方法:
getColumnCount() 返回resultset對象的列數
getColumnName(int column) 得到指定列的名稱
getColumnTypeName(int column) 得到指定列的類型
使用jdbc對數據庫進行crud操做時,會有不少重複的代碼,仔細分析不難發現其實變化的只是其中幾行代碼
對於 cud(增刪改) 操做,代碼幾乎徹底同樣, 惟一的區別就是sql語句不一樣,咱們徹底能夠把相同的代碼抽取出來定義在一個工具方法中,而後定義一個參數來接收sql語句
對於 r(查詢) 操做,除SQL語句不一樣以外,根據操做的實體不一樣,對ResultSet結果集的處理也有所不相同,所以可義一個query方法,除以參數形式接收變化的SQL語句外,可使用策略模式由qurey方法的調用者決定如何把ResultSet中的數據映射到實體對象中
// 通用的增刪改方法
public static int update(String sql, Object[] params) throws SQLException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 得到鏈接
conn = getConnection();
// 預編譯sql
pstmt = conn.prepareStatement(sql);
// 將參數設置進去
for(int i=0; params!=null&&i<params.length; i++) {
pstmt.setObject(i+1, params[i]);
}
// 發送sql
int num = pstmt.executeUpdate();
return num;
} finally {
// 釋放資源
release(conn, pstmt, rs);
}
}
// 優化查詢
public static Object query(String sql, Object[] params, ResultSetHandler rsh) throws SQLException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 得到鏈接
conn = getConnection();
// 預編譯sql
pstmt = conn.prepareStatement(sql);
// 將參數設置進去
for(int i=0; params!=null&&i<params.length; i++) {
pstmt.setObject(i+1, params[i]);
}
// 發送sql
rs = pstmt.executeQuery();
// 不知作別人想如何處理結果集
// 乾脆想別人所要一個結果集的處理器
// 爲了讓當前代碼繼續,定義一個結果集處理器接口
// 策略模式, 規定算法,具體的算法留給未來的調用者實現
Object obj = rsh.handle(rs);
return obj;
} finally {
// 釋放資源
release(conn, pstmt, rs);
}
}
public interface ResultSetHandler {
// 處理結果集的方法
public Object handle(ResultSet rs);
}
public class BeanListHandler implements ResultSetHandler {
private Class clazz;
public BeanListHandler(Class clazz) {
this.clazz = clazz;
}
public Object handle(ResultSet rs) {
try {
// 取出結果集全部的記錄,封裝到bean,存入list返回
List list = new ArrayList();
while (rs.next()) {
Object bean = clazz.newInstance();
// 得到元數據
ResultSetMetaData metaData = rs.getMetaData();
// 得到列的數量
int count = metaData.getColumnCount();
// 遍歷列
for(int i=1; i<=count; i++) {
// 取列名
String columnName = metaData.getColumnName(i);
// 取這列的值
Object value = rs.getObject(columnName);
// 反射出屬性
Field field = clazz.getDeclaredField(columnName);
// 設置屬性
field.setAccessible(true);
field.set(bean, value);
}
// 加入list
list.add(bean);
}
return list;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public class BeanHandler implements ResultSetHandler {
private Class clazz;
public BeanHandler(Class clazz) {
this.clazz = clazz;
}
public Object handle(ResultSet rs) {
// 不知道有幾列數據,不知道列名,不知道封裝到什麼樣的bean
// 表的列明和javabean的字段名一致
try {
if(rs.next()) {
// 建立bean
Object bean = clazz.newInstance();
// 封裝數據
// 得到結果集的元數據
ResultSetMetaData metaData = rs.getMetaData();
int count = metaData.getColumnCount();
// 迭代取每一列的數據
for(int i=1; i<=count; i++) {
// 得到列名 username
String columnName = metaData.getColumnName(i);
// 得到數據 ddd
Object value = rs.getObject(columnName);
// 根據列名反射出映射的屬性 username
Field field = clazz.getDeclaredField(columnName);
// 爲屬性賦值
field.setAccessible(true);
field.set(bean, value);
}
return bean;
}
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// 取出第一行的全部記錄存入一個Object數組
public class ArrayHandler implements ResultSetHandler {
public Object handle(ResultSet rs) {
try {
if (rs.next()) {
// 指向了第一行的記錄
// 得到元數據
ResultSetMetaData metaData = rs.getMetaData();
// 得到列數
int count = metaData.getColumnCount();
// 建立數組
Object[] arr = new Object[count];
// 迭代全部列的值,存入數組
for(int i=1; i<=count; i++) {
Object value = rs.getObject(i); // 得到指定列的值
arr[i-1] = value;
}
return arr;
}
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
批處理
處理大數據
Clob Character large Object
text
Blob binary large object
Object-Relation Mapping 對象關係映射(對象關係模型)
經常使用的 O-R Mapping 工具備:
Hibernate
session.save(user)
ibatis
sql 語句要本身寫
DBUtils
簡單的工具
commons-dbutils 是 Apache 組織提供的一個開源 JDBC工具類庫,它是對JDBC的簡單封裝
DBUtils 核心API
org.apache.commons.dbutils.QueryRunner //提供update(cud)和query(r)方法
org.apache.commons.dbutils.ResultSetHandler //結果集處理器,接口類型
org.apache.commons.dbutils.DbUtils //工具類,提供一系列close方法,裝載驅動等
API詳解
1. QueryRunner
重載的構造函數
public QueryRunner()
調用無參的構造方法,在進行 crud 操做時須要傳入 Connection 對象,通常用於事務
public QueryRunner(DataSource ds)
建立對象時傳入 數據源 多數狀況下采用此構造函數
2. ResultSetHandler
該接口爲結果集處理器,因此對結果集進行處理的程序都須要實現該接口
DBUtils框架提供了一系列經常使用的結果集處理器實現類
l ArrayHandler:把結果集中的第一行數據轉成對象數組。
l ArrayListHandler:把結果集中的每一行數據都轉成一個數組,再存放到List中。
l BeanHandler:將結果集中的第一行數據封裝到一個對應的JavaBean實例中。
l BeanListHandler:將結果集中的每一行數據都封裝到一個對應的JavaBean實例中,存放到List裏。
l ColumnListHandler:將結果集中某一列的數據存放到List中。
l KeyedHandler(name):將結果集中的每一行數據都封裝到一個Map裏,再把這些map再存到一個map裏,其key爲指定的key。
l MapHandler:將結果集中的第一行數據封裝到一個Map裏,key是列名,value就是對應的值。
l MapListHandler:將結果集中的每一行數據都封裝到一個Map裏,而後再存放到List
3、 JDBC 操做多表
多表的關係三種:
1. many2one
典型應用 部門和員工
2. mamy2many
典型應用 老師和學生
3. one2one
典型應用 用戶和住址
用戶上傳頭像、上傳圖片、郵件上傳附件等
文件上傳表單和普通表單有兩個區別
1) 須要文件上傳字段 <input type=」file」 />
2) form 表單的 enctype 屬性須要指定爲 multipart/form-data
3) 文件的提交方式必須爲POST
在 Servlet 中經過 request.getInputStream 得到表單上傳數據,會發現數據是分段發送的
因爲本身寫程序解析有難度,咱們可使用Apache 開發的開源組件Commons-fileupload
須要導入 jar 包Commons-fileupload 和Commons-io
// 1. 建立工廠類
DiskFileItemFactory factory = new DiskFileItemFactory();
// 2. 建立FileUpload對象
ServletFileUpload upload = new ServletFileUpload(factory);
// 3. 判斷是不是上傳表單
boolean b = upload.isMultipartContent(request);
if(!b) {
// 不是文件上傳
request.setAttribute("message", "對不起,不是文件上傳表單!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
// 是文件上傳表單
// 4. 解析request,得到FileItem項
List<FileItem> fileitems = upload.parseRequest(request);
// 5. 遍歷集合
for(FileItem item : fileitems) {
// 判斷是否是普通字段
if(item.isFormField()) {
String name = item.getFieldName();
String value = item.getString();
// 手工的轉換了
value = new String(value.getBytes("iso-8859-1"),"utf-8");
System.out.println(name + "=" + value);
}
else {
// 文件上傳字段
// 得到文件名
String filename = item.getName();
System.out.println(filename);
filename = filename.substring(filename.lastIndexOf("\\")+1);
System.out.println(filename);
// 建立文件
ServletContext context = getServletContext();
String dir = context.getRealPath("WEN-INF/upload");
File file = new File(dir, filename);
file.createNewFile();
// 得到流,讀取數據寫入文件
InputStream in = item.getInputStream();
FileOutputStream fos = new FileOutputStream(file);
int len;
byte[] buffer = new byte[1024];
while((len=in.read(buffer))>0)
fos.write(buffer,0,len);
fos.close();
in.close();
item.delete(); // 刪除臨時文件
}
1) 文件名中文亂碼問題,解決辦法: 告訴文件上傳組件以什麼編碼方式來解碼文件名
ServletUpload.setCharacterEncoding(「utf-8」);
request. setCharacterEncoding(「utf-8」);
2) 普通字段中文亂碼問題
fileitem.getString(「utf-8」);
對於大文件不能緩存在內存,須要緩存到硬盤,爲了方便管理,咱們須要設置臨時文件存放目錄
// 設置臨時文件的存放位置
factory.setRepository(new File("d:/temp"));
文件上傳完畢須要刪除臨時文件,不然會致使服務器存在兩份上傳文件
// 注意,須要先將流進行關閉,不然會致使臨時文件沒法刪除
out.close();
in.close();
// 刪除臨時文件
fileitem.delete();
1) 目錄須要隱藏,禁止外界直接訪問
2) 文件名須要保證不重複
3) 文件應該分目錄存放
須要實現對文件上傳進度的監聽,須要給FileUpload 對象添加 ProgressListener
在upload方法中對與進度相關的數據進行處理
upload.setProgressListener(new ProgressListener() {
long num = 0;
public void update(long bytesRead, long contentLength, int items) {
long progress = bytesRead*100/contentLength;
if(progress==num)
return;
num = progress;
System.out.println("上傳進度:" + progress + "%");
// request.getSession().setAttribute("progress", progress);
}
});
實驗:
1) 使用 iframe 發送請求,請求一個Servlet, 在Servlet 中返回響應,發送自增的num
此時會發現 iframe 會不停地向Servlet發送請求
2) 點擊文件上傳按鈕後,iframe馬上中止刷新,直至上傳完畢頁面跳轉至新頁面
3)爲了觀察實驗結果,將form 的 target 指定爲 iframe, UploadServlet回送上傳完畢的結果
4) 出現上述問題的緣由,瀏覽器不支持多線程同時訪問服務器只能同時發送一個請求,
這樣的訪問方式爲同步訪問
5) 要在文件上傳的同時在iframe中實現進度訪問,就須要ie瀏覽器與服務器進行異步交互
此時就須要 XMLHttpRequest 對象(ajax)
在javascript中能夠直接使用XMLHttpRequest 對象與服務器進行異步通訊
Step 1.得到XmlHttpRequest 對象的方式有兩種
1. ie7以上版本
var xhr = null;
if(window.XMLHttpRequest)
xhr = new XMLHttpRequest();
2. ie7如下版本
if(window.ActiveXObject)
xhr = new ActiveXObject(「Microsoft.XMLHTTP」);
Step 2.得到對象後須要調用open()方法輸入請求地址
注意請求方式, 地址的輸入, 而且須要設置爲true 指定異步訪問該地址
xhr.open(「get」,」/upload/servlet/UploadServlet」, false)
// 調用send 方法發送請求,post方式須要發送消息體,get方式則不用直接傳入null值
xhr.send(null);
// 訪問 responseText 屬性得到 Servlet 回送的數據
document.write(xhr.responseText);
//設置緩衝區大小,字節爲單位,默認爲10K,通常不用修改
factory.setSizeThreshold(1000);
//設置臨時文件存放目錄
factory.setRepository(file);
//判斷是否爲文件上傳表單
boolean b = upload.isMultipartContent(request);
//解析request對象
List<FileItem> list = upload.parseRequest(request);
//設置上傳文件的最大值
setFileSizeMax(long fileSizeMax)
//設置上傳文件總量的最大值
setSizeMax(long sizeMax)
//設置編碼格式
setHeaderEncoding(java.lang.String encoding)
//註冊進度監聽器
setProgressListener(ProgressListener pListener)
//得到表單字段的屬性名
item.getFieldName();
//得到普通字段的值
item.getString(charsetName)
//得到文件上傳字段的文件名
item.getName()
//得到文件上傳的流
item.getInputStream()
Filter 過濾器,又稱攔截器
實現 Filter 接口的類咱們稱之爲 Filter (過濾器或攔截器)
Filter能對用戶訪問的資源進行攔截
在Filter裏面能夠用 request得到請求消息 用response寫入響應消息
chain.doFilter(request, response) 方法放行 目標Servlet使用的是同一個請求和響應
doFilter 方法後面的代碼會執行,在目標Servlet 返回響應後執行, 也可使用同一個請求和響應
1) 寫一個類實現 Filter 接口 , 在doFilter 方法中寫功能代碼
public class Filter1 implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("before");
chain.doFilter(request, response);
System.out.println("after");
}
2) 在web.xml中配置Filter攔截的資源路徑
<filter>
<filter-name>filter1</filter-name>
<filter-class>cn.itcast.filter.Filter1</filter-class>
</filter>
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
能夠針對某一個url配置多個Filter, 這些Filter就會組成一個Filter鏈, 用FilterChain對象表示
FilterChain對象的doFilter方法做用就是讓Filter鏈上的當前攔截器放行,請求進入下一個Filter
response 的中文編碼問題, 只能在response.getWriter() 第一次被調用以前指定編碼纔有效
一旦指定了編碼,當前Filter鏈和目標Servlet使用的response都是同一個編碼,由於用的原本就是一個
Response,後面再指定編碼將被視爲無效
Filter 就像一個特殊的Servlet
Filter 在web容器啓動是就初始化
Filter 能夠實現攔截功能,由於有個 FilterChain 對象,有個 doFilter方法能夠實現對訪問資源的放行
Filter能夠替代Servlet全部的功能,還多一個放行功能
實現Filter的init和destroy方法就能夠觀察Filter的聲明週期
web容器啓動時,會讀web.xml文件,將全部的Filter都初始化
Filter對象建立後會駐留在內存,當web應用移除或服務器中止時才銷燬
Filter鏈中全部的Filter的攔截順序 按照 在 web.xml 文件中的配置的前後順序來進行攔截
在 web.xml 文件中爲Filter 配置初始化參數
<init-param>
<param-name>name</param-name>
<param-value>xxxx</param-value>
</init-param>
在 init 方法中讀取配置文件
public void init(FilterConfig filterConfig) throws ServletException {
String name = filterConfig.getInitParameter("name");
}
1) 文件緩存
因爲html頁面的url是沒有在 web.xml 文件中配置的 服務器會調用DefaultServlet
在DefaultServlet 中會檢查文件的修改時間, 若是沒有修改則發送 304頭
這樣就會致使過濾器也被緩存
能夠經過發送 200 狀態碼,可是 html 頁面的數據仍然得不到讀取
2) html 頁面亂碼
在 Filter 和 Html 中指定編碼爲 utf-8 , 這樣會致使 html 頁面中文亂碼
緣由是 html 頁面數據會經過 DefaultServlet 發送
查看 web.xml 文件 發現DefaultServlet默認使用 gbk編碼
修改配置 加初始化參數
<init-param>
<param-name>fileEncoding</param-name>
<param-value>utf-8</param-value>
</init-param>
Filter的dispatcher元素有4種取值, 分別表明四種攔截方式
REQUEST 攔截直接的請求方式
INCLUDE 攔截頁面包含的訪問方式
FORWARD 攔截請求轉發訪問方式
ERROR 攔截出錯頁面的訪問方式
攔截的url地址可使用 /* *.擴展名
<filter-mapping> 元素中能夠配置多個地址 用於攔截多個url或servlet
對於多個條件都符合的url, filter會進行屢次攔截
1. 緩存
禁止瀏覽器緩存全部動態頁面
response.setDateHeader("Expires",-1);
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
強制瀏覽器緩存全部的靜態頁面 html jpg css
String uri = request.getRequestURI();
String time = null;
if(uri.endsWith(".html"))
time = config.getInitParameter("html");
else if(uri.endsWith(".jpg"))
time = config.getInitParameter("jpg");
long l = Long.parseLong(time);
response.setDateHeader("Expires",System.currentTimeMillis() + l);
2. 實現用戶自動登錄
1)在用戶登錄成功後,發送一個名稱爲user的cookie給客戶端,cookie的值爲用戶名和md5加密後的密碼
2)編寫過濾器檢查用戶是否帶名爲user的cookie來,若是有,檢查用戶名和密碼作自動登錄
核心思路:
用戶登錄後找LoginServlet , LoginServlet中作登錄,若是登錄成功, 得到用戶選擇的自動登錄時間
建立一個新的cookie 將用戶名和密碼用 「_」鏈接做爲value,autoLogin做爲name
設置cookie 的有效路徑 request.getContextPath() 做用於整個web應用
設置cookie的有效時間爲 autologintime
發送 cookie
寫一個過濾器,對全站的資源進行攔截, 檢查用戶發送的cookie有沒有一個名爲autologin的
若是有 取出用戶名和密碼 再次作登錄處理 若是登錄成功, 將 user 存入session ,放行
出於安全性考慮, cookie 中的密碼應該進行 md5 加密
3. 統一全站字符編碼
response和request的post的方式好辦
// 解決全站的亂碼問題 request response
response.setContentType("text/html;charset=utf-8");
request.setCharacterEncoding("utf-8"); // 只對 post 方式起做用, 對get方式不起做用
對於request的get方式須要手工轉換,此時就須要用到 包裝設計模式decorator包裝 getParameter方法
4. 爲全站添加頁眉和頁腳 -> 添加用戶模塊
5. 發送壓縮後的響應數據
給 IE 瀏覽器會送的數據 須要進行gzip 壓縮 訪問速度快 省點瀏覽
在最後將數據打給瀏覽器的時候, 將 response 中的數據所有壓縮
在過濾器放行的時候傳入一個 假的 response 提供緩衝區, 這樣後面的資源都會寫入個人緩衝區
緩衝區滿了 或者 請求快結束的時候 將緩衝區的數據壓縮後寫入 真的 response
j2se 提供了一個流GZIPOutputStream 用於 gzip壓縮
j2se 提供了一個流 ZIPOutputStream 用於 zip 壓縮
6. 跟蹤用戶上次訪問時間: Cookie lastAccessTime
7. 統計站內各個頁面的訪問次數
8. 實現某些資源只能被某些ip訪問: ->攔截 -> 檢查ip -> 決定是否放行
9. 實現防盜鏈功能: 針對全部的下載頁面
10. 實現html標籤轉義、過濾敏感詞彙
思路:
包裝request對象
包裝 getParameter 方法
將被包裝對象的 getParameter 方法返回的數據進行轉義後再返回
Lesson 20
1、 事件監聽
1. 在程序中常常會用到事件監聽機制
2. 關鍵字:
事件: 用戶的一個操做, 能夠是點擊一個按鈕、調用一個方法、建立一個對象
事件源: 發生事件的對象
事件監聽器: 負責監聽發生在事件源上的事件
事件處理器: 監聽器的成員方法,當事件發生的時候會觸發對應的處理器(成員方法)
3. 事件處理機制
1) 將監聽器綁定到事件源 (註冊監聽器)
2) 事件發生觸發監聽器的成員方法,即事件處理器,傳遞事件對象
3) 事件處理器經過事件得到事件源,進行處理
4. 作事件監聽通常都須要作兩件事情
1) 寫一個類實現監聽器接口
2) 將監聽器註冊到事件源上
2、 servlet事件監聽器
1. 在Servlet 技術中主要有三類事件監聽器:
1) 監聽三個域對象的建立和銷燬 application、session、request
ServletContextListener、HttpSessionListener、ServletRequestListener
三個監聽器都是接口類型
應用: ServletContextListener(監聽application) 用戶作初始化工做和資源釋放
HttpSessionListener(監聽Session) 能夠用於統計在線人數
ServletRequestListener(監聽servlet) 可用於統計訪問次數
public interface ServletContextListener
{
// 事件處理器
Init()
Destroy()
}
web應用啓動時, web 容器會將全部的監聽器都實例化,並綁定到對應的事件源上
2) 監聽三個域對象中屬性的變化(增長、刪除和替換)
ServletContextAttributeListener,
HttpSessionAttributeListener
HttpServletRequestAttributeListener
處理器
attributeAdded
attributeReplaced
attributeRemoved
3) 感知對象被綁定到session域
HttpSessionBindingListener
該接口由javabean對象來實現
該監聽器不須要註冊
案例: 統計來訪者的ip ServletRequestListener
統計在線人數 HttpSessionListener
統計在線用戶
1. HttpSessionAttributeListener
當有屬性增長了 getName 判斷是否是user屬性
若是是 說明有人登錄了 加1
當有屬性移除了 getName 判斷是否是user屬性
若是是 說明有人註銷了 減1
2. HttpSessionBindingListener
session定時掃描器 HttpSessionListener
Lesson 22
大部分的web應用都須要集成郵件發送功能
在 internet 網上發送和接收郵件都必須經過一個專門服務器,處理郵件的服務器咱們稱爲郵件服務器
如今不少門戶網站都擁有本身的郵件服務器,例如:sina、sohu、163等
電子郵箱是指用戶在郵件服務器上申請的帳戶,
郵件服務器會爲每一個帳戶分配必定的空間用於存儲發送和接收的郵件
咱們發送一封電子郵件就須要將郵件發送給對方電子郵箱所在的服務器,對方能夠等待服務器將信件送到郵箱或直接去服務器上收取郵件
在互聯網上任何數據的傳輸都須要遵照協議,好比ie與服務器的數據交互遵循的是http協議
郵件在發送過程當中也須要遵照必定的協議
1)用戶發送一封電子郵件須要遵循 SMTP 協議
ehlo 主機名 ehlo
auth login //通過base64編碼後的用戶名和密碼
mail from:<aaa@itcast.cn>
rcpt to:<bbb@itcast.cn>
Data .號表明郵件內容的結束
quit
2)用戶接收一封電子郵件須要遵循 POP3 協議
user<SP>username<CRLF>
pass<SP>password<CRLF>
stat<CRLF> 返回郵箱的統計信息
list<SP>[msg#]<CRLF>返回某一封郵件的統計信息
retr<SP>msg#<CRLF> 最重要的一個命令 接收指定編號的郵件
quit<CRLF>
3)郵件服務器也會根據功能的不一樣分爲接收郵件的服務器和發送郵件的服務器
發送郵件的服務器咱們習慣稱爲SMTP 服務器 默認監聽25端口
接收郵件的服務器咱們習慣稱爲 POP3 服務器 默認監聽110端口
4) 新浪的一個電子郵箱給搜狐的一個電子郵箱發送郵件的過程以下圖所示
資料
新浪服務器 POP3服務器: pop3.sina.com,SMTP服務器: smtp.sina.com
搜狐服務器 pop3.sohu.com smtp.sohu.com
1) 安裝一臺易郵服務器,新建兩個郵件帳號
註冊的帳號是 zhangsan
電子郵箱地址: zhangsan@itcast.cn
2) 在dos命令行手動輸入命令完成發送郵件(smtp)和接收郵件(pop3)
提示: 用戶名和密碼須要使用base64編碼
String username = "aaa";
String password = "123456";
BASE64Encoder encoder = new BASE64Encoder();
System.out.println(encoder.encode(userName.getBytes()));
System.out.println(encoder.encode(password.getBytes()));
3) 經過RFC822文檔實現發送一封簡單郵件(不安全.已淘汰)
該文檔規定了如何寫一封簡單郵件
文檔中規定郵件分爲郵件頭和郵件體兩部分,兩部分須要使用一個空行來分隔,郵件以一個’.’結束
郵件頭
from 指定發件人
to 指定收件人
subject 指定主題
cc\bcc 指定抄送和密抄
郵件體
xxxxx
例:
ehlo
auth login
YWFh
MTIzNDU2
mail from:aaa@itcast.cn
rcpt to:bbb@itcast.cn
Data
from<aaa@itcast.cn>
to<bbb@itcast.cn>
subject<a mail>
xxxxxxxxxxxx
.
RFC822文檔有漏洞,能夠冒名發送郵件
3) 配置outlook軟件 完成郵件的發送和接收
1. Mime 協議
2. javamail
Session 與郵件服務器的會話
Message(抽象類)-à MimeMessage(Message的子類) ---à 用於封裝Mime消息
Multipart(抽象類)-à MimeMultipart(Multipart的子類) ---à 用於封裝Mime消息體
Bodypart(抽象類)-à MimeBodyPart(Bodypart的子類) ---à 用於封裝Mime消息體個部分數據
Transport 用於發送郵件