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;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值