Java Web 基礎筆記(更新中)

Copyright © 2020 Linyer. All Rights Reserved.

初識JSP

JSP 輸出 和 註釋

  • Java代碼寫在<% %>裏面,稱爲Java小腳本。
  • 設置 JSP 頁面屬性:
<%@ page %>
  • 在頁面輸出一句話:
<% out.print("<h1>Java Web 筆記!</h1>"); %>
  • 變量輸出:
<% String head = "Java Web 筆記!" %>
<h1><%=head%></h1>
  • HTML 註釋:
<!-- html註釋 -->
  • JSP 註釋:
<%-- out.print("<h1>Java Web 筆記!</h1>"); --%>
  • JSP 中腳本註釋:
<% 
//單行註釋!
/*多行註釋!*/
%>

JSP 中使用變量

<%@ page import="java.text.SimpleDateFormat" %>
<%
	String title = "Java Web 筆記!";
	//當前系統時間
	Date date = new Date();
	//格式化輸出時間
	SimpleDateFormat fromater = new SimpleDateFormat("yyyy-MM-dd");
	String time = fromater.format(date);
%>
<h1><%=title %></h1>
<h1>當前時間爲:<%=time %></h1>

JSP 聲明全局變量 和 方法

  • 全局變量每刷新一次都加1
<h1><%=title %></h1>
當前時間爲:<%=time %>
<%
	//局部變量
	int i = 2;
%>
<h1><%=i++ %></h1>
<%!
	//全局變量
	int j = 6;
%>
<h1><%=j++ %></h1>
  • 方法要寫全局形式
<%!
	public int add() {
		return 8+8;
	}
%>
<%=add() %>

Web 程序的錯誤

  • 404 錯誤:找不到訪問的頁面或資源。
  • 500 錯誤:JSP頁面語法有誤。

JSP 實現 數據傳遞 和 保存

獲取請求中的數據

  • 發送請求頁面 Regist.jsp
    • 外面包上<form></form>發送數據
<h1 align="center"><font color="green">用戶註冊</font></h1>
<form name="registerFrm" id="registFrm" action="doRegist.jsp" method="get">
<table class="tb" border="0" cellspacing="5" cellpadding="0" align="center">
	<tr>
		<td class="text_tabledatil2">用戶名</td>
		<td><input type="text" name="username"/></td>
	</tr>
	<tr>
		<td class="text_tabledatil2">密碼</td>
		<td><input type="password" name="password"/></td> 
		</tr>	
	<tr>	
		<td class="text_tabledatil2">確認密碼</td>
		<td><input type="password" name="con_password"/></td> 
	</tr>	
	<tr>	
		<td class="text_tabledatil2">e-mail</td>
		<td><input type="text" name="email"/></td> 
	</tr>
	<tr>	
		<td class="text_tabledatil2">愛好</td>
		<td>
			<input type="checkbox" name="hobby" value="游泳"/>游泳<br/>
			<input type="checkbox" name="hobby" value="閱讀"/>閱讀<br/>
			<input type="checkbox" name="hobby" value="爬山"/>爬山<br/>
			<input type="checkbox" name="hobby" value="旅遊"/>旅遊<br/>
		</td>
	</tr>
	<tr>	
		<td style="text-align:center" colspan="2">
			<button type="submit" class="page-btn" name="save">註冊</button>
		</td>
	</tr> 		
</table>
</form>
  • 方法:
public String getParameter(String name)
  • 接受請求頁面 doRegist.jsp
<%
   	String userName = request.getParameter("username");
   	String pwd = request.getParameter("password");
   	String email = request.getParameter("email");
   	String[] hobby = request.getParameterValues("hobby");
%>
用戶名:<%=userName %><br/>
密碼 :<%=pwd %><br/>
email:<%=email %><br/>
用戶愛好: 
<%
	if(hobby!=null&&hobby.length!=0){
	  	for(String hobbys:hobby){
	  		out.print(hobbys+" ");
	  	}
	}else{
  		out.print("沒選愛好!");
  }
%>

轉發 與 重定向

  • request 的作用域:一次請求
  • 保存屬性方法:
public void setAttribute(String name,Object o)

要對 name 做非空判斷!

  • 獲取屬性方法:
public Object getAttribute(String name)
  • 轉發:使用 RequestDispatcher 對象中的 forward() 方法
request.getRequestDispatcher("url").forward(request,response)
  • 重定向:將用戶請求重新定位到一個新的 URL
request.sendRedirect("url")
  • 轉發與重定向的區別
比較項目 轉發 重定向
URL 變化
重新發出請求 不會
是否攜帶請求
目標 URL 要求 僅本 Web 應用 任意 URL
  • doRegist.jsp
if(!pwd.equals(c_pwd)) {
	request.setAttribute("mess","兩次輸入密碼不一致,註冊失敗!");
 	request.getRequestDispatcher("Regist.jsp").forward(request, response);
}else{
   	String info = "sucess";
   	response.sendRedirect("index.jsp?info="+info);
}
  • Regist.jsp
Object omess = request.getAttribute("mess");
	if(omess!=null){
	out.print(omess.toString());
}
  • index.jsp
if(request.getParameter("info").equals("sucess")){
	out.print("<h1 align=\"center\"><font color=\"green\">恭喜你!註冊成功!</font></h1>");
}

JSP 內置對象

  • JSP 已經準備好的,可以直接使用的對象
對象 語句
請求對象 request
輸出對象 out
響應對象 response
應用程序對象 application
會話對象 session
頁面上下文對象 pageContext
頁面對象 page
配置對象 config
異常對象 exception

session 的使用

  • session 作用域:一次會話
  • 會話:一個會話就是瀏覽器和服務器之間的一次通話
  • 會話 可以在多次請求中保存和使用數據
  • 修改 doRegister.jspelse 後面爲:
else{
	session.setAttribute("userName",userName);
	response.sendRedirect("index.jsp");
}

會話的 清除 和 過期

程序主動清除 session 數據

  • 設置會話失效:
session.invalidate();
  • 移除會話的一個屬性:
public void remoteAttribute(String name);
  • 用法:
session.remoteAttribute("userNmae");

服務器主動清除長時間沒有再次發出請求的 session

  • 設置會話過期時間:
  • 方法一:
public void setMaxInactiveInterval(int interval);

設置最大活動時間,單位爲

  • 方法二:
<session-config>
	<session-timeout>30</session-timeout>
</session-config> 
  • 單位爲 分鐘
  • web.xml 中設置!
  • Loginout.jsp
//用戶註銷會話
session.invalidate();
response.sendRedirect("index.jsp"); 

cookie 的使用

  • cookie:跟蹤用戶的整個會話
  • 在客戶端記錄信息,給客戶端發送一個通行證,每個客戶一個。
  • 本質是一小段文本信息。
  • 以文件的形式保存數據。
  • 添加數據
public void addCookie(Cookie cookie);
  • 獲取數據
public Cookie[] getCookie();
  • 設置路徑
cookie.setPath("/");
  • 整個工程無論什麼路徑都能訪問 cookie
  • session 基於 cookie
  • utf-8中文 編碼 解碼
userName = URLEncoder.encode(userName,"utf-8");
userName = URLDecoder.decode(userName,"utf-8");
  • doRegist.jsp
Cookie cookie = new Cookie("userName",userName);
cookie.setMaxAge(60*60);
response.addCookie(cookie); 

application 的使用

  • application 作用域:整個應用程序
  • 統計頁面訪問次數
//統計該頁面的訪問次數
Object count = application.getAttribute("count");
if(count==null){
	//第一次訪問
	application.setAttribute("count", 1);
}else{
	//非第一次訪問
	Integer i = (Integer)count;
	application.setAttribute("count", i+1);
}
Integer icount = (Integer)application.getAttribute("count");
out.print("訪問次數爲:"+icount);

三個對象對比

  • request、session、application
  • 相同點:都可以存儲屬性
  • 不同點:
    • request 中存儲的數據僅在一個請求中可用
    • session 中存儲的數據在一個會話的有效期內可用
    • application 中存儲的數據在整個Web項目中可用

使用 JDBC 操作數據庫

JDBC 使用步驟

  • JDBC API
    • 實現 Java 程序對各種數據庫的訪問
    • 一組接口和類,位於 java.sqljavax.sql
    • 面向接口編程
  • JDBC 步驟固定!
    1. Class.forName(String) 加載驅動
    2. DriverManage 獲取 Connection 連接
    3. 創建 StatemenPrepardStatement 對象執行 SQL 語句
    4. 返回 ResultSet 查詢結果
    5. 釋放資源(先用先關,後用後關)

使用 JDBC 實現查詢

  • 查詢新聞 id、標題
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Statement;

public class NewsDao {
	//查詢新聞id、標題
	public static void main(String[] args) {
		
		//寫在外面,可以在finally裏關
		Connection connection = null;
		Statement stmt = null;
		ResultSet rs = null;
		
		try {
			//Class.forName(String) 加載驅動
			Class.forName("com.mysql.jdbc.Driver");
			//DriverManage 獲取 Connection 連接
			String url = "jdbc:mysql://localhost:9988/kgcnews?characterEncoding=utf8&useSSL=true";
			connection = (Connection) DriverManager.getConnection(url,"root","123456");
			// SQL 語句
			String sql = "select id,title from news_detail;";
			//創建 Statemen對象執行 SQL 語句
			stmt = (Statement)connection.createStatement();
			//返回 ResultSet 查詢結果
			rs = stmt.executeQuery(sql);
			while (rs.next()) {
				int id = rs.getInt("id");
				String title = rs.getString("title");
				System.out.println(id+"\t"+title);
			}
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			//釋放資源
			try {
				rs.close();
				stmt.close();
				connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}						
		}		
	}
}

使用 PrepardStatement

  • StatementPreparedStatement 區別
    • PreparedStatement 接口繼承 Statement
    • Statement st = connection.createStatement();
    • PrepareStatement pstm = connection.prepareStatement(sql);
    • SQL語句使用 “?” 作爲數據佔位符
      使用 setXxx() 方法設置數據

    • PreparedStatement——預編譯
      • 效率、性能、開銷
      • 安全性(PreparedStatement可以防止一定的SQL注入)
      • 代碼可讀性
  • 查詢特定標題的新聞信息
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;

public class NewsDao {
	//查詢特定標題的新聞信息
	public void getNewsByTitle(String title){
		
		//寫在外面,可以在finally裏關
		Connection connection = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try {
			//Class.forName(String) 加載驅動
			Class.forName("com.mysql.jdbc.Driver");
			//DriverManage 獲取 Connection 連接
			String url = "jdbc:mysql://localhost:9988/kgcnews?characterEncoding=utf8&useSSL=true";
			connection = (Connection) DriverManager.getConnection(url,"root","123456");
			// SQL 語句,?叫佔位符
			String sql = "select * from news_detail where title=?";
			//創建PreparedStatement對象執行 SQL 語句
			pstmt = (PreparedStatement) connection.prepareStatement(sql);
			//在sql語句的第一個問號的位置填充title
			pstmt.setString(1, title);
			//返回 ResultSet 查詢結果,不用再傳sql了
			rs = pstmt.executeQuery();
			while (rs.next()) {
				int id = rs.getInt("id");
				String newsTitle = rs.getString("title");
				System.out.println(id+"\t"+newsTitle);
			}
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			//釋放資源
			try {
				rs.close();
				pstmt.close();
				connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}						
		}				
	}
	
	public static void main(String[] args) {
		NewsDao dao = new NewsDao();
		dao.getNewsByTitle("Java Web開課啦");
	}
}

實現數據 增刪改 操作

  • 示例:
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;

//用 JDBC 實現新聞數據 增 刪 改 操作
public class NewsDao{
	Connection connection = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;	
	
	//獲取數據庫連接
	public void getConnection(){
		try {
			Class.forName("com.mysql.jdbc.Driver");
			String url = "jdbc:mysql://localhost:9988/kgcnews?characterEncoding=utf8&useSSL=true";
			connection = (Connection) DriverManager.getConnection(url,"root","123456");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}catch (SQLException e) {
			e.printStackTrace();
		}

	}
	
	//關閉數據庫連接
	public void closeConection(){
		try {
			pstmt.close();
			connection.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	//增加新聞信息
	public void addNews(int id,int categoryid,String title,String summary,String content,String author,Date createdate){
		try {
			this.getConnection();
			String sql = "INSERT INTO news_detail(id,categoryid,title,summary,content,author,createdate) VALUES(?,?,?,?,?,?,?)";
			pstmt = (PreparedStatement) connection.prepareStatement(sql);
			pstmt.setInt(1,id);
			pstmt.setInt(2,categoryid);
			pstmt.setString(3,title);
			pstmt.setString(4,summary);
			pstmt.setString(5,content);
			pstmt.setString(6,author);
			pstmt.setTimestamp(7, new Timestamp(createdate.getTime()));
			int i = pstmt.executeUpdate();
			if(i > 0){
				System.out.println("插入新聞成功!");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			this.closeConection();
		}		
	}
	
	//刪除特定新聞標題
	public void deleteNews(int id){
		try {
			this.getConnection();
			String sql = "DELETE FROM news_detail WHERE id=?";
			pstmt = (PreparedStatement) connection.prepareStatement(sql);
			pstmt.setInt(1, id);
			int i = pstmt.executeUpdate();
			if(i > 0){
				System.out.println("刪除新聞成功!");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			this.closeConection();
		}				
	}
	
	//修改特定新聞標題的方法
	public void updateNews(int id,String title){
		try {
			this.getConnection();
			String sql = "UPDATE news_detail SET title=? WHERE id=?";
			pstmt = (PreparedStatement) connection.prepareStatement(sql);
			pstmt.setString(1, title);
			pstmt.setInt(2, id);
			int i = pstmt.executeUpdate();
			if(i > 0){
				System.out.println("修改新聞成功!");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			this.closeConection();
		}				
	}
	
	//查詢全部新聞信息
	public void getNewsList(String title){
		try {
			this.getConnection();
			String sql = "select id,categoryid,title,summary,content,author,createdate from news_detail";
			pstmt = (PreparedStatement) connection.prepareStatement(sql);
			rs = pstmt.executeQuery();
			while (rs.next()) {
				int id = rs.getInt("id");
				int categoryid = rs.getInt("categoryid");
				String newsTitle = rs.getString("title");
				String summary = rs.getString("summary");
				String content = rs.getString("content");
				String author = rs.getString("author");
				Timestamp createdate = rs.getTimestamp("createdate");
				System.out.println(id+"\t"+categoryid+"\t"+newsTitle+"\t"+summary+"\t"+content+"\t"+author+"\t"+createdate);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			this.closeConection();
		}
	}
	
	//查詢特定標題的新聞信息
	public void getNewsByTitle(String title){
		try {
			this.getConnection();
			String sql = "select * from news_detail where title=?";
			//創建PreparedStatement對象執行 SQL 語句
			pstmt = (PreparedStatement) connection.prepareStatement(sql);
			//在sql語句的第一個問號的位置填充title
			pstmt.setString(1, title);
			//返回 ResultSet 查詢結果,不用再傳sql了
			rs = pstmt.executeQuery();
			while (rs.next()) {
				int id = rs.getInt("id");
				String newsTitle = rs.getString("title");
				System.out.println(id+"\t"+newsTitle);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			//釋放資源
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			this.closeConection();
		}
	}
	
	public static void main(String[] args) {
		NewsDao dao = new NewsDao();
	//	dao.addNews(3, 1, "test", "test", "test", "Linyer", new Date());
	//	dao.deleteNews(3);
	//	dao.updateNews(3, "newTitle");
	//	dao.getNewsList("Java Web開課啦");
		dao.getNewsByTitle("Java Web開課啦");
	}
}

DAO 模式 及 單例模式

DAO 組件的優化思路

  • 操作:
    • 提取數據庫公共操作(獲取數據庫連接、釋放資源、增刪改、查)形成一個數據庫操作的基類。
    • NewsDao 提取一個穩定的新聞操作的接口,新聞操作寫在接口的實現類中。
  • 作用:
    • 將相似功能的代碼抽取封裝成方法,減少代碼冗餘。
    • 因爲不同的數據庫會有不同的實現,對數據庫的操作一般抽取成接口,在以後的開發中可以降低耦合。

DAO 操作的基類

  • BaseDao.java 示例(放在dao包中):
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;

//數據庫操作的基類
public class BaseDao {
	Connection connection = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;
	
	//獲得數據庫資源
	public boolean getConnection(){
		try {
			Class.forName("com.mysql.jdbc.Driver");
			String url = "jdbc:mysql://localhost:9988/kgcnews?characterEncoding=utf8&useSSL=true";
			connection = (Connection) DriverManager.getConnection(url,"root","123456");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			return false;
		}catch (SQLException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	//增刪改
	public int executeUpdate(String sql,Object[] params) {
		int updateRows = 0;
		if(this.getConnection()){
			try {
				pstmt = (PreparedStatement) connection.prepareStatement(sql);
				//填充佔位符
				for(int i=0;i<params.length;i++){
					pstmt.setObject(i+1, params[i]);
				}
				updateRows = pstmt.executeUpdate();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return updateRows;
	}
	
	//查詢
	public ResultSet executeSQL(String sql,Object[] params) {
		if(this.getConnection()){
			try {
				pstmt = (PreparedStatement) connection.prepareStatement(sql);
				//填充佔位符
				for(int i=0;i<params.length;i++){
					pstmt.setObject(i+1, params[i]);
				}
				rs = pstmt.executeQuery();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return rs;
	}
	
	//釋放資源
	public boolean closeResource(){
		if(rs!=null){
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
				return false;
			}
		}
		if(pstmt!=null){
			try {
				pstmt.close();
				
			} catch (SQLException e) {
				e.printStackTrace();
				return false;
			}
		}
		if(connection!=null){
			try {
				connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
				return false;
			}
		}
		return true;
	}
	
}

面向接口的 DAO 設計

  • NewsDao.java 示例(放在dao包中):
import java.util.Date;

public interface NewsDao{
	
	//增加新聞信息
	public void addNews(int id,int categoryid,String title,String summary,String content,String author,Date createdate);

	//刪除特定新聞標題
	public void deleteNews(int id);
	
	//修改特定新聞標題的方法
	public void updateNews(int id,String title);
	
	//查詢全部新聞信息
	public void getNewsList(String title);
	
	//查詢特定標題的新聞信息
	public void getNewsByTitle(String title);

}

  • NewsDaoImpl.java 示例(放在dao.impl包中):
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date

//用 JDBC 實現新聞數據 增 刪 改 操作
public class NewsDaoImpl extends BaseDao implements NewsDao{
	//增加新聞信息
	public void addNews(int id,int categoryid,String title,String summary,String content,String author,Date createdate){
		try {
			String sql = "INSERT INTO news_detail(id,categoryid,title,summary,content,author,createdate) VALUES(?,?,?,?,?,?,?)";
			Object params[] = {id,categoryid,title,summary,content,author,createdate};
			int i = this.executeUpdate(sql,params);
			if(i > 0){
				System.out.println("插入新聞成功!");
			}
		} finally {
			this.closeResource();
		}
	}
	
	//刪除特定新聞標題
	public void deleteNews(int id){
		try {
			String sql = "DELETE FROM news_detail WHERE id=?";
			Object params[] = {id};
			int i = this.executeUpdate(sql,params);
			if(i > 0){
				System.out.println("刪除新聞成功!");
			}
		} finally {
			this.closeResource();
		}				
	}
	
	//修改特定新聞標題的方法
	public void updateNews(int id,String title){
		try {
			String sql = "UPDATE news_detail SET title=? WHERE id=?";
			Object params[] = {id,title};
			int i = this.executeUpdate(sql,params);
			if(i > 0){
				System.out.println("修改新聞成功!");
			}
		} finally {
			this.closeResource();
		}				
	}
	
	//查詢全部新聞信息
	public void getNewsList(String title){
		try {
			String sql = "select id,categoryid,title,summary,content,author,createdate from news_detail";
			Object params[] = {};
			ResultSet rs = this.executeSQL(sql, params);
			while (rs.next()) {
				int id = rs.getInt("id");
				int categoryid = rs.getInt("categoryid");
				String newsTitle = rs.getString("title");
				String summary = rs.getString("summary");
				String content = rs.getString("content");
				String author = rs.getString("author");
				Timestamp createdate = rs.getTimestamp("createdate");
				System.out.println(id+"\t"+categoryid+"\t"+newsTitle+"\t"+summary+"\t"+content+"\t"+author+"\t"+createdate);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			this.closeResource();
		}
	}
	
	//查詢特定標題的新聞信息
	public void getNewsByTitle(String title){
		try {
			String sql = "select * from news_detail where title=?";
			Object params[] = {0};
			ResultSet rs = this.executeSQL(sql, params);
			while (rs.next()) {
				int id = rs.getInt("id");
				String newsTitle = rs.getString("title");
				System.out.println(id+"\t"+newsTitle);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			this.closeResource();
		}
	}
	
	public static void main(String[] args) {
		NewsDao dao = new NewsDaoImpl();
	//	dao.addNews(3, 1, "test", "test", "test", "Linyer", new Date());
	//	dao.deleteNews(3);
	//	dao.updateNews(3, "newTitle");
	//	dao.getNewsList("Java Web開課啦");
		dao.getNewsByTitle("Java Web開課啦");
	}
}

使用屬性文件管理數據

  • 據庫發生改變時,需要重新修改代碼,重新編譯和部署。
  • 將數據庫信息寫在配置文件中,讓程序通過讀取配置文件來獲取這些信息。
  • .properties後綴的文件就是配置文件,存儲的信息爲鍵值對的形式。
  • database.properties 示例(於 src 目錄下):
jdbc.driver=com.mysql.jdbc.driver
jdbc.connection.url=jdbc:mysql://localhost:9988/kgcnews?characterEncoding=utf8&useSSL=true
jdbc.connection.username=root
jdbc.connection.password=123456
  • ConfigManager.java 示例(於 util 包中):
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

//讀取數據庫屬性文件,獲取數據庫連接信息
public class ConfigManager {
	private Properties properties;
	
	public ConfigManager(){
		String configFile = "database.properties";
		InputStream in = ConfigManager.class.getClassLoader().getResourceAsStream(configFile);
		try {
			properties.load(in);
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//根據屬性文件中的鍵獲得對應的值
	public String getString(String key){
		return properties.getProperty(key);
	}
}

單例模式

  • 如何讓用戶只創建一個 ConfigManager
  1. 把構造方法私有。
  2. 程序提供給別人一個對象。
  • 分兩種模式:懶漢模式 / 餓漢模式
    • 區別在於 new 對象的時機不同。
  • 懶漢模式
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

//讀取數據庫屬性文件,獲取數據庫連接信息
public class ConfigManager {
	private static ConfigManager configManager;
	private Properties properties;
	
	private ConfigManager(){
		String configFile = "database.properties";
		InputStream in = ConfigManager.class.getClassLoader().getResourceAsStream(configFile);
		try {
			properties.load(in);
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//提供給比兒一個唯一的ConfigManager對象
	public static synchronized ConfigManager getInstance(){
		if(configManager == null){
			configManager = new ConfigManager();
		}
		return configManager;
	}
	
	//根據屬性文件中的鍵獲得對應的值
	public String getString(String key){
		return properties.getProperty(key);
	}
}

  • 餓漢模式
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

//讀取數據庫屬性文件,獲取數據庫連接信息
public class ConfigManager {
	private static ConfigManager configManager = new ConfigManager();
	private Properties properties;
	
	private ConfigManager(){
		String configFile = "database.properties";
		InputStream in = ConfigManager.class.getClassLoader().getResourceAsStream(configFile);
		try {
			properties.load(in);
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//提供給比兒一個唯一的ConfigManager對象
	public static ConfigManager getInstance(){
		return configManager;
	}
	
	//根據屬性文件中的鍵獲得對應的值
	public String getString(String key){
		return properties.getProperty(key);
	}
}

數據源以及分層開發

理解和使用數據源

  • 數據源
    • javax.sql.DataSource接口負責建立與數據庫的連接
    • Tomecat 提供,將連接保存在連接池中
      1、數據源用來連接數據庫,獲得連接(Connection)對象
      2、連接池用來管理連接(Connection)對象
      3、在程序中使用 JNDI 獲取數據源

JNDI --> DataSource --> Connection --> 連接池

  • 配置 tomcatconf 中的 context.xml
<Resource name="jdbc/news" 
	auth="Container"  type="javax.sql.DataSource"  maxActive="100" 
    maxIdle="30" maxWait="10000" username="root"  password="123456" 
    driverClassName="com.mysql.jdbc.Driver" 
    url="jdbc:mysql://127.0.0.1:9988/kgcnews?characterEncoding=utf8&useSSL=true"/>
  • 獲取數據庫連接
//獲取數據庫連接
public boolean getConnection2(){
	try {
		//初始化上下文
		Context cxt = new InitialContext();
		//獲取與邏輯名稱相關聯的數據源對象
		DataSource ds = (DataSource) cxt.lookup("java:comp/env/jdbc/news");
		//通過數據源獲取數據庫連接
		connection = (Connection) ds.getConnection();
	} catch (NamingException e) {
		e.printStackTrace();
	} catch (SQLException e) {
		e.printStackTrace();
	}

使用 JavaBean 封裝數據

  • JavaBean
    • 就是一個 Java
    • 封裝業務邏輯
    • 封裝數據
public void add(int id,int categoryld,String title,String summary,String content,Date createdate){
//方法體
}
public void add(新聞信息對象){
//方法體
}

如:

import java.sql.Date;

//新聞JavaBean,僅封裝數據(屬性、setter及getter)
public class News {
	private int id;
	private int categoryId;
	private String title;
	private String summary;
	private String content;
	private String picPath;
	private String author;
	private Date creatDate;
	private Date modifyDate;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public int getCategoryId() {
		return categoryId;
	}
	public void setCategoryId(int categoryId) {
		this.categoryId = categoryId;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getSummary() {
		return summary;
	}
	public void setSummary(String summary) {
		this.summary = summary;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public String getPicPath() {
		return picPath;
	}
	public void setPicPath(String picPath) {
		this.picPath = picPath;
	}
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
	public Date getCreatDate() {
		return creatDate;
	}
	public void setCreatDate(Date creatDate) {
		this.creatDate = creatDate;
	}
	public Date getModifyDate() {
		return modifyDate;
	}
	public void setModifyDate(Date modifyDate) {
		this.modifyDate = modifyDate;
	}
}

分層開發思路

  • dao 層 —> 數據訪問接口層
    • 主要負責和數據操作相關的事情接口:NewsDao
    • 接口實現類:NewsDaoImpl
  • Service 層 —> 業務邏輯層主要負責與業務邏輯相關操作,對dao層的封裝和調用
    • 接口:NewsService
    • 接口實現類:NewsServiceImpl

JSP 標籤

  • JSP 動作標籤
    • 通過動作標籤,程序員可以在 JSP 頁面中把頁面的顯示功能部分封裝起來,使整個頁面更簡潔和易於維護

useBean

  • <jsp:useBean>
    • 裝載一個將在 JSP 頁面中使用的 JavaBean,發揮 Java 組件重用的優勢
<jsp:useBean id="name" class="package.class" scope="scope">

註釋

  • id–>JavaBean的引用名
  • class–>JavaBean的類
  • scope–>JavaBean的範圍

include

  • <jsp:include>
    • 把指定文件插入正在生成的頁面中
<jsp:include page="URL">
  • include 指令
<%@include file="URL"%>
  • <%@include%><jsp:include>區別:
    • <jsp:include>動態包含,將被包含頁面的結果包含進來。
      先處理,再包含
    • <%@include%>靜態包含,將被包含頁面的內容包含進來。
      先包含,再處理

頁面跳轉

  • <jsp:forward>
<jsp:forward page="URL">

Servlet 和 過濾器

Servlet 概述

  • Servlet 做了什麼?
    • 本身做任何業務處理
    • 只是接收請求並決定調用哪個 JavaBean 去處理請求
    • 確定用哪個頁面來顯示處理返回的數據
  • Servlet 是什麼?
    • Server+Applet,是一種服務器端的 Java 應用程序
    • 只有當一個服務器端的程序使用了 Servlet API 的時候,這個服務端的程序才能稱之爲 Servlet
  • Servlet 特點
    • Java 程序
    • 運行在服務器端
    • 需要 web 容器的支持
    • 使用了 ServletAPI
    • 本身不做任何業務處理,中間調度作用

Servlet API

  • javax.servlet.Servlet 接口
    • 所有Java Servlet的基礎接口類,規定了必須由Servlet具體類實現的方法集
  • javax.servlet.GenericServlet
    • Servlet 的通用版本,是一種與協議無關的 Servlet
  • javax.servlet.http.HttpServlet(使用較多)
    • GenericServlet 基礎上擴展的基於 Http 協議的 Servlet
    • init()
    • service()
    • doGet()
    • doPost()
    • destroy()

Servlet 中主要方法

  • init(): servlet 的初始化方法,僅僅會執行一次
  • service(): 處理請求和生成響應
  • destroy(): 在服務器停止並且程序中的Servlet對象不再使用的時候調用,只執行一次
  • ServletRequest
    • 封裝客戶的請求信息
    • 作用相當於 JSP 內置對象 request
  • ServletResponse
    • 創建響應信息,將處理結果返回給客戶端
    • 作用相當於SP內置對象response
  • ServletConfig: 包含了servlet的初始化參數信息

Servlet 生命週期

  • Servlet 生命週期各個階段
    • 加載和實例化
    • 初始化
    • 處理請求
    • 銷燬
生命週期 誰來做 何時做
實例化 Servlet 容器 servlet 容器啓動或者容器檢測到客戶端請求時
初始化 servlet 容器 實例化後,容器調用 Servletinit()
初始化 對象處理請求 Servlet 容器得到客戶端請求並做出處理時
銷燬 servlet 容器 當程序中的 Servlet 對象不再使用的時候,或者 Web 服務器停止運行的時候

跳轉路徑問題

  • HttpServletResponse 重定向 response.sendRedirect()
    http://localhost:8080/news/servlet/AddServlet

    • (1)相對路徑
      response.sendRedirect("newsDetailList.jsp")
      http://1ocalhost:8080/news/servlet/newsDetailList.jsp
    • (2)絕對路徑
      response.sendRedirect("/news/jsp/admin/newsDetailList.jsp")
      http://1ocalhost:8080/news/jsp/admin/newsDetailList.jsp
  • HttpServletRequest 轉發 request.getRequestDispatcher().forward()
    http://1ocalhost:8080/news/servlet/AddServlet

    • (1)相對路徑
      request.getRequestDispatcher("newsDetailList.jsp").forward()
      http://localhost:8080/news/servlet/newsDetailList.jsp
    • (2)絕對路徑
      response.sendRedirect("/news/jsp/admin/newsDetailList.jsp")
      http://localhost:8080/news/jsp/admin/newsDetailList.jsp

EL 與 JSTL

EL 表達式

EL 嚴格 區分 大小寫,初學者嚴格按規範書寫,有利於養成好的編碼習慣

  • EL 表達式(Expression Language)
    • 語法${EL表達式}例如:${username}

EL 操作符

  • 操作符.(用的比較多)
    • 獲取對象的屬性,例如:${news.title}
  • 操作符[]
    • 獲取對象的屬性,例如:${news["title"]}
    • 獲取集合中的對象,例如newsList[0]

EL 功能

  • 取得 JavaBean 對象的屬性
    • ${news.title}
  • 取得 數組ListMap 類型對象的元素
    • ${ist[0]}
  • 使用各類運算符對原始數據進行簡單處理
    • ${totalRecordCount/pageSizer}
  • 屏蔽一些常見的異常
    • ${username}
  • 能實現簡單的自動類型轉換
    • ${news}相當於(News)request.getAttribute("news")

EL 訪問作用域

  • request.setAttribute("news",news);
  • 兩種方式取數據:
    • Java 小腳本:request.getAttribute("news",news);
    • 使用 EL 表達式:${news}或者${requestScope.news}
作用域 Java代碼取值 EL取值
請求作用域 request.getAttribute("news"); ${requestscope.news}
會話作用域 session.getAttribute("username"); ${sessionscope.username}
程序作用域 application.getAttribute("count"); ${applicationscope.count}
頁面作用域 pageContext.getAttribute("userNum"); ${pagescope.userNum}

如果只有 ${news}會從作用域從小到大找

JSTL 表達式

  • JSTL(JavaServerPages Standard Tag Library)
    • JSP 標準標籤庫
    • 實現 JSP 頁面中的邏輯控制
  • JSTL 使用步驟

JSTL 標籤

標籤庫名稱 資源標示符(uri)
*<c:out/> 輸出文本內容到out對象,常用於顯示特殊字符,顯示默認值
<c:set/> 在作用域中設置變量或對象屬性的值
<c.remove/> 在作用域中移除變量的值
*<c:if/> 實現條件判斷結構
*<c:forEach/> 實現循環結構
<c:url/> 構造url地址
<c:param/> 在url後附加參數
*<c:import/> 在頁面中嵌入另一個資源內容
*<fmt:formatDate/> 格式化時間
<fmt:formatNumber/> 格式化數字

*爲常用加標籤

分頁查詢

分頁查詢的後臺實現

  • 用 SQL 語句查詢新聞總數
Select count(1)
From news_detail
  • SQL語句限制顯示的行數

分頁查詢新聞數據,每頁顯示兩條數據,這裏顯示第 1 頁:

SELECT id,title,author,createdate
WHERE 1=1
FROM news detail
LIMIT 0,2

分頁查詢新聞數據,每頁顯示兩條數據,這裏顯示第 2 頁:

SELECT id,title,author,createdate
WHERE 1=1
FROM news detail
LIMIT 2,2

分頁查詢新聞數據,每頁顯示兩條數據,這裏顯示第 3 頁:

SELECT id,title,author,createdate
WHERE 1=1
FROM news detail
LIMIT 4,2
  • 公用的分頁SQL:分頁後每頁顯示幾條新聞(頁面容量):pageSize
    從第幾條數據開始顯示(當前頁碼pageNo-1)*pageSize
SELECT id,title,author,createdate
WHERE 1=1
FROM news detail
LIMIT (pageNo-1)*pageSize,pageSize

圓角邊框

border-radius: 20px 10px 50px 30px;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章