對Servlet的理解和認識
Servlet是在伺服器端的應用程式,本身不能單獨執行,需要配合web應用來完成它的功能和使用,目前我們主要使用servlet完成前後端互動以及訪問資料庫,使用servlet與html結合就能夠搭建出一個完整的web應用專案。可見它的功能還是很強大的。但是servlet儘管很強大,用它去搭建web專案時會感覺很吃力,比如伺服器端的servlet處理完一個請求時,需要對客戶端完成響應,有的時候需要在後臺程式碼中完成html程式碼內容的編寫,很不方便。而且一個servlet只能對映到一個類中。當web專案的功能很複雜時,需要配置更多的servlet容器完成相應的功能。
servlet的主要工作流程:我們在客戶端發出servlet請求到伺服器,伺服器識別這個請求傳送到servlet進行處理,servlet完成處理後傳送響應到伺服器,伺服器根據http協議再講響應告訴客戶端,完成流程。
一:單獨的java專案與結合tomcat伺服器完成servlet的使用:
1. 使用servlet的jar包:servlet-api.jar,在資源網站中可以輕易的下載得到。
2. 這裡以eclipse為例,建立一個java project專案,匯入jar包(右擊專案-properties-java build path-libraries-add external jars)。
3. 建立HelloServlet類,繼承HttpServlet。該類下提供了doGet(處理get請求)、doPost(處理post請求)、service(自動識別請求進行處理,常使用的方法)等主要使用的方法,在該方法中有兩個引數分別是request和response。通過以下程式碼向客戶端完成相應。在客戶端中列印Hello Servlet和當前時間。
public void doGet(HttpServletRequest request, HttpServletResponse response){ try { response.getWriter().println("<h1>Hello Servlet!</h1>"); response.getWriter().println(new Date().toLocaleString()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
4. 在專案下配置web.xml檔案,新建該檔案在web - WEB-INF - web.xml。完成以下配置:
<web-app>
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
servlet-name就是給它起得名字,servlet-class就是對應的類,servlet-mapping是對name=HelloServlet這個servlet的對映,url-pattren代表的是當遇見/hello這個請求的時候,就交給這個servlet去處理。
5. 配置伺服器讀取的編譯檔案classes:
在WEB-INF下建立classes資料夾,然後專案右鍵->properties->Java Build Path->Source->右下角的 Brower-> 指定位置是 j2ee/web/WEB-INF/classes。點選OK,因為tomcat啟動之後會自動去這個路徑找classes檔案。
6. 配置tomcat:
將tomcat的sever.xml檔案開啟後,在下圖所示位置新增:
<Context path="/" docBase="e:\\project\\j2ee\\web" debug="0" reloadable="false" />
告訴tomcat應該如何訪問這個專案以及把這個專案的路徑告訴tomcat
7. 啟動tomcat,訪問localhost:8080/hello就可以看到servlet完成的響應內容了
二: 對於servlet的一些方法和使用規則介紹
1. 如何獲取請求帶來的引數:
我們通常可以在html頁面中通過form表單或者ajax向伺服器發出請求,點選登入時,會發送一個方法為post的login請求,此時也就是會把位址列變為:localhost:8080/login?name=&password=進行訪問伺服器,此時伺服器會找web.xml中是否有轉向該servlet請求的類。
注:post請求時:位址列不會顯示引數,get請求時:位址列會顯示引數。不寫時預設為get請求。
<body>
<form action="login" method="post">
賬號: <input type="text" name="name"> <br>
密碼: <input type="password" name="password"> <br>
<input type="submit" value="登入">
</form>
</body>
web.xml中配置該servlet,並新建Login類,在該類中:
public class Login extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
String password = request.getParameter("password");
System.out.println("name:" + name);
System.out.println("password:" + password);
}
}
通過request.getParameter(“引數的名字”)獲取引數的值。此時我們就能在tomcat的console中看到列印的引數值了。這個請求的流程強調一下,理解很關鍵:客戶端通過form提交post請求---伺服器在web.xml中發現了/login對應的servlet---跳轉到Login類中找尋doPost方法,在該方法中處理。
2. servlet如何對客戶端的請求作出相應:
那我們再增加一個判斷,在Login的doPost方法中判斷一下客戶端傳來的賬號密碼是否正確,然後告訴客戶端你輸入的對不對。
通過response物件寫入html內容,完成響應。程式碼如下:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
String password = request.getParameter("password");
String html = null;
if ("root".equals(name) && "root".equals(password))
html = "<div style='color:green'>success</div>";
else
html = "<div style='color:red'>fail</div>";
PrintWriter pw = response.getWriter();//通過response的getWriter建立PrintWriter物件
pw.println(html);//根據這個response生成html 字串,然後再通過HTTP協議,把這個html字串,回發給瀏覽器,瀏覽器再根據HTTP協議獲取這個html字串,並渲染在介面上。
}
此時客戶端中回顯示:
3. service()方法:service方法會自動識別post或者get請求,一般都用這個的多。
4. 中文亂碼問題:html頁面中:<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
java程式碼中:如果是引數是中文的,對引數進行編解碼設定:request.setCharacterEncoding("UTF-8");
如果要返回中文內容:設定html內容格式:response.setContentType("text/html;charset=UTF-8");
就完成了中文亂碼問題。
5. servlet的生命週期:
在此週期中:構造方法(例項化)和init()(初始化)方法都只會執行一次 ,然後在呼叫service方法,完成之後呼叫destory()摧毀,變成可回收物件等待GC回收。
6. 跳轉: 準備兩個html頁面,success和fail,內容只顯示成功和失敗兩個字就好。我們用來驗證登入的賬號密碼是否正確然後完成跳轉。servlet的跳轉分為伺服器跳轉和客戶端跳轉。
伺服器跳轉:在service方法中判斷完了之後,直接在伺服器中調到指定的檔案中,然後響應在客戶端中。
客戶端跳轉:service方法中判斷完了,告訴客戶端下一步去訪問誰,客戶端收到該響應後,再次去訪問伺服器,伺服器再把它訪問的頁面返回給它。
程式碼如下:
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
String password = request.getParameter("password");
if ("admin".equals(name) && "admin".equals(password)) {
request.getRequestDispatcher("success.html").forward(request, response);//這是伺服器端跳轉
}
else{
response.sendRedirect("fail.html");//客戶端跳轉,重定向
}
}
7. servlet的自啟動:隨著tomcat的啟動,自動初始化。即訪問init()方法。完成一些初始化的工作。
告訴伺服器誰需要自啟動:在web.xml檔案中新增<load-on-startup>10</load-on-startup>,10代表啟動的優先順序,越小越高。
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>HelloServlet</servlet-class>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
在該類中重寫public void init(ServletConfig config) {業務程式碼}
8. 其他:
response的301或者302客戶端跳轉、response設定不使用快取、如何上傳檔案等方法,敬請了解。
二:動態WEB專案:
1. 建立,導包:建立Dynamic Web Project,匯入jar包到WebContent/WEB-INF/lib 路徑下,WEB-INF建立web.xml檔案,配置tomcat並部署在tomcat中。啟動tomcat。
注:類檔案會被輸出到build裡,而不是WEB-INF/classes目錄下;
WebContent會被整個複製到 E:\project\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\j2ee 這個位置下面去,Eclipse中啟動的tomcat其實是訪問的這個位置。所以當WebContent裡的內容比較多的時候,就會花較長時間複製;
tomcat啟動失敗:可能是埠被佔用或者該tomcat已經啟動了;
選好tomcat路徑後無法點選finish,解決辦法:
如何將普通的java project專案變為動態web專案:
右鍵專案j2ee->properties->Project Facets->Convert to faceted form...
勾選Dynamic Web Module
勾選之後,會出現 Furthe configuration available ..., 點選
動態web專案的預設內容目錄是WebContent,而 j2ee這個專案的對應目錄是 web, 所以這裡要輸入web
OK
三:使用servlet完成與資料庫的互動。也就是對資料庫進行增刪改查。
我們建立一個小demo,以增加英雄為例,首先我們在本地資料庫(用的mysql)中建立test資料庫,並新建表hero,分別有以下欄位,id為自增長。
我們通過html頁面完成對該資料庫表的操作。
1. 新建addHero.html頁面。簡單設定為姓名,血量,傷害input框,並設定新增按鈕提交form表單,form表單action指向AddHero
2. 設定web.xml新增AddHero的servlet的配置,映射向AddHero.java,然後我們需要新建Hero.java實體類,定義id/name/hp/damage/並且有get/set方法。然後新建HeroDao類,提供JDBC資料庫連線、增刪改查SQL語句的執行。具體HeroDao.java如下:
package dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import bean.Hero;
public class HeroDao {
//無參構造方法,在例項化的時候就已經載入了JDBC類。
public HeroDao() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO: handle exception
e.printStackTrace();
}
}
//設定公共方法連線資料庫
public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
}
//獲取總條數
public int getTotal() {
int total = 0;
try(Connection c = getConnection();Statement s = c.createStatement()) {
String sql = "select count(*) from hero";
ResultSet rs = s.executeQuery(sql);
while(rs.next()) {
total = rs.getInt(1);
}
System.out.println("total:" + total);
} catch (Exception e) {
// TODO: handle exception
}
return total;
}
//新增
public void add(Hero hero) {
String sql = "insert into hero values(null,?,?,?)";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, hero.name);
ps.setFloat(2, hero.hp);
ps.setInt(3, hero.damage);
ps.execute();
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
int id = rs.getInt(1);
hero.id = id;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
//修改
public void update(Hero hero) {
String sql = "update hero set name= ?, hp = ? , damage = ? where id = ?";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, hero.name);
ps.setFloat(2, hero.hp);
ps.setInt(3, hero.damage);
ps.setInt(4, hero.id);
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
}
}
//刪除
public void delete(int id) {
try (Connection c = getConnection(); Statement s = c.createStatement();) {
String sql = "delete from hero where id = " + id;
s.execute(sql);
} catch (SQLException e) {
e.printStackTrace();
}
}
//根據ID獲取某條資料
public Hero get(int id) {
Hero hero = null;
try (Connection c = getConnection(); Statement s = c.createStatement();) {
String sql = "select * from hero where id = " + id;
ResultSet rs = s.executeQuery(sql);
if (rs.next()) {
hero = new Hero();
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
hero.name = name;
hero.hp = hp;
hero.damage = damage;
hero.id = id;
}
} catch (SQLException e) {
e.printStackTrace();
}
return hero;
}
//配合下面有參的方法完成對資料庫列表的查詢
public List<Hero> list() {
return list(0, Short.MAX_VALUE);
}
public List<Hero> list(int start, int count) {
List<Hero> heros = new ArrayList<Hero>();
String sql = "select * from hero order by id asc limit ?,? ";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setInt(1, start);
ps.setInt(2, count);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
Hero hero = new Hero();
int id = rs.getInt(1);
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
hero.id = id;
hero.name = name;
hero.hp = hp;
hero.damage = damage;
heros.add(hero);
}
} catch (SQLException e) {
e.printStackTrace();
}
return heros;
}
}
3. 在AddHero.java中,整合httpservlet類,方法service,獲取前臺input傳來的引數,放在herod物件中,將該物件作為引數放入HeroDao的add方法中,執行該方法。完成資料庫的新增。最終客戶端跳轉到heroList頁面中。顯示已經新增的資訊。程式碼如下:
public class AddHero extends HttpServlet {
protected void service(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
Hero hero = new Hero();
hero.setName(request.getParameter("name"));
hero.setHp(Integer.parseInt(request.getParameter("hp")));
hero.setDamage(Integer.parseInt(request.getParameter("damage")));
HeroDao hd = new HeroDao();
hd.add(hero);
response.sendRedirect("/j2ee_1/heroList");
}
}
4. heroList.java中,用table將資料庫中的資料展示出來,用servlet寫html頁面內容,傳給客戶端進行顯示,程式碼如下:
public class HeroList extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {
response.setContentType("text/html;charset=UTF-8");
List<Hero> heros = new HeroDao().list();//獲取資料庫中所有資料列表
StringBuffer sb = new StringBuffer();//完成html頁面內容設定
sb.append("<table align='center' border='1' cellspacing='0'>\r\n");
sb.append("<tr><td>id</td><td>name</td><td>hp</td><td>damage</td><td>刪除</td><td>修改</td></tr>\r\n");
//佔位符的使用%d%s%f...
String trFormat = "<tr><td>%d</td><td>%s</td><td>%f</td><td>%d</td><td><a href='delHero?id=%d'>刪除</a></td><td><a href='editHero?id=%d'>修改</a></td></tr>\r\n";
//將資料庫中的資料遍歷顯示在table中。
for (Hero hero : heros) {
String tr = String.format(trFormat, hero.getId(),hero.getName(),hero.getHp(),hero.getDamage(),hero.getId(),hero.getId());
sb.append(tr);
}
sb.append("</table>");
//servlet告訴客戶端顯示這些東西。
response.getWriter().write(sb.toString());;
}
}
注:href="delHero?id=%d"herf也可以請求servlet。
4. 刪除、修改內容同理,這就是使用servlet完成對資料庫的操作。
5. servlet還可以將html頁面中的json資料傳遞到後臺,以及將後臺中的json資料傳送到頁面中顯示,以物件的形式傳遞。
6. 接下來學習使用jsp。
本人宣告:以上學習來自how2j.cn的學習筆記。如有侵權,請聯絡本人刪除。