C/S模式的軟件 客戶端/服務器 比如QQ,或者一些大型游戲
用戶需要先下載軟件的客戶端,然后才可以使用.
B/S模式的軟件 瀏覽器/服務器 我們上網時候所訪問網站
的基本全是B/S模式的項目.用戶不需要下載任何東西,只需要
用到瀏覽器就可以訪問到這個項目.
我們在java中要學習的web項目,就是B/S架構的項目.
web項目中,其實就是倆個軟件之間的信息交換,客戶端機器
中的瀏覽器軟件和服務器端機器中的web服務器軟件之間的
交換,比如在我們的學習中,就是客戶端的瀏覽器和服務器端
的tomcat進行信息交換。
瀏覽器和tomcat是倆個不同的軟件,但是在開發(fā)這倆個軟件
的時候,都是加入對http協議的支持,所有它們倆個進行信
息交換的時候,是通過http協議規(guī)定的方式進行交換的:
客戶端先發(fā)送一個請求,然后服務器端接收到請求后再返
回給客戶端一個響應,并且把客戶端所請求的資源返回給客戶端.
我們要做的時候就是,開發(fā)一個web項目,然后把這個web項目
放到tomcat里面指的的位置,然后再運行這個tomcat軟件,
在tomcat運行過程中,其他計算機里面,只要是網絡和我們這
個服務器是通的,那么都可以通過瀏覽器來訪問我們這個web項目恐仑。
在用瀏覽器訪問tomcat的時候,tomcat會按照瀏覽器發(fā)送的
這個請求里面的內容,把瀏覽器想訪問的頁面從web項目中找
到并返回給這個瀏覽器,或者是tomcat去調用web項目所寫
的某些java類中的方法(其實就調用servlet中的方法)
注意:web項目中,任何一個類里面都不需要寫main方法,整個
項目的運行,對象的創(chuàng)建,方法的調用,都是由tomcat處理的,
我們不需要去管. tomcat根據客戶端的要求,會調用某個
servlet中的指定方法,然后在這個指定方法里面,我們可
以寫上調用我們自己寫的某個類的方法,類似的這樣方法相
互調用下去,最后調用到我們sql語句的執(zhí)行,這樣我們的
一個功能就走完了,比如說登錄或者注冊功能。
一個web項目最基本的項目結構:
項目名字:web_test
1.文件夾web_test,項目名字是隨便起的
2.web_test文件中有文件WEB-INF,WEB-INF這個名字是固定的
3.WEB-INF文件夾中:classes文件夾,lib文件夾,web.xml文件,這個名字都是固定的两入。
web.xml文件在tomcat目錄中的conf目錄里面有一個web.xml模板.
注意:
web_test文件夾下面還可以放頁面,css樣式、js文件敲才、圖片,html等等.
classes文件夾中放的是編譯后的.class文件
lib文件夾中放的是當前項目中運行所需要的jar包(私有裹纳,本項目可以使用的)
tomcat服務器:
bin 目錄:啟動、關閉tomcat相關的命令紧武。
conf目錄:tomcat的配置文件
lib 目錄:tomcat中可以同時部署多個項目,多個項目都需要用到的相同jar包,就可以放在這個目錄剃氧。
logs目錄:記錄tomcat日常運行的情況或者錯誤信息养盗。
temp目錄:tomcat運行期間可能會產生一些臨時文件,這寫臨時文件就會放在這個目錄.
webapps目錄:我們開發(fā)好的web項目就是需要放到這個指定的目錄下面,默認里面已經有一些tomcat自帶的項目樣例了.
work目錄:編譯jsp頁面時候需要用到的一個目錄.
jsp編譯成的java代碼及字節(jié)碼存放的位置
session的序列化產物寡夹。
訪問tomcat中項目里面的資源的方式:
http://IP:port/項目名字/項目在資源
http://localhost:8080/web_test/test.png
http://localhost:8080/web_test/form.html
localhost:8888/jd1812_web/images/test.png
localhost:8888/jd1812_web/css/test.css
手寫一個servlet并且放到tomcat中去運行:
package com.briup.test;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.PrintWriter;
public class FristServletTest extends HttpServlet{
public void doGet(HttpServletRequest req,HttpServletResponse resp)throws ServletException,IOException{
PrintWriter out = resp.getWriter();
out.println("hello world");
out.flush();
out.close();
}
}
servlet-api.jar在tomcat的lib目錄中
servlet-api.jar和FristServletTest.java都在桌面
編譯java文件所在的命令窗口的路徑也在桌面
javac -cp servlet-api.jar -d . FristServletTest.java
編譯完之后,把這個.class文件已經包目錄,放到項目中
的classes目錄里面,然后再項目中的web.xml文件進行
配置,配置的目的是:想把一個路徑映射成我們自己一個
servlet,這個我們在瀏覽器里面就可以用個這個映射
的路徑來來訪問這個servlet了.
瀏覽器的地址欄中只能提供類似于這樣的信息:
主要是以路徑的形式提供訪問的信息:
/.../.../.../...
http://172.16.4.81:8002/WebTest/hello.html
但是我們需要的是這樣的信息:
要運行servlet至少要有servlet的對象,所有我們需要這個類的package.類名
com.briup.test.FristServletTest
所以我們需要把一個路徑映射成一個servlet:
比如:把/first路徑映射成FristServletTest這個servlet,將來用戶在瀏覽器中輸入/first路徑之后,就相當于告訴服務器,需要運行FristServletTest這個servlet.
例如:
web.xml文件中加入:
<servlet>
<servlet-name>firstservlet</servlet-name>
<servlet-class>com.briup.test.FristServletTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>firstservlet</servlet-name>
<url-pattern>/first</url-pattern>
</servlet-mapping>
<servlet>中的<servlet-name>子標簽:
給你要描述的servlet起一個名字,這個是名字是隨便起的,目的是為了在下面做映射的時候能夠再找到這個servlet
<servlet>中的<servlet-class>子標簽:
這個servlet的package.類名
<servlet-mapping>中的<servlet-name>子標簽:
指明這個映射是給哪一個servlet的做的映射,通過之前隨便起的servlet的名字找瞻佛。
<servlet-mapping>中的<url-pattern>子標簽:
這個就是我們要映射的路徑,將來瀏覽器里面輸入這個路徑就相當于要訪問我們這個servlet规婆。
注意:這個路徑一定是以/開頭的:/a /a/b /a/b/hello
servlet
1.什么是servlet
servlet其實就是java里面一種java類,只不過這種java類有一些特殊的作用,所以我們就叫它servlet
2.servlet的作用
可以讓瀏覽器直接通過一個路徑去訪問。(一般的java類是做不到這一點的)
3.怎么寫一個類,能成為一個servlet
三種方式:
a.實現一個接口:javax.servlet.Servlet
b.繼承一個抽象類:javax.servlet.GenericServlet
c.繼承一個抽象類javax.servlet.http.HttpServlet
這種是之后一直要用的方法.
注意:Servlet接口中,有五個抽象方法,其實最重要的一個方法是:service(ServletRequestreq,ServletResponse res)
抽象類GenericServlet實現了接口Servlet,但是留了一個抽象方法沒有實現,就是上面所提到的service方法,除此之外,GenericServlet里面還增加了一些新的方法.
抽象類HttpServlet,繼承了GenericServlet,但是HttpServlet中沒有任何抽象方法,除了從父類繼承過來的方法之外,里面還有很多新的方法:doXxx方法,另外需要注意的是,HttpServlet里面有倆個service方法,但是倆個方法的參數類型不同.
i.Servlet接口中的service方法.
因為無論我們使用哪一種方式去寫出來的一個servlet類的中,一定會有一個方法叫做service,這個方法是Servlet接口中的定義的,tomcat服務器接收到客戶端請求后,幫我們調用servlet中的方法的時候,它只會調用這一個service方法.
注意這個service方法中參數的類型:
service(ServletRequestreq,ServletResponse res)
ii.GenericServlet中的倆個init方法
帶參數的init(ServletConfig config)方法是從Servlet接口中實現的,還有一個是這個類直接定義的,無參的init()方法.
tomcat服務器創(chuàng)建servlet對象的時候,會幫我們去調用init(ServletConfig config)進行servlet類中的初始化工作,所以如果我們想在servlet類中初始化自己的一些相關數據的話,需要去重寫這個init方法酷窥。有參數的init方法中調用了無參的init方法,所以將來我們只需要重寫這個無參的init方法就可以了。這樣我們就不需要再專門對這個init(ServletConfig config)方法中的參數進行處理了。
iii.HttpServlet中的倆個service方法
這個是從Servlet接口中實現的方法.
service(ServletRequest req, ServletResponse res){
//強轉參數類型
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
//調用新定義的service方法
service(request,response);
}
HttpServlet中新定義的方法
service(HttpServletRequest req, HttpServletResponse resp){
//拿到當前請求的類型 get/post
String method = req.getMethod();
//判斷請求的方法 然后去調用doXxx方法
if(method.equals("GET")){
doGet(req,resp);
}
if(method.equals("POST")){
doPost(req,resp);
}
...
..
}
http://ip:port/項目名字/資源路徑
http://127.0.0.1:8002/web_test/servlet1
4.servlet的生命周期
servlet的對象是單例:在web項目整個運行期間,每一個servlet類只會有一個對象存在.默認情況下,這一個對象是在servlet第一次被訪問的時候才會創(chuàng)建的,而且之后不管再訪問多少次這個servlet,也不會再去創(chuàng)建它的的對象的.(我們可以設置某一個servlet的對象在服務器啟動的時候就被創(chuàng)建出來)
這些操作(方法的調用)都是由服務器來做的:
1.默認情況下,第一次被訪問的時候tomcat創(chuàng)建servlet對象(調用無參構造器)
2.tomcat調用init(ServletConfig config)方法
在servlet對象實例化之后,tomcat服務器會立馬調用這個方法給servlet的實例進行初始化工作矾踱。
3.客戶端訪問的時候,tomcat會調用service(ServletRequest req,ServletResponse res)方法
4.在銷毀servlet對象的時候會tomcat調用destroy()方法.
通過web.xml文件中設置,可以讓servlet的對象是在服務器啟動的時候被創(chuàng)建出來.
web.xml:
<servlet>
<servlet-name>LifeServletTest</servlet-name>
<servlet-class>com.briup.servlet.LifeServletTest</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
加入<load-on-startup>標簽,表示這個servlet需要在啟動服務器的時候就被創(chuàng)建對象.其中,標簽可以要放一個數字,這個數字的大小就決定了多個servlet對象被創(chuàng)建的順序,數字越小越先創(chuàng)建.(如果我們配置了多個servlet在啟動服務器的時候被創(chuàng)建對象的話)
5.servlet的線程安全問題
問題產生的原因:
1.servlet是單例,一個servlet類只有一個對象在項目運行期間。
2.web項目項目本身就有多線程的特點,雖然我們在代碼中沒有寫和線程相關的東西,但是這個多線程的特點是由服務器給我們提供出來的,一個客戶端發(fā)送了一個請求,服務器接收到請求后就會建一個線程去處理這個請求疏哗。
所以就有可能出現這樣的情況:倆個線程同時去訪問同一個servlet對象.
如何解決/防止
1.加鎖synchronized
2.盡量少的定義成員變量
因為只有成員變量才會產生線程安全的問題(在多個線程訪問同一個對象的時候),方法中的局部變量是沒有這樣的問題的.
3.其他(實現一些安全性的接口)
6.客戶端向服務器發(fā)請求并且傳參(get/post)
客戶端向服務器發(fā)送請求可以是get方式也可以是post方式.所以傳參也分為get方式下傳參和post方式下傳參.
1.哪些方式的請求屬于get/post方式
get方式:
a.瀏覽器中輸入地址(URL)然后回車
b.超鏈接
c.頁面中引入的css樣式文件
d.頁面中引入的js的文件(javascript)
e.<img src="image/a.jpg" />
f.form表單中method="get"
g.ajax中可以設置異步提交請求的方式為get
f.其他
post方式:
a.form表單中method="post"
b.ajax中可以設置異步提交請求的方式為post
c.其他
2.get和post的特點及其區(qū)別
它們各自的特點及其區(qū)別主要是體現在所傳遞的參數上面呛讲。
a.get方式參數
參數是直接放在要請求url后面的
例如:
url?參數名=參數值&參數名=參數值
要請求的url為:
http://ip:port/WebTest/firstServlet
傳參:
.../firstServlet?name=tom
.../firstServlet?name=tom&age=20
這種方式傳參的特點:
1.參數直接放在url后面
2.從瀏覽器的地址欄里面可以直接看到所傳的參數
3.參數的長度有限制,不能把一個很長的數據通過get方式傳參.(與瀏覽器種類有關)
b.post方式傳參
參數是放在請求的體部的。
(瀏覽器發(fā)送的請求可以大致的分為請求頭+請求體)
這種方式傳參的特點:
1.參數放在請求的體部而不是url后面.
2.瀏覽器的地址欄中看不到所傳的參數.
3.因為參數不用出現在地址欄里面,所有參數長度是沒有限制的.
7.servlet中接收客戶端傳過來的參數返奉。
客戶端都是以這樣的形式傳參的:
參數名字=參數值
所有在servlet中我們需要用參數名字來拿到參數值:
String value = request.getParameter("key");
其中key就是參數名字,value是參數值,不管傳的值本身是什么類型,servlet中接收到以后只能是字符串類型或者字符串類型數組.
如果客戶端用的多選框,這個時候傳過來的參數就要用一個字符串類型數組來接收:String[] like = req.getParameterValues("like");
在表單中,參數值就是用戶所填寫的內容或者是所選的選項的value屬性值,參數名字就是每一個input或者其他輸入元素中的name屬性的值.
例如:
<input type="text" name="username">
參數名字就是name屬性的值:username
參數的值就是將來用戶所填內容.
在servlet中,不管是get方式提交的時候還是post方式提交的時候,來到servlet里面以后,都是用相同的方式來取得數據贝搁。
request.getParameter("key");
request.getParameterValues("key");
8.中文亂碼
a.get方式提交數據,servlet拿到后亂碼
需要修改tomcat中的配置文件,然后重新啟動tomcat服務器.
server.xml:在這個文件中找到修改端口號的那個標簽,然后加入一個新的屬性URIEncoding="UTF-8",或者是寫GBK、GB2312
例如:
<Connector connectionTimeout="20000" port="8002" protocol="HTTP/1.1" redirectPort="8443"
URIEncoding="GBK"/>
b.post方式提交數據,servlet拿到后亂碼
在代碼中,我們去接收數據之前,也就是調用getParameter方法之前,需要先轉換一下接收數據的編碼:
req.setCharacterEncoding("GBK");
里面的值可以是GBK芽偏、UTF-8雷逆、GB2312
注意:其實不管是get方式或者是post方式提交數據,我們最后是在Eclipse/MyEclipse中打印了出來,所以我們最后還要看看工具中是用什么編碼顯示的我們的數據的.點擊一個文件或者項目名字后右鍵,properties選項中可以看到這個編碼.
c.servlet向瀏覽器返回數據,瀏覽器顯示亂碼.
在代碼中,獲得輸出流之前,我們要先設置輸出流是用什么編碼把數據寫回去的:
resp.setCharacterEncoding("GBK");
同時我們還可以通知瀏覽器我們向它傳輸的數據是用的什么樣的編碼:
resp.setContentType("text/html;charset=GBK");
注意:在我們使用的瀏覽器中,也是可以調整編碼的設置的,就是我們可以自己選擇一種編碼來顯示當前頁面的內容,同時瀏覽器也會有自己一個默認的顯示編碼.當瀏覽器不知道服務器傳過來的的數據是用什么編碼的時候,瀏覽器會用默認的編碼來顯示.
9.servlet中的跳轉.
a.服務器內部跳轉
1.跳轉到一個頁面
2.跳轉到另外一個servlet
page="頁面/servlet"
//獲得一個跳轉對象(服務器內部跳轉)
RequestDispatcher rd = req.getRequestDispatcher(page);
//跳轉
rd.forward(req,resp);
特點:
1.不管servlet服務器內部跳轉多少次,瀏覽器地址欄中顯示的地址都是第一次訪問到的servlet的地址
2.服務器內部跳轉其實就是把request和response對象進行轉發(fā),轉發(fā)到另外一個服務器內部的一個資源中,如果轉發(fā)的對象是一個頁面,那么就把這個頁面返回給瀏覽器,如果轉發(fā)的對象是另外一個servlet,那么會調用到這個servlet里面的service方法,然后把之前那個request和response當做傳送傳進來.
3.在服務器內部跳轉的過程中,每一個環(huán)節(jié)中所用到的request和response對象都是同一個request和response對象.
b.客戶端重定向
1.跳轉到一個頁面
2.跳轉到另外一個servlet
String page = "頁面/servlet";
resp.sendRedirect(page);
特點:
1.跳轉之后,瀏覽器中顯示的是跳轉到的那個資源(頁面/servlet)的地址,而不是第一次訪問的servlet地址.
2.客服端重定向,其實就是讓客戶端瀏覽器重新再發(fā)一次新的請求到要跳轉的資源。所以在客戶端重定向中我們使用的對象是response,因為response這個對象的作用就是向客戶端傳遞數據傳遞消息用的污尉。
3.在客戶端重定向的過程中,每個環(huán)節(jié)中所用到的request,response對象都是瀏覽器新發(fā)出的膀哲。