1. 程式人生 > >對Servlet的理解和認識

對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的學習筆記。如有侵權,請聯絡本人刪除。