今日内容
零、复习昨日
一、接收请求
二、处理响应
三、综合案例
零、复习昨日
见晨考
一、接收请求
浏览器发出请求,经过web.xml映射匹配,找到Servlet对应的方法(doGet/doPost),接收请求数据,可以接收请求中的请求行,请求头,请求正文,具体流程如下
浏览器发出请求
a/form/ajax
经过web.xml映射匹配
web.xml(8行代码)
servlet类中的doGet/doPost
前端是get请求,就重写doGet
前端是post请求,就重写doPost
如何接收数据?通过HttpServletRequest对象处理
需求: html页面中写一个表单,发送请求,后台服务器接收所有请求数据
1.1 编写页面
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<h2>演示表单发送请求,后台接收请求数据</h2>
<%--
action路径,需要加上项目名
/项目名/请求路径
单选,复选框默认选中是 添加checked属性
下来框默认选中是 option中添加selected属性
--%>
<form action="/day39/req" method="get">
用户名<input type="text" name="username"><br>
密码<input type="password" name="password"><br>
邮箱<input type="email" name="email"><br>
性别<input type="radio" name="sex" value="1" checked>男
<input type="radio" name="sex"value="2">女<br>
爱好<input type="checkbox" name="hobby" value="coding" checked>敲代码
<input type="checkbox" name="hobby" value="game"> 打游戏
<input type="checkbox" name="hobby" value="ball">打球<br>
生日<input type="date" name="birthday"><br>
籍贯<select name="jiguan">
<option value="henan" selected>河南</option>
<option value="hebei">河北</option>
<option value="shanxi">山西</option>
</select><br>
<input type="submit" value="提交">
</form>
</body>
</html>
1.2 编写Servlet
package com.qf.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
public class MyReqRespServlet extends HttpServlet {
/**
*
* HttpServletRequest req 来接收请求数据
* HttpServletResponse resp 来处理响应
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// === 请求行 === [熟悉]
String method = req.getMethod( );
/**
* URL: 资源定位符 http://localhost:8080/day39/req
* URI: 资源标识符 /day39/req
*/
StringBuffer url = req.getRequestURL( );
String uri = req.getRequestURI( );
String protocol = req.getProtocol( );
System.out.println("method:"+method);
System.out.println("url:"+url );
System.out.println("uri:"+uri);
System.out.println("protocol:"+protocol );
// ==== 请求头 ====
// 获得所有的请求头[了解]
Enumeration<String> names = req.getHeaderNames( );
while (names.hasMoreElements()) {
String heard = names.nextElement( );
String value = req.getHeader(heard);
System.out.println(heard+"-->"+value );
}
/**
* [重点] 获得请求正文
* 无论是明文,暗文,邮箱,日期,单选,下拉框获取数据的方法都是
* req.getParameter(name),参数是前端标签内name属性的值
*/
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
String sex = req.getParameter("sex");
// int i = Integer.parseInt(sex);
String birthday = req.getParameter("birthday");
//SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//Date date = sdf.parse(birthday);
String jiguan = req.getParameter("jiguan");
System.out.println("username = " + username);
System.out.println("password = " + password );
System.out.println("email = " + email);
System.out.println("sex = " + sex);
System.out.println("birthday = " + birthday);
System.out.println("jiguan = " + jiguan);
String[] hobbies = req.getParameterValues("hobby");
System.out.println(Arrays.toString(hobbies ) );
}
}
1.3 配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>req</servlet-name>
<servlet-class>com.qf.servlet.MyReqRespServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>req</servlet-name>
<!--此处不需要写项目名,只需要写请求路径-->
<url-pattern>/req</url-pattern>
</servlet-mapping>
</web-app>
1.4 部署项目
1.5 启动测试
二、做出响应
做出响应是通过HttpServletResponse对象
- 响应行
- 状态码
- 响应头
- 响应信息,其中有一个cookie后续会用到,以及编码格式
- 响应正文
- 向浏览器展现的内容
// 响应状态码
// 200 是成功, 302 重定向 404 资源未找到 500 服务器错误
// 一般不用设置,为自动响应
// resp.setStatus(200);
// 设置响应头
// resp.setHeader("key","value");
// 指定浏览器如何解析响应的内容,解决响应乱码
resp.setContentType("text/html;charset=utf-8");
// 向浏览器响应内容(响应正文)
PrintWriter out = resp.getWriter( );
out.write("<html>");
out.write(" <head>");
out.write(" <title>这是响应</title>");
out.write(" </head>");
out.write(" <body>");
out.write(" <div style='background-color:red;width:500px;height:500px;font-size:50px'>");
out.write(" 这是响应,欢迎"+username);
out.write(" </div>");
out.write(" </body>");
out.write("</html>");
三、乱码解决
请求乱码
req.setCharacterEncoding("utf-8");
响应乱码
resp.setContentType("text/html;charset=utf-8");
四、 综合案例(Servlet + JDBC)
要求:实现登录功能、展示所有用户功能
以下仅展示关键代码
登录页面
html/jsp
后台服务器代码
web.xml
servlet,接收数据
jdbc+orm
servlet,做出响应
mysql
库,表
4.1 数据库
CREATE TABLE admin(
username VARCHAR(20) PRIMARY KEY,
PASSWORD VARCHAR(20) NOT NULL,
phone varchar(11) NOT NULL,
Address varchar(20) NOT NULL
)CHARSET=utf8;
INSERT INTO admin(username,PASSWORD,phone,address)
VALUES('gavin','123456','12345678901','北京市昌平区');
INSERT INTO admin(username,PASSWORD,phone,address)
VALUES('aaron','123456','12345678901','北京市昌平区');
实体类
public class Admin {
private String username;
private String password;
private String phone;
private String address;
// set get 构造 toString ...
}
4.2 pom依赖
<!-- 引入servlet依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- 引入jsp依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- 阿里巴巴数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
4.3 DBUtils
package com.qf.util;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
public class DBUtil {
// 读取输入流中的数据放入properties对象
private static Properties properties = new Properties( );
// 声明Druid连接池
private static DruidDataSource dataSource;
/**
* 静态代码块
* 目的: 为了加载DBUtil类时就执行静态代码块内的代码
* 就会加载驱动,且只保留一份
*/
static {
try {
// 将db.properties文件变为输入流
InputStream inputStream = DBUtil.class.getResourceAsStream("/db.properties");
properties.load(inputStream);
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace( );
System.out.println("加载驱动出错");
}
}
/**
* 返回数据库连接
*/
public static Connection getConnection() {
Connection conn = null;
try {
conn = dataSource.getConnection( );
} catch (Exception e) {
e.printStackTrace( );
System.out.println("获得连接出错");
}
return conn;
}
/**
* 关闭所有连接
*/
public static void closeAll(Connection conn, Statement s) {
try {
conn.close( );
s.close( );
} catch (SQLException e) {
e.printStackTrace( );
}
}
public static void closeAll(Connection conn, Statement s, ResultSet rs) {
try {
conn.close( );
s.close( );
rs.close( );
} catch (SQLException e) {
e.printStackTrace( );
}
}
/**
* 设计方法,自动完成查询结果的封装
*/
public static <T> T selectOne(Class<T> target, String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
T t = null; // 最终返回的目标类型
try {
conn = getConnection( );
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery( );
// 根据目标类,获得所有属性
Field[] fields = target.getDeclaredFields( );
while(rs.next()) {
t = target.newInstance( );
// 遍历得到所有的属性,即就是数据库的字段
for (Field field : fields) {
String name = field.getName( );
Object value = rs.getObject(name);
field.setAccessible(true);
if (value != null) {
field.set(t,value);
}
}
}
}catch (Exception e) {
e.printStackTrace();
} finally {
closeAll(conn,ps,rs);
}
return t;
}
public static <T> List<T> selectAll(Class<T> target, String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
T t = null; // 最终返回的目标类型
ArrayList<T> list = new ArrayList<>( );
try {
conn = getConnection( );
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1,args[i]);
}
rs = ps.executeQuery( );
// 根据目标类,获得所有属性
Field[] fields = target.getDeclaredFields( );
while(rs.next()) {
t = target.newInstance( );
// 遍历得到所有的属性,即就是数据库的字段
for (Field field : fields) {
String name = field.getName( );
Object value = rs.getObject(name);
field.setAccessible(true);
if (value != null) {
field.set(t,value);
}
}
list.add(t);
}
}catch (Exception e) {
e.printStackTrace();
} finally {
closeAll(conn,ps,rs);
}
return list;
}
/**
* 封装增删改的方法
*/
public static int update(String sql,Object... args){
Connection conn = null;
PreparedStatement ps = null;
int num = 0;
try {
conn = getConnection( );
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1,args[i]);
}
num = ps.executeUpdate( );
}catch (Exception e) {
e.printStackTrace();
} finally {
closeAll(conn,ps);
}
return num;
}
}
加入数据库连接池
db.properties
# properties文件是一种特殊的文件格式,形如map
# 文件内容的写法: key=value
# 数据库驱动
driverClass=com.mysql.jdbc.Driver
# 连接url
url=jdbc:mysql://localhost:3306/java2307?useSSL=false
# 用户名
username=root
# 密码
password=123456
# ----- 加入druid的一些连接配置
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 60000毫秒/1000等于60秒 -->
maxWait=5000
4.4 登录页面
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>登录</h2>
<form action="/day39/login">
用户名<input type="text" name="username"><br>
密码 <input type="password" name="password"><br>
<input type="submit" value="登录"><br>
</form>
</body>
</html>
4.5 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- 设置项目启动访问的首页,默认是index.jsp -->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>req</servlet-name>
<servlet-class>com.qf.servlet.MyReqRespServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>req</servlet-name>
<url-pattern>/req</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.qf.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
</web-app>
4.6 业务层service
业务层就处理项目业务逻辑的,业务逻辑包含哪些内容?
- 除了servlet接收请求做出响应以及jdbc操作数据库的代码,其他全是业务逻辑
业务层代码编码
- 需要创建包结构service
- 创建业务层接口及实现类
AdminService
public interface AdminService {
/**
* 根据用户名密码查询用户
* @param username
* @param password
* @return 查到返回的是用户对象
* 查不到返回的null
*/
Admin login(String username , String password);
/**
* 查询全部用户
*/
List<Admin> findAll();
}
AdminServiceImpl
public class AdminServiceImpl implements AdminService{
private AdminDao adminDao = new AdminDaoImpl();
@Override
public Admin login(String username, String password) {
/**
* 业务逻辑代码...
*/
Admin admin = adminDao.login(username, password);
return admin;
}
@Override
public List<Admin> findAll() {
List<Admin> list = adminDao.findAll( );
return list;
}
}
4.7 数据访问层dao
数据访问层 Data Access Object - DAO
项目需要创建dao层代码专门操作数据库
dao层编码
- 创建dao包
- 创建dao接口及实现类
AdminDao
public interface AdminDao {
/**
* 登录
*/
Admin login(String username,String password);
/**
* 查询全部
*/
List<Admin> findAll();
}
AdminDaoImpl
public class AdminDaoImpl implements AdminDao{
@Override
public Admin login(String username, String password) {
Connection conn = DBUtil.getConnection( );
PreparedStatement ps = null;
ResultSet rs = null;
Admin admin = null;
try {
ps = conn.prepareStatement("select * from admin where username = ? and password = ?");
ps.setString(1,username);
ps.setString(2,password);
rs = ps.executeQuery( );
while (rs.next()) {
// 从数据库查出的
String username1 = rs.getString("username");
String password1 = rs.getString("password");
String phone = rs.getString("phone");
String address = rs.getString("address");
admin = new Admin( );// 用这个对象封装数据库的数据
admin.setUsername(username1);
admin.setPassword(password1);
admin.setPhone(phone);
admin.setAddress(address);
}
} catch (SQLException e) {
e.printStackTrace( );
} finally {
DBUtil.closeAll(conn,ps,rs);
}
return admin;
}
@Override
public List<Admin> findAll() {
Connection conn = DBUtil.getConnection( );
PreparedStatement ps = null;
ResultSet rs = null;
Admin admin = null;
ArrayList<Admin> list = new ArrayList<>( );
try {
ps = conn.prepareStatement("select * from admin ");
rs = ps.executeQuery( );
while (rs.next()) {
// 从数据库查出的
String username1 = rs.getString("username");
String password1 = rs.getString("password");
String phone = rs.getString("phone");
String address = rs.getString("address");
admin = new Admin( );// 用这个对象封装数据库的数据
admin.setUsername(username1);
admin.setPassword(password1);
admin.setPhone(phone);
admin.setAddress(address);
// 存入集合
list.add(admin);
}
} catch (SQLException e) {
e.printStackTrace( );
} finally {
DBUtil.closeAll(conn,ps,rs);
}
return list;
}
}
4.8 LoginServlet
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置编码
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
// 接收请求
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("username = " + username );
System.out.println("password = " + password );
/**
* 中间处理业务逻辑的代码
* 需要单独创建业务层类(service),数据访问层类(dao)
* 不要把所有事情都交给servlet,要做到单一职责
*/
AdminService service = new AdminServiceImpl();
Admin admin = service.login(username, password);
PrintWriter out = resp.getWriter( );
// 做出响应
if (admin != null) {
// 登录成功,展现全部
List<Admin> list = service.findAll( );
out.write("<html>");
out.write("<h2 style='color:green'>欢迎"+username+"登录管理员系统</h2>");
out.write("<hr>");
out.write("<table border='2'>");
out.write("<tr>");
out.write(" <td>用户名</td>");
out.write(" <td>密码</td>");
out.write(" <td>手机号</td>");
out.write(" <td>地址</td>");
out.write("</tr>");
for (int i = 0; i < list.size(); i++) {
Admin obj = list.get(i);
out.write("<tr>");
out.write(" <td>"+obj.getUsername()+"</td>");
out.write(" <td>"+obj.getPassword()+"</td>");
out.write(" <td>"+obj.getPhone()+"</td>");
out.write(" <td>"+obj.getAddress()+"</td>");
out.write("</tr>");
}
out.write("</table>");
out.write("</html>");
} else {
// 登录失败
out.write("<html>");
out.write("<h1 style='color:red'>用户名或密码错误</h1>");
out.write("</html>");
}
}
}