▶【SecKill】U1 项目框架搭建
一、SpringBoot环境搭建
1、新建Maven Project
【报错】Could not get the value for parameter encoding for plugin execution default-resources
原因:maven包无法下载下来
【解决方法】选中项目右键 → Maven → Update Proejct
等更新完就可以再启动项目试试(一般情况下网络顺畅稳定,此方法可行),如果还是不行就去网上找到对应版本jar下载下来的,手动放到maven指定文件夹里
2、在pom.xml中添加依赖
3、搭建 简易 的SpringBoot框架
(1)删除src/test/java测试类
(2)改包名
【注】SpringBoot是在SpringMVC的基础上,做了封装。
SpringBoot开发程序 = SpringMVC(controller调用service,service调用DAO 的分层结构开发)
(3)分层结构controller、service、dao
① controller
② service
③ dao
(4)测试Demo1:新建DemoController类,添加代码(官网Spring Quickstart Guide简易项目代码)
package com.kirin.miaosha.controller; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @EnableAutoConfiguration public class DemoController { @RequestMapping("/") @ResponseBody String home() { return "Hello World!"; } public static void main(String[] args) throws Exception { SpringApplication.run(DemoController.class, args); } }
(5)运行测试
成功!
4、测试Demo2
(1)在com.kirin.miaosha中新建class类:MainApplication.class
(2)将DemoController.java中的这2段放入MainApplication.class中
DemoController.java:
MainApplication.class:
package com.kirin.miaosha; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MainApplication { public static void main(String[] args) throws Exception { SpringApplication.run(MainApplication.class, args); } }
(3)运行MainApplication.class
【注】运行新的Tomcat前,先按停止键,暂停之前正在运行的Tomcat
5、Controller中的方法:
(1)rest api json输出
【例】
新建com.kirin.miaosha.result包
在com.kirin.miaosha.result包下,新建Result类
在Result类中,定义变量code、msg、data
生成get()、set()方法
package com.kirin.miaosha.result; public class Result<T> { //<T>:泛型(广泛的类型) private int code; private String msg; private T data; //不知道data的类型,则定义为T,在类后声明<T>,表示T为泛型 //生成get、set方法 public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
DemoController.java:
package com.kirin.miaosha.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.kirin.miaosha.result.Result; @Controller public class DemoController { @RequestMapping("/") @ResponseBody String home() { return "Hello World!"; } //Controller中的方法: //1.rest api json输出 //【例】com.kirin.miaosha.result包下Result类 @RequestMapping("/hello") @ResponseBody String hello() { Result.success(data); return new Result(0,"success","helo,kirin"); } @RequestMapping("/helloError") @ResponseBody String helloError() { Result.error(CodeMsg); return new Result(500102,"XXX"); return new Result(500101,"XXX"); return new Result(500100,"session失效"); } }
Result.java:
CodeMsg.java:
package com.kirin.miaosha.result; public class CodeMsg { private int code; private String msg; //定义通用异常 public static CodeMsg SUCCESS = new CodeMsg(0, "success"); public static CodeMsg SERVER_ERROR = new CodeMsg(500100, "服务端异常"); private CodeMsg(int code, String msg) { this.code = code; this.msg = msg; } //生成get、set方法(后来发现用不到set方法,删除) public int getCode() { return code; } public String getMsg() { return msg; } }
Result.java:
package com.kirin.miaosha.result; public class Result<T> { //<T>:泛型(广泛的类型) private int code; private String msg; private T data; //不知道data的类型,则定义为T,在类后声明<T>,表示T为泛型 //com.kirin.miaosha.controller包下DemoController.java //成功时调用 public static <T> Result<T> success(T data){ return new Result<T>(data); //写完后报错:生成一个构造函数private Result(T data) } //失败时调用 public static <T> Result<T> error(CodeMsg cm){ //报错:自动生成CodeMsg.java类 return new Result<T>(cm); //写完后报错:生成一个构造函数private Result(CodeMsg cm) } //public static <T> Result<T> success(T data)报错后,自动生成的构造函数 private Result(T data) { this.code = 0; //成功 this.msg = "success"; this.data = data; } //public static <T> Result<T> error(CodeMsg cm)报错后,自动生成的构造函数 private Result(CodeMsg cm) { if(cm == null) { return; } this.code = cm.getCode(); this.msg = cm.getMsg(); } //生成get、set方法(后来发现用不到set方法,删除) public int getCode() { return code; }public String getMsg() { return msg; }public T getData() { return data; } }
DemoController.java:
package com.kirin.miaosha.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.kirin.miaosha.result.CodeMsg; import com.kirin.miaosha.result.Result; @Controller public class DemoController { @RequestMapping("/") @ResponseBody String home() { return "Hello World!"; } //Controller中的方法: //1.rest api json输出 //【例】com.kirin.miaosha.result包下Result类 @RequestMapping("/hello") @ResponseBody public Result <String> hello() { return Result.success("helo,kirin"); // return new Result(0,"success","helo,kirin"); } @RequestMapping("/helloError") @ResponseBody public Result <String> helloError() { return Result.error(CodeMsg.SERVER_ERROR); // return new Result(500100,"session失效"); } }
运行MainApplication.java
成功!
(2)前端页面
二、集成Thymeleaf(页面模板=JSP),Result结果封装
1、添加Thymeleaf依赖(前端=JSP)
<!-- thymeleaf依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
2、配置文件
spring.thymeleaf.cache=false spring.thymeleaf.content-type=text/html spring.thymeleaf.enabled=true spring.thymeleaf.encoding=UTF-8 spring.thymeleaf.mode=HTML5 spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.suffix=.html
3、Result结果封装
(1)DemoController.java:
//U1-2 集成Thymeleaf(页面模板=JSP),Result结果封装 @RequestMapping("/thymeleaf") public String Thymeleaf() { //返回页面模板 return "hello"; //在配置文件中配置hello的页面模板 }
(2)在配置文件中配置hello的页面模板
① 新建templates文件夹
② 在配置文件中配置hello的页面模板:src/main/resources/templates/hello.html
<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>hello</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <p th:text="'hello:'+${name}" ></p> </body> </html>
③ 定义一个对象
//U1-2 集成Thymeleaf(页面模板=JSP),Result结果封装 @RequestMapping("/thymeleaf") public String Thymeleaf(Model model) { //返回页面模板 model.addAttribute("name","kirin"); //定义一个对象 return "hello"; //在配置文件中配置hello的页面模板:src/main/resources/templates/hello.html }
运行MainApplication.java
成功!
原因:项目的配置没有及时更新with这个pom.xml文件,所以更新一下配置文件即可。
三、集成Mybatis(数据库)+Druid
1、添加pom.xml依赖
<!-- 4.添加Mybatis依赖 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency>
2、添加配置mybatis
① 新建包domain:访问根数据库的表
② 在application.properties中添加配置mybatis
# mybatis mybatis.type-aliases-package=com.kirin.miaosha.domain mybatis.configuration.map-underscore-to-camel-case=true mybatis.configuration.default-fetch-size=100 mybatis.configuration.default-statement-timeout=3000 mybatis.mapperLocations = classpath:com/kirin/miaosha/dao/*.xml
③ 在application.properties中添加配置druid(连接池)
# druid spring.datasource.url=jdbc:mysql://localhost:3306/miaosha spring.datasource.username=root spring.datasource.password=12345 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.filters=stat spring.datasource.maxActive=2 spring.datasource.initialSize=1 spring.datasource.maxWait=60000 spring.datasource.minIdle=1 spring.datasource.timeBetweenEvictionRunsMillis=60000 spring.datasource.minEvictableIdleTimeMillis=300000 spring.datasource.validationQuery=select 'x' spring.datasource.testWhileIdle=true spring.datasource.testOnBorrow=false spring.datasource.testOnReturn=false spring.datasource.poolPreparedStatements=true spring.datasource.maxOpenPreparedStatements=20
④ 在application.properties中添加配置Mysql
<!-- 5.添加Mysql依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.5</version> </dependency>
⑤ 连接数据库
A.新建数据库miaosha
CREATE DATABASE `miaosha`; USE `miaosha`;
B.新建数据库表user
C.新建user表对应的domain对象
package com.kirin.miaosha.domain; public class User { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
D.新建调用user表的接口UserDao
package com.kirin.miaosha.dao; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import com.kirin.miaosha.domain.User; @Mapper public interface UserDao { @Select("select * from user where id = #{id}") public User getById(@Param("id") int id); }
E.新建UserService
package com.kirin.miaosha.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.kirin.miaosha.dao.UserDao; import com.kirin.miaosha.domain.User; @Service public class UserService { @Autowired UserDao userDao; public User getById(int id) { return userDao.getById(id); } }
F.DemoController.java
在数据库插入1条记录
//U1-4 数据库测试:获取id @Autowired UserService userService; @RequestMapping("/db/get") @ResponseBody public Result<User> dbGet() { User user = userService.getById(1); return Result.success(user); }
G.运行
成功!
H.数据库测试2:事务
DemoController.java:
在UserDao.java写插入语句
UserService.java:
I. 运行
数据库中 id=2,name=2222 的记录没有插入,因为这是一条事务
若去掉事务标签,
则数据库中 id=2,name=2222 的记录插入
四、集成Jedis+Redis安装+通用缓存Key封装
1、Linux服务器准备工作
(1)Navicat连接Linux服务器的MYSQL
(2)新建miaosha数据库
① 连接数据库
启动服务
systemctl start mysqld.service
登录
mysql -uroot -p
密码为空
② 新建数据库:create database 数据库名;
create database miaosha;
设置数据库编码:以utf8为例
set names utf8;
选择数据库:use 数据库名;
use miaosha;
在Navicat出现miaosha数据库
③ 新建数据库表user
在数据库插入1条记录
④ 修改application.properties中服务器地址
⑤ 运行
2、安装Redis
(1)安装包redis-4.0.2.tar.gz
(2)将安装包拉进root文件夹下
(3)解压
tar -zxvf redis-4.0.2.tar.gz
解压后,出现目录redis-4.0.2
移动该文件夹到
mv redis-4.0.2 /usr/local/redis
(4)make
① 进入移动后的目录
② 用4个CPU make一下,可加快编译速度
make -j 4
make完成后,查看目录
③ 把编译完成的可执行文件添加到目录中
make install
完成!
④ 修改 redis.conf 配置文件
查看 redis.conf 配置文件
vi redis.conf
⑤ 修改 redis.conf 配置文件
按「i」:进入插入模式 按「ESC」:从插入模式切换为命令行模式 打 :w 保存文件但不退出vi 打 :wq 保存文件并退出vi
A. 使其可以让所有服务器访问
B. 允许后台执行
⑥ 查看Redis服务器是否可以访问
运行Redis服务器
redis-server ./redis.conf
(5)执行 install_server.sh,生成系统服务
进入 utils 下
执行 install_server.sh,生成系统服务
./install_server.sh
3、集成Redis
(1)在pom.xml添加Jedis和Fastjson依赖
<!-- 6.添加Jedis依赖 --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <!-- 7.添加Fastjson依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.38</version> </dependency>
(2)在application.properties配置Redis
#redis redis.host=192.168.xx.xx redis.port=6379 redis.timeout=3 redis.password=xxxxx redis.poolMaxTotal=10 redis.poolMaxIdle=10 redis.poolMaxWait=3
(3)
① 新建redis包
② 新建RedisConfig.java,加入配置文件的参数和方法
③ 新建其余7个class类
BasePrefix.java:
package com.kirin.miaosha.redis; public abstract class BasePrefix implements KeyPrefix{ private int expireSeconds; private String prefix; public BasePrefix(String prefix) { //0代表永不过期 this(0, prefix); } public BasePrefix( int expireSeconds, String prefix) { this.expireSeconds = expireSeconds; this.prefix = prefix; } public int expireSeconds() { //默认0代表永不过期 return expireSeconds; } public String getPrefix() { String className = getClass().getSimpleName(); return className+":" + prefix; } }
KeyPrefix.java:
package com.kirin.miaosha.redis; public interface KeyPrefix { public int expireSeconds(); //有效期 public String getPrefix(); //前缀 }
MiaoshaUserKey.java:
package com.kirin.miaosha.redis; public class MiaoshaUserKey extends BasePrefix{ public static final int TOKEN_EXPIRE = 3600*24 * 2; private MiaoshaUserKey(int expireSeconds, String prefix) { super(expireSeconds, prefix); } public static MiaoshaUserKey token = new MiaoshaUserKey(TOKEN_EXPIRE, "tk"); }
OrderKey.java:
package com.kirin.miaosha.redis; public class OrderKey extends BasePrefix { public OrderKey(int expireSeconds, String prefix) { super(expireSeconds, prefix); } }
RedisPoolFactory.java:
package com.kirin.miaosha.redis; @Service public class RedisPoolFactory { @Autowired RedisConfig redisConfig; @Bean public JedisPool JedisPoolFactory() { JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxIdle(redisConfig.getPoolMaxIdle()); poolConfig.setMaxTotal(redisConfig.getPoolMaxTotal()); poolConfig.setMaxWaitMillis(redisConfig.getPoolMaxWait() * 1000); JedisPool jp = new JedisPool(poolConfig, redisConfig.getHost(), redisConfig.getPort(), redisConfig.getTimeout()*1000, redisConfig.getPassword(), 0); return jp; } }
RedisService.java:
package com.kirin.miaosha.redis; @Service public class RedisService { @Autowired JedisPool jedisPool; //获取当前对象 public <T> T get(KeyPrefix prefix, String key, Class<T> clazz) { Jedis jedis = null; try { jedis = jedisPool.getResource(); //生成真正的key String realKey = prefix.getPrefix() + key; String str = jedis.get(realKey); T t = stringToBean(str, clazz); //将获取的值,从String类型转换成bean类型 return t; }finally { returnToPool(jedis); } } //设置对象 public <T> boolean set(KeyPrefix prefix, String key, T value) { Jedis jedis = null; try { jedis = jedisPool.getResource(); String str = beanToString(value); //将设置的值,从bean类型转换成String类型 if(str == null || str.length() <= 0) { return false; } //生成真正的key String realKey = prefix.getPrefix() + key; int seconds = prefix.expireSeconds(); if(seconds <= 0) { jedis.set(realKey, str); }else { jedis.setex(realKey, seconds, str); } return true; }finally { returnToPool(jedis); } } //判断key是否存在 public <T> boolean exists(KeyPrefix prefix, String key) { Jedis jedis = null; try { jedis = jedisPool.getResource(); //生成真正的key String realKey = prefix.getPrefix() + key; return jedis.exists(realKey); }finally { returnToPool(jedis); } } //将设置的值,从bean类型转换成String类型 private <T> String beanToString(T value) { if(value == null) { //若空,直接返回 return null; } Class<?> clazz = value.getClass(); if(clazz == int.class || clazz == Integer.class) { //若是int类型,直接写入 return ""+value; }else if(clazz == String.class) { //若是String类型,直接写入 return (String)value; }else if(clazz == long.class || clazz == Long.class) { //若是long类型,直接写入 return ""+value; }else { return JSON.toJSONString(value); } } //将获取的值,从String类型转换成bean类型 @SuppressWarnings("unchecked") private <T> T stringToBean(String str, Class<T> clazz) { if(str == null || str.length() <= 0 || clazz == null) { //若空,直接返回 return null; } if(clazz == int.class || clazz == Integer.class) { //若是int类型,强转为int类型 return (T)Integer.valueOf(str); }else if(clazz == String.class) { //若是String类型,直接输出 return (T)str; }else if(clazz == long.class || clazz == Long.class) { //若是long类型,强转为long类型 return (T)Long.valueOf(str); }else { return JSON.toJavaObject(JSON.parseObject(str), clazz); } } private void returnToPool(Jedis jedis) { if(jedis != null) { jedis.close(); } } }
UserKey.java:
package com.kirin.miaosha.redis; public class UserKey extends BasePrefix{ private UserKey(String prefix) { super(prefix); } }
DemoController.java:
测试: