这里我将和大家一起学习和回顾整个servlet和jsp技术,由于这部分学习的东西较多,我把这块分成了三篇,每一篇都从简到难地安排了一些小知识点,东西是当时我老师讲的,现在将知识点都整理下来,供大家一起讨论学习,也方便以后自己复习。如果有不对的地方欢迎大家指正。
以下是这篇大该的纲要及详细内容:
1.servlet简介,开发步骤…(之前写的一篇,这里不再写了)
2.HTTP协议的讲解,添加用户
3.重定向,Dao的概念及编写
4.JSP概念,java代码片段,html转jsp…
5.转发,相对路径与绝对路径
<2>.HTTP协议的讲解,添加用户
1.http协议 (了解)
(1)什么是http协议?
是一种网络应用层协议、规定了浏览器与web服务器之间如何通信以及相应的数据包的格式。
1)如何通信?
step1.建立连接
step2.发送请求
step3.发送响应
step4.关闭连接
这样做的好处是,服务器可以利用有限的连接数为尽可能
多的请求服务。
2)数据包的格式
a.请求数据包
请求行(请求方式 请求资源路径 协议和版本)
若干消息头
消息头是一些键值对,使用": "隔开,通信的双方可以借助于消息头来传递一些特定的信息,比如浏览器可以发送"user-agent"消息头,告诉服务器浏览器的类型和版本。
实体内容
如果请求类型为get,实体内容为空。
只有当请求类型为post时,实体内容才会有数据。
b.响应数据包
状态行(协议类型和版本 状态码 状态描述)
注:
状态码是一个三位数字,表示服务器处理请求的一种状态,常见状态码如下:
200: 正常
500: 系统出错
404: 请求路径出错
若干消息头
服务器也可以发送一些消息头给浏览器,比如,发送content-type消息头,告诉浏览器,服务器返回的数据类型(包括编码)
c.实体内容
程序的处理结果,浏览器会解析出来,生成相应的页面。
(2)两种请求方式
1)哪些情况下,浏览器会发送get请求?
a.直接在浏览器地址栏输入地址。
b.点击链接。
c.表单的默认提交方式。
2)get请求的特点
a.会将请求参数显示在浏览器地址栏,不安全。
注:
因为有些网络设备(比如路由器)会记录访问地址。
b.会将请求参数添加到请求资源路径的后面,只能提交少量的数据给服务器。
注:
因为请求行大约只能存放2k左右的数据。
3)哪些情况下,浏览器会发送post请求
a.设置表单的method属性值为"post"。
4)post请求的特点
a.不会将请求参数显示在浏览器地址栏,相对安全。
注:
http协议并不会对数据进行加密,所以,对于敏感数据,需要进行加密处理(使用https协议)。
b.将请求参数添加到了实体内容里面,可以提交大量的数据给服务器。
2.Servlet输出中文需要注意的问题
(1)为什么会产生乱码?
因为out.println方法默认会使用"iso-8859-1"来编码。
(2)如何解决?
response.setContentType(“text/html;charset=utf-8”);
3.读取请求参数值
(1)String getParameter(String paramName)
a.请求参数名(paramName)要与实际传递过来的请求参数名一致,否则会获得null值。
b.提交表单时,如果不填写任何数据,会获得""。
(2)String[] getParamterValues(String paramName)
a.当有多个请求参数名相同时,使用此方法。
b.对于多选框,如果不选择任何选项,会获得null值。
4.表单包含有中文参数值,如何处理?
(1)为什么会产生乱码?
提交表单时,浏览器会对表单中的中文参数值进行编码,比如
使用utf-8来编码,服务器端默认会使用iso-8859-1来解码,所以会产生乱码。
注:
浏览器会按照打开该表单所在页面时的字符集来编码。
(2)如何解决?
1)post请求
request.setCharacterEncoding(“utf-8”);
注:
此行代码要添加到所有的getParameter方法的最前面。
只针对post请求有效。
2)get请求
修改tomcat的配置文件(server.xml)
<Connector URIEncoding="utf-8"/>
注:
只针对get请求有效。
5.访问数据库
step1.导包
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
step2.添加jdbc.properties文件。
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jsd1807db?useUnicode=true&characterEncoding=UTF-8
username=root
password=root
step3.添加DBUtils类。
public class DBUtils {
private static String driver;
private static String url;
private static String username;
private static String password;
private static BasicDataSource dataSource;
static {
Properties prop = new Properties();
InputStream ips = DBUtils.class
.getClassLoader()
.getResourceAsStream("jdbc.properties");
try {
prop.load(ips);
driver = prop.getProperty("driver");
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("password");
//创建数据源对象
dataSource =
new BasicDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setInitialSize(3);
dataSource.setMaxActive(3);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
ips.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static Connection getConn()
throws Exception {
return dataSource.getConnection();
}
public static void close(Connection conn,
Statement stat, ResultSet rs) {
try {
if(rs!=null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(stat!=null) {
stat.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null) {
//打开自动提交
conn.setAutoCommit(true);
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
step4.添加一张表(t_user)
create table t_user(
id int primary key auto_increment,
username varchar(50) unique,
password varchar(20),
email varchar(30)
);
step5.在service方法里面,使用jdbc api访问数据库
public class AddUserServlet extends HttpServlet{
@Override
protected void service(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException,
IOException {
//处理表单中文参数值的问题
request.setCharacterEncoding("utf-8");
/*
* 这行代码的作用:
* 1.设置content-type消息头的值。
* 2.out.println方法在输出时,会使用
* charset指定的字符集来编码。
*/
response.setContentType(
"text/html;charset=utf-8");
PrintWriter out =
response.getWriter();
//读取用户信息
String username =
request.getParameter("username");
String pwd =
request.getParameter("pwd");
String email =
request.getParameter("email");
//将用户信息插入到数据库
Connection conn = null;
PreparedStatement ps = null;
try {
conn = DBUtils.getConn();
String sql = "INSERT INTO t_user "
+ "VALUES(null,?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, pwd);
ps.setString(3, email);
ps.executeUpdate();
out.println("添加成功");
} catch (Exception e) {
/*
* step1.记日志(保留现场)
* 注:
* 在实际项目中,经常需要将异常
* 信息写到文件里面。
*/
e.printStackTrace();
/*
* step2.看异常能否恢复,如果
* 异常不能够恢复(比如数据库服务
* 停止、网络中断等等,这样的异常
* 我们一般称之为系统异常),则提示
* 用户稍后重试;
* 如果能够恢复,则立即恢复。
*/
out.println("系统繁忙,稍后重试");
}finally {
DBUtils.close(conn, ps, null);
}
}
}
<3>.重定向,Dao的概念及编写
1.重定向
(1)什么是重定向?
服务器通知浏览器向某个地址发送请求。
注:
服务器可以发送302状态码和Location消息头(该消息头的值是一个地址,一般称之为重定向地址)给浏览器,浏览器收到之后,会立即向重定向地址发送请求。
(2)如何重定向?
response.sendRedirect(String url);
注:
url是重定向地址。
容器在重定向之前,会清空response对象上存放的所有数据。
(3)特点
a.重定向之后,浏览器地址栏的地址会发生改变。
b.重定向的地址是任意的。
2. DAO (Data Access Object)
(1)DAO是什么?
是一个封装了数据访问逻辑的对象。
(2)如何写一个DAO?
step1.写一个java类(一般称之为实体类)。
注:
该类与要访问的表的结构保持一致,即表有哪些字段,该类要有与之对应的属性,属性类型要与表的字段类型匹配。
我们可以将记录中保存的信息添加到实体对象里面,方便处理。
public class User {
private int id;
private String username;
private String pwd;
private String email;
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", pwd=" + pwd + ", email=" + email + "]";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
step2.写一个java类,提供一些访问数据库的方法。
public class UserDAO {
/**
* 查询出所有用户的信息。
* 注:
* 关系数据库里面存放的是一条条记录,而java是面向对象的语言。在设计DAO时,我们经常将查询到的记录转换成 一个对应的java对象。
*/
public List<User> findAll()
throws Exception{
List<User> users =
new ArrayList<User>();
Connection conn = null;
PreparedStatement stat = null;
ResultSet rs = null;
try {
conn = DBUtils.getConn();
String sql =
"SELECT * FROM t_user";
stat = conn.prepareStatement(sql);
rs = stat.executeQuery();
while(rs.next()) {
int id = rs.getInt("id");
String username =
rs.getString("username");
String pwd =
rs.getString("password");
String email =
rs.getString("email");
User user = new User();
user.setId(id);
user.setUsername(username);
user.setPwd(pwd);
user.setEmail(email);
users.add(user);
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}finally {
DBUtils.close(conn, stat, rs);
}
return users;
}
}
(3)DAO的优点
a.DAO封装了数据访问逻辑,调用者不用关心数据访问逻辑是如何实现的。这样,代码更好维护。
b.方便测试。如果将数据访问写在servlet里面,需要部署整个应用才能测试。
<4>.JSP概念,java代码片段,html转jsp…
1.jsp
(1)jsp是什么?
sun公司制订的一种服务器端动态页面技术规范。
注:
虽然使用Servlet也可以生成动态页面,但是过于繁琐(需要使用out.println语句),并且不利于页面的维护(比如,要修改页面就必须修改java代码),所以,sun才制订了jsp技术规范。
jsp其实是一个以.jsp为后缀的文件,主要内容是html和少量的java代码。容器会将jsp文件转换成一个对应的servlet然后执行。
记住,jsp的本质就是一个servlet!
(2)如何写一个jsp文件?
step1. 添加一个以.jsp为后缀的文件。
step2. 在该文件里面,可以使用如下元素:
1)html(css,javascript)
直接写即可。
2)java代码
方式一 java代码片断
<% java代码 %>
方式二 jsp表达式
<%= java表达式 %>
3)隐含对象
a.什么是隐含对象?
直接可以使用的对象,比如out、request、response。
b.为什么可以直接使用这些隐含对象?
因为容器会自动添加获得这些对象的语句。
4)指令
a.什么是指令?
通知容器,在将jsp文件转换成一个Servlet类时,
做一些的额外的处理,比如导包。
b.指令的语法
<%@ 指令名 属性=值 %>
c.page指令
import属性:用于指定要导的包名。
比如
<%@ page import="java.util.*,java.text.*"%>
contentType属性:设置response.setContentType方法的内容。
pageEncoding属性:告诉容器,在读取jsp文件的内容时,使用指定的字符集来解码。
(3)jsp是如何执行的?
1)阶段一 容器将.jsp文件转换成一个Servlet类。
html(css,js) ----> service方法里面,使用out.write输出。
<% %> -----> service方法里面,照搬。
<%= %> -----> service方法里面,使用out.print输出。
2)阶段二 容器调用该Servlet
需要编译、实例化、然后调用service方法。
练习 写一个date.jsp,输出当前的系统日期,比如
输出 "2018-10-31"
<5>.转发,相对路径与绝对路径
1.转发
(1)什么是转发?
一个web组件将未完成的处理交给另外一个web组件继续做。
注:
web组件: jsp或者servlet的统称。
通常是一个servlet获得数据,然后转发给一个jsp来展现。
(2)如何转发?
step1.绑订数据到request对象上。
request.setAttribute(String name,Object obj);
注:
a. name通常称之为绑订名,obj称之为绑订值。
b. 该方法内部的实现:就是以name作为key,以
obj作为value,调用Map.put方法。
c. Object request.getAttribute(String name);
step2.获得转发器。
RequestDispatcher rd =
request.getRequestDispatcher(Sting uri);
注:
a.uri:转发的目的地,通常是一个jsp。
b.RequestDispatcher是一个接口,该方法会返回 一个符合该接口的对象,这个对象一般我们称之为转发器。
c.转发的本质是一个web组件通知容器去调用另外一个web组件,可以将转发器理解为web组件通知容器的媒介。
step3.转发
rd.forward(request,response);
(3)特点
a.转发之后,浏览器地址栏的地址不变。
b.转发的目的地有限制,要求属于同一个web应用。
2.比较转发与重定向
(1)地址栏地址有无变化
转发之后,浏览器地址栏地址不变;重定向之后,浏览器地址栏地址会发生改变。
(2)目的地有无限制
转发有限制,重定向没有任何限制。
(3)能否共享request对象和response对象。
转发可以,重定向不行。
注:
容器收到请求之后,会立即创建request对象和response对象。当响应发送完毕,容器会立即销毁这两个对象。也就是说,request对象和response对象的生存时间是一次请求与响应期间存在。
重定向是两次请求。
(4)一件事是否完成
转发是一件事未做完,让另外一个web组件继续做;
而重定向是一件事已经完成,再做另外一件独立的事件。
2.路径问题
链接、表单提交、重定向和转发如何填写相应的路径
<a href=""></a>
<form action="">
response.sendRedirect("")
request.getRequestDispatcher("")
(1)什么是相对路径?
不以"/"开头的路径
(2)什么是绝对路径?
以"/"开头的路径
(3)如何写绝对路径?
链接、表单提交、重定向从应用名开始写;转发从应用名之后开始写。
注:
不要将应用名直接写在路径里面!
应该使用以下方法来获得实际部署时的应用名:
String request.getContextPath();
在实际开发时,建议尽量使用绝对路径。
练习
添加用户时,如果用户名已经存在,则在添加用户的页面上
提示“用户名已经存在”。否则,将用户信息插入到数据库,然后重定向到用户列表。
提示:
step1.在UserDAO类中添加一个方法
public User findByUsername(String uname);
public User findByUsername(String name) throws Exception {
//假设找不到
User user = null;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtils.getconnn();
String sql ="select * from t_user where username=?";
ps=conn.prepareStatement(sql);
ps.setString(1,name);
rs = ps.executeQuery();
if(rs.next()) {
user = new User();
user.setId(rs.getInt(1));
user.setUsername(rs.getString(2));
user.setPwd(rs.getString(3));
user.setEmail(rs.getString(4));
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}finally {
DBUtils.close(rs, ps, conn);
}
return user;
}
step2.修改AddUserServlet
调用UserDAO的findByUsername方法,如果返回值不为null,则绑订错误提示信息到request,然后转发到addUser.jsp;否则调用UserDAO的save方法,将用户信息插入到数据库。
public class AddUserServlet extends HttpServlet{
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/**
* 用来处理表单中文参数值的问题 这里的字符集要和表单页面的字符集得一致
*/
request.setCharacterEncoding("utf-8"); //提交的表单里有中文,就这么处理
//输出用户信息
/**
* 这行代码的作用:
* 1.设置content-type消息头的值
* 2.out.println方法在输出时,会使用charset指定的字符集来编码。
*/
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter(); //获得输出流
//读取用户信息
String username = request.getParameter("username");
UserDao userdao = new UserDao();
try {
/**
* 先查看用户名是否存在,如果已经存在,则转发到addUser.jsp,提示用户名已经存在;否则,将该用户的信息插入到数据库,重定向到
* 用户列表。
*/
User user = userdao.findByUsername(username);
if(user!=null){
request.setAttribute("msg","该用户名已被占用");//step1.将数据绑定到request对象上。
RequestDispatcher rd = request.getRequestDispatcher("addUser.jsp");//step2.获得转发器
rd.forward(request, response);//step3.转发
}else {
//注意: 为空不能调用..方法所以要。。
user=new User(); //避免空指针异常
user.setUsername(request.getParameter("username"));
user.setPwd(request.getParameter("password"));
user.setEmail(request.getParameter("email"));
userdao.addUser(user);
response.sendRedirect("list"); ///给浏览器发送302状态码,让浏览器马上访问消息头Location: list
}
} catch (Exception e1) {
e1.printStackTrace();
out.println("系统繁忙,稍后重试");
}
}
}
step3.修改addUser.jsp (显示提示信息)
上面的"msg"是servlet中绑定的数据的key值。