少女祈祷中...

Java Web开发

一、HTTP协议

协议:网络中计算机之间为能够互相通信所制定的一些规则

协议栈:TCP/IP的四层协议栈

数据包:TCP/IP协议簇可能会将应用层发送的数据分成多个包依次发送,例如段、包、帧

客户端服务器模式:

  • 客户端发起请求,服务器回送响应
  • HTTP是一个无状态协议(非Keep-Alive模式下,每次建立连接完全独立,每次发出请求,服务器返回响应,链接就被关闭了)

HTTP工作过程

  • 地址解析:如http://localhost.com:8080/index.htm,分解得到协议名、主机名、端口、对象路径
  • 封装HTTP请求数据包:把以上部分结合本机自己的信息,封装成一个HTTPi请求数据包
  • 封装成TCP包,建立TCP连接(三次握手),客户机先与服务器建立连接
  • 客户机发送请求命令:例如GET/sample/hello.jsp HTTP/1.1
  • 服务器响应:服务器接收到请求后给与响应信息,如HTTP/1.1 200 OK。然后以Content-Type应答头信息所描述的格式发送用户请求的实际数据
  • 一般服务器发送响应数据后就关闭TCP连接,但是如果头信息中有Connection:Keep-Alive,就可以保持打开状态,继续通过相同的连接发送请求

URL格式请求

三部分:协议类型、服务器地址(和端口号)、路径。可以使用携带键值对形式的参数,以问号开头,采用name=value形式,多个URL参数之间使用&隔开

1
http://blog.csdn.com/users?gender=male

Request

常用的方法:GET和POST

  • GET用于获取资源,不会对服务器数据进行修改,所以不发送body
  • POST方法用于增加或修改资源,它会将发送给服务器的内容写在Body里面

Response

常用状态码:

  • 1xx:临时性消息。如:100(继续发送)、101(正在切换协议);
  • 2xx:成功。最典型的是200(OK)、201(创建成功);
  • 3xx:重定向。如:301(永久移动)302(暂时移动)、304(内容未改变);
  • 4xx:客户端错误。如:400(客户端请求错误)、401(认证失败)、403(被禁⽌)、404(找不到内
    容);
  • 5xx:服务器错误。如:500(服务器内部错误)

二、Tomcat和Servlet原理

Tomcat

是一个Java语言编写的运行在JVM之上的Web服务器,它和HTTP服务器一样,绑定IP地址并监听TCP端口,也能:

  • 管理Servlet程序的声明周期
  • 将URL映射到制定的Servlet进行处理
  • 与Servlet程序合作处理由Web浏览器发俩的HTTP请求,生成HttpServlet程序对象并传递给Servlet进行处理,将Servlet中的HttpServletResponse对象生成的内容返回给服务器

Servlet

java编写的服务器端程序,交互式地浏览和修改数据,生成动态Web内容

运行在Web服务器上,作为来自浏览器的请求和HTTP服务器数据库或应用程序之间的中间层。

典型的生命周期:

  • HTTP请求被委派到Servlet容器
  • 容器调用service()方法前加载Servlet类
  • 容器处理由多个线程产生的多个请求,每个线程执行单一service方法

service方法:处理请求,把格式话的数据写回客户端。

  • 每次收到一个请求就产生一个新的进程调用服务
  • 检查HTTP请求类型,在适当的时候调用doGet、doPost等方法

不用对service()方法做任何动作

具体使用:

  • 先编写如下类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 导⼊必需的 java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
// 扩展 HttpServlet 类
public class HelloWorld extends HttpServlet {
private String message;
public void init() throws ServletException
{
// 执⾏必需的初始化
message = "Hello World";
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// 设置响应内容类型
response.setContentType("text/html");
// 实际的逻辑是在这⾥
PrintWriter out = response.getWriter();
out.println("<h1>" + message + "</h1>");
}
public void destroy()
{
//
}
}

然后:

三、REST和JSON

REST风格的提出:Roy Fielding博士,迅速取代SOAP成为Web API的标准

Web API与前后端分离

如果Url返回的不是HTML,而是机器能直接解析的数据,这个URL就可以看成是一个Web API:

例如,由于JSON能直接被js读取,所以以JSON格式编写的WebAPI,即REST API,具有简单、易读、易用的特点

规范:除了GET请求外,POST\PUT等请求的body是JSON数据格式,请求的Content-Type为application/json

JSON

本质是一个正则表达式,parser是一个有限状态机

四、Spring Boot入门

用来简化新Spring应用的初始搭建以及开发过程。

  • 简化依赖
  • 简化配置
  • 简化部署
  • 简化监控

POM文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>…</parent>
<properties>…</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

程序入口:

main方法必须放到根package下,命名不做要求。

1
2
3
4
5
6
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Controller

对URL路径的响应是通过HelloController类来实现的

1
2
3
4
5
6
7
8
9
10
11
// HelloController.java
package hello;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
public class HelloController {
@RequestMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
}

启动和浏览器访问

启动项目的三种方式:

  • 在IDE运行Application的main方法
  • 使用命令mvn spring-boot:run
  • 运行mvn package生成可运行JAR文件,使用java -jar直接运行

五、数据设计

数据持久化:数据只有能够被记录存储回忆才能成为知识

关系型数据库:关系

⽤户的需求及其业务领域出发,进过分析和总结,提炼出来⽤以描述⽤户业务的概念类,以及概念类之间的关系。

主要关心类和类之间的关系

逻辑模型

就是将概念模型具体化,要实现概念的功能的时候具体需要的实体和属性

物理模型

对逻辑模型在真实数据库中的实现的表现

将类图转换为关系表

转换过程:

  • 一个类/对象映射为一张表(属性=列名,对象=行)
  • 为每个转换后的表建立主键
  • 处理关联(类/对象的来链接–关系/表通过主键/外键实现关联)

SQL语句

结构化查询语言,管理关系数据库管理系统的一套标准语言

JDBC原理

Java数据库连接是Java语言中用来规范客户端程序如何访问数据库管理系统的应用程序接口

使用JDBC完成一个SQL操作的示例代码:

1
2
3
4
5
6
7
8
9
10
11
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2. 获得数据库连接
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
//3.操作数据库,实现增删改查
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT user_name, age FROM imooc_goddess");
//如果有数据,rs.next()返回true
while(rs.next()){
System.out.println(rs.getString("user_name")+" 年龄:"+rs.getInt("age"));
}

DAO

数据存取对象,位于业务逻辑层和数据层之间实现以面向对象API为形式的对持久化数据进行CRUD操作的对象

就是将数据库操作都封装起来。

实现以下几个部分:

  • DAO接口:把对数据库的所有操作定义成抽象方法,可以提供多种实现
  • DAO实现类:针对不同数据库给出DAO接口定义方法的具体实现
  • 实体类:用于存放与传输对象数据
  • 数据库连接和关闭工具类
1
2
3
4
5
6
7
8
//DAO 接⼝:
public interface PetDao {
/**
* 查询所有宠物
*/
List<Pet> findAllPets() throws Exception;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//DAO 实现类:
public class PetDaoImpl extends BaseDao implements PetDao {
/**
* 查询所有宠物
*/
public List<Pet> findAllPets() throws Exception {
Connection conn=BaseDao.getConnection();
String sql="select * from pet";
PreparedStatement stmt= conn.prepareStatement(sql);
ResultSet rs= stmt.executeQuery();
List<Pet> petList=new ArrayList<Pet>();
while(rs.next()) {
Pet pet=new Pet(
rs.getInt("id"),
rs.getInt("owner_id"),
rs.getInt("store_id"),
rs.getString("name"),
rs.getString("type_name"),
rs.getInt("health"),
rs.getInt("love"),
rs.getDate("birthday")
);
petList.add(pet);
}
BaseDao.closeAll(conn, stmt, rs);
return petList;
}
}

1
2
3
4
5
6
7
8
9
10
11
//宠物实体类(省略了getter和setter⽅法)
public class Pet {
private Integer id;
private Integer ownerId; //主⼈ID
private Integer storeId; //商店ID
private String name; //姓名
private String typeName; //类型
private int health; //健康值
private int love; //爱⼼值
private Date birthday; //⽣⽇
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//通过JDBC连接数据库
public class BaseDao {
private static String driver="com.mysql.jdbc.Driver";
private static String url="jdbc:mysql://127.0.0.1:3306/epet";
private static String user="root";
private static String password="root";
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
public static void closeAll(Connection conn,Statement stmt,ResultSet rs)
throws SQLException {
if(rs!=null) {
rs.close();
}
if(stmt!=null) {
stmt.close();
}
if(conn!=null) {
conn.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public int executeSQL(String preparedSql, Object[] param) throws ClassNotFoundException {
Connection conn = null;
PreparedStatement pstmt = null;
/* 处理SQL,执⾏SQL */
try {
conn = getConnection(); // 得到数据库连接
pstmt = conn.prepareStatement(preparedSql); // 得到PreparedStatement对象
if (param != null) {
for (int i = 0; i < param.length; i++) {
pstmt.setObject(i + 1, param[i]); // 为预编译sql设置参数
}
}
ResultSet num = pstmt.executeQuery(); // 执⾏SQL语句
} catch (SQLException e) {
e.printStackTrace(); // 处理SQLException异常
} finally {
try {
BaseDao.closeAll(conn, pstmt, null);
} catch (SQLException e) {
e.printStackTrace();
}
}
return 0;
}
}

使用MySQL时添加如下依赖:

1
2
3
4
5
6
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

在application.yml中修改用户密码

1
2
3
4
5
6
spring:
datasource:
url: jdbc:mysql://localhost:3306/mysql_test
username: root
password: root
driver: com.mysql.jdbc.Driver

六、Mybatis

ORM:结合面向编程的思想,可以将关系数据库中的关系用对象来表达。需要对象关系映射(Object Relation Mapping)

例如,如果要执行:
SELECT id, first_name, last_name, phone, birth_date, sex FROM persons WHERE id = 10

如果是逻辑代码,就是:

1
2
res = db.execSql(sql); 
name = res[0]["FIRST_NAME"];

如果是ORM,就是

1
2
p = Person.get(10);
name = p.first_name

ORM具体框架:

最上层是数据访问接口,中间是数据处理层

ORM是Object和Relation之间的映射

  • Object->Relation
  • Relation->Object

Hibernate和MyBatis是常见的两种ORM模型

  • Hibernate是个完整的ORM框架
  • MyBatis完成的是Relation->Object

Mybatis的安装与使用:只需将 mybatis-x.x.x.jar ⽂件置于类路径(classpath)中即可

使用Maven:

1
2
3
4
5
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>

SpringBoot中

1
2
3
4
5
6
<!--spring boot mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>

具体的MyBatis相关的文件包括

XML文件:

1
2
3
4
5
6
7
@Mapper
@Repository
public interface AdminMapper {
int addManager(User user);
List<User> getAllManagers();
}

PO对象

1
2
3
4
5
6
7
8
9
10
public class User { //省略getter、setter
private Integer id;
private String email;
private String password;
private String userName;
private String phoneNumber;
private double credit;
private UserType userType;
}

Mapper实现的XML文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.hotel.data.admin.AdminMapper">
<insert id="addManager" parameterType="com.example.hotel.po.User" useGeneratedKeys="true" keyProperty="id">
insert into User(email,password,usertype)
values(#{email},#{password},#{userType})
</insert>
<select id="getAllManagers" resultMap="User">
select * from User where usertype='HotelManager'
</select>
<resultMap id="User" type="com.example.hotel.po.User">
<id column="id" property="id"></id>
<result column="email" property="email"></result>
<result column="password" property="password"></result>
<result column="username" property="userName"></result>
<result column="phonenumber" property="phoneNumber"></result>
<result column="credit" property="credit"></result>
<result column="usertype" property="userType"></result>
</resultMap>
</mapper>

项目POM文件

1
2
3
4
5
6
7
8
9
10
11
12
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

application.yml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring:
datasource:
url: jdbc:mysql://localhost:3306/Hotel?serverTimezone=CTT&characterEncoding=UTF-8
username: root
password: ******
driver-class-name: com.mysql.cj.jdbc.Driver
max-active: 200
max-idle: 20
min-idle: 10
thymeleaf:
cache: false
jackson:
time-zone: GMT+8
mybatis:
mapper-locations: classpath:dataImpl/*/*Mapper.xml

七、Spring Boot + Mybatis

项目结构

后端调用流程:controller->bl->blImpl->data->dataImpl

controller

源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RestController
@RequestMapping("/api/coupon")
public class CouponController {
@Autowired
private CouponService couponService;
@PostMapping("/hotelTargetMoney")
public ResponseVO
addHotelTargetMoneyCoupon(@RequestBody
HotelTargetMoneyCouponVO hotelTargetMoneyCouponVO) {
CouponVO couponVO =
couponService.addHotelTargetMoneyCoupon(hotelTargetMoneyCou
ponVO);
return ResponseVO.buildSuccess(couponVO);
}
@GetMapping("/hotelAllCoupons")
public ResponseVO getHotelAllCoupons(@RequestParam Integer
hotelId) {
return
ResponseVO.buildSuccess(couponService.getHotelAllCoupon(hotelI
d));
}
}

bl:

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface CouponService {
/**
* 返回某⼀订单可⽤的优惠策略列表
* @param orderVO
* @return
*/
List<Coupon> getMatchOrderCoupon(OrderVO
orderVO); ## Use CouponVO instead of Coupon
/**
* 查看某个酒店提供的所有优惠策略(包括失效
的)
* @param hotelId
* @return
*/
List<Coupon> getHotelAllCoupon(Integer
hotelId);
/**
* 添加酒店满减优惠策略
* @param couponVO
* @return
*/
CouponVO
addHotelTargetMoneyCoupon(HotelTargetMoneyC
ouponVO couponVO);
}

bllmpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@Service
public class CouponServiceImpl implements CouponService {
private final TargetMoneyCouponStrategyImpl targetMoneyCouponStrategy;
private final TimeCouponStrategyImpl timeCouponStrategy;
private final CouponMapper couponMapper;
private static List<CouponMatchStrategy> strategyList = new ArrayList<>();
@Autowired
public CouponServiceImpl(TargetMoneyCouponStrategyImpl targetMoneyCouponStrategy,
TimeCouponStrategyImpl timeCouponStrategy,
CouponMapper couponMapper) {
this.couponMapper = couponMapper;
this.targetMoneyCouponStrategy = targetMoneyCouponStrategy;
this.timeCouponStrategy = timeCouponStrategy;
strategyList.add(targetMoneyCouponStrategy);
strategyList.add(timeCouponStrategy);
}
@Override
public List<Coupon> getMatchOrderCoupon(OrderVO orderVO) {
List<Coupon> hotelCoupons = getHotelAllCoupon(orderVO.getHotelId());
List<Coupon> availAbleCoupons = new ArrayList<>();
for (int i = 0; i < hotelCoupons.size(); i++) {
for (CouponMatchStrategy strategy : strategyList) {
if (strategy.isMatch(orderVO, hotelCoupons.get(i))) {
availAbleCoupons.add(hotelCoupons.get(i));
}
}
}
return availAbleCoupons;
}

@Override
public List<Coupon>
getHotelAllCoupon(Integer hotelId) {
List<Coupon> hotelCoupons =
couponMapper.selectByHotelId(hotelId);
return hotelCoupons;
}
@Override
public CouponVO
addHotelTargetMoneyCoupon(HotelTarge
tMoneyCouponVO couponVO) {
Coupon coupon = new Coupon();

coupon.setCouponName(couponVO.getN
ame());

coupon.setDescription(couponVO.getDes
cription());

coupon.setCouponType(couponVO.getTy
pe());

coupon.setTargetMoney(couponVO.getTa
rgetMoney());

coupon.setHotelId(couponVO.getHotelId()
);

coupon.setDiscountMoney(couponVO.get
DiscountMoney());
coupon.setStatus(1);
int result =
couponMapper.insertCoupon(coupon);
couponVO.setId(result);
return couponVO;
}
}

data

代码:

1
2
3
4
5
6
7
8
9
10
11
package com.example.hotel.data.coupon;
import com.example.hotel.po.Coupon;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface CouponMapper {
int insertCoupon(Coupon coupon);
List<Coupon> selectByHotelId(Integer hotelId);
}

dataimpl

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://
mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.hotel.data.coupon.CouponMapper">
<insert id="insertCoupon" parameterType="com.example.hotel.po.Coupon"
useGeneratedKeys="true" keyProperty="id">
insert into
coupon(description,couponName,target_money,discount_money,start_time,end_tim
e,hotelId,couponType,discount,status)

values(#{description},#{couponName},#{targetMoney},#{discountMoney},#{startTim
e},#{endTime},#{hotelId},#{couponType},#{discount},#{status})
</insert> <!-- 插⼊⼀个优惠券 -->
<select id="selectByHotelId" resultMap="Coupon">
select * from Coupon where hotelId=#{hotelId}
</select>
</mapper>

<resultMap id="Coupon" type="com.example.hotel.po.Coupon">
<result column="description" property="description"></result>
<result column="id" property="id"></result>
<result column="couponName" property="couponName"></result>
<result column="hotelId" property="hotelId"></result>
<result column="couponType" property="couponType"></result>
<result column="discount" property="discount"></result>
<result column="status" property="status"></result>
<result column="target_money" property="targetMoney"></result>
<result column="discount_money" property="discountMoney"></result>
<result column="start_time" property="startTime"></result>
<result column="end_time" property="endTime"></result>
</resultMap>