秒杀(1)环境搭建
一、 配置thymeleaf
1.pom.xml文件
<?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>
<groupId>org.example</groupId>
<artifactId>it_javahjh</artifactId>
<version>1.0-SNAPSHOT</version>
<name>it_javahjh</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<!--加入thymeleaf依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.5</version>
</dependency>
<!-- jedis连接redis需要的jar-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!-- json-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.38</version>
</dependency>
<!-- 引入MD5工具类 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<!-- JSR303校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- springboot中使用注解导入properties文件需要的jar -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
application.properties文件,默认的Thymeleaf模版文件放置位置如下:
#thymeleaf
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
测试环境
controller层:
@Controller
@RequestMapping("/demo")
public class SampleController {
@RequestMapping("/hello")
public String thymeleaf(Model model){
model.addAttribute("name","joshua");
return "hello";
}
在resource中创建template创建页面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>
测试结果
网页输入地址如下:
二、集成mybatis
application.properties配置
# mybatis
mybatis.type-aliases-package=org.example.domain
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.default-fetch-size=100
mybatis.configuration.default-statement-timeout=3000
mybatis.mapper-locations=classpath:org/example/dao/*.xml
#数据源
spring.datasource.url=jdbc:mysql://localhost:3306/miaosha?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=123
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
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
mysql中新建User表,并在表中加入一条数据:
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
创建实体类,并生成get,set方法。
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;
}
}
dao层接口:
@Mapper
public interface UserDao {
@Select("select * from user where id = #{id}")
public User getById(@Param("id") int id) ;
@Insert("insert into user(id,name)value(#{id},#{name}) ")
public int insert(User user);
}
service层:
@Service
public class UserService {
@Autowired
UserDao userDao;
public User getById(int id) {
return userDao.getById(id);
}
@Transactional //事务说明
public boolean tx() {
User u1=new User();
u1.setId(2);
u1.setName("222");
userDao.insert(u1);
User u2=new User();
u2.setId(1);
u2.setName("111");
userDao.insert(u2);
return true;
}
}
创建返回值类——CodeMsg类:
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,"服务端异常"); public CodeMsg(int code, String msg) {
this.code = code;
this.msg = msg;
}
}
创建Result类:
public class Result<T> {
private int code;
private String msg;
private T data;
/**
* 成功时候的调用
* */
public static <T> Result<T> success(T data){
return new Result<T>(data);
}
/**
* 失败时候的调用
* */
public static <T> Result<T> error(CodeMsg codeMsg){
return new Result<T>(codeMsg);
}
private Result(T data) {
this.data = data;
}
private Result(int code, String msg) {
this.code = code;
this.msg = msg;
}
private Result(CodeMsg codeMsg) {
if(codeMsg != null) {
this.code = codeMsg.getCode();
this.msg = codeMsg.getMsg();
}
}
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;
}
}
controller层:
@Controller
@RequestMapping("/demo")
public class SampleController {
@Autowired
private UserService userService;
@RequestMapping("/db/get")
@ResponseBody
public Result<User> dbGet() {
User user= userService.getById(1);
return Result.success(user);
}
@RequestMapping("/db/tx")
@ResponseBody
public Result<Boolean> dbTx() {
userService.tx();
return Result.success(true);
}
}
测试:
三、集成redis
application.properties配置:
#redis
redis.host=127.0.0.1
redis.port=6379
redis.timeout=3
redis.password=xxxxx//自己的redis密码
redis.poolMaxTotal=10
redis.poolMaxIdle=10
redis.poolMaxWait=3
创建配置文件对应的实体类读取配置文件(对应application.properties中的配置):
@Component
@ConfigurationProperties(prefix = "redis")
public class RedisConfig {
private String host;
private int port;
private int timeout;//秒
private String password;
private int poolMaxTotal;
private int poolMaxIdle;
private int poolMaxWait;//秒
//需要自己生成set,get方法
}
创建redisService,整合redis具体方法:
redis与spring的整合一般分为spring-data-redis整合和jedis整合,先看看两者的区别:
1、引用的依赖不同:
spring-data-redis使用的依赖如下:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.9.RELEASE</version>
</dependency>
jedis使用的依赖如下:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
2、管理jedis实例方式、操作redis服务的不同:
spring-data-redis:
通过org.springframework.data.redis.connection.jedis.JedisConnectionFactory来管理,即通过工厂类管理,然后通过配置的模版bean,操作redis服务,代码段中充斥大量与业务无关的模版片段代码,代码冗余,不易维护。
jedis方式:
通过redis.clients.jedis.JedisPool来管理,即通过池来管理,通过池对象获取jedis实例,然后通过jedis实例直接操作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();//用jedispool获得jedis对象
//生成真正的key
String realKey=prefix.getPrefix()+key;
String str= jedis.get(realKey);
T t=StringToBean(str,clazz);
int seconds=prefix.expireSeconds();
if (seconds<=0){
jedis.set(realKey,str);
}else {
jedis.setex(realKey,seconds,str);
}
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);
if(str == null || str.length() <= 0) {
return false;
}
//生成真正的key
String realKey=prefix.getPrefix()+key;
jedis.set(realKey,str);
return true;
}finally {
returnToPool(jedis);
}
}
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) {
return (T)Integer.valueOf(str);
}else if(clazz == String.class) {
return (T)str;
}else if(clazz == long.class || clazz == Long.class) {
return (T)Long.valueOf(str);
}else {
return JSON.toJavaObject(JSON.parseObject(str), clazz);
}
}
private <T> String beanToString(T value) {
if (value == null) {
return null;
}
Class<?> clazz = value.getClass();
if (clazz == int.class || clazz == Integer.class) {
return "" + value;
} else if (clazz == String.class) {
return (String) value;
} else if (clazz == long.class || clazz == Long.class) {
return "" + value;
} else {
return JSON.toJSONString(value);
}
}
private void returnToPool(Jedis jedis) {
if (jedis!=null){
jedis.close();
}
}
//判断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);
}
}
//增加值
public <T> Long incr(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.incr(realKey);
}finally {
returnToPool(jedis);
}
}
//减少值
public <T> Long decr(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.decr(realKey);
}finally {
returnToPool(jedis);
}
}
}
创建RedisPool连接工厂:
@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;
}
}
测试
创建接口KeyPreFifx:
Prefix,key前缀。redis存储的都是键值对<key,value>,多人开发防止不同的人使用相同的key,覆盖了本来就写好的key,从而影响数据。
public interface KeyPrefix {
public int expireSeconds();//有效期
public String getPrefix();//前缀
}
创建抽象类BasePrefix:
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;
}
}
创建UserKey继承抽象类BasePrefix
public class UserKey extends BasePrefix{
private UserKey(String prefix) {
super(prefix);
}
public static UserKey getById = new UserKey("id");
public static UserKey getByName = new UserKey("name");
}
Controller层添加方法:
@RequestMapping("/redis/get")
@ResponseBody
public Result<User> redisGet() {
User user=redisService.get(UserKey.getById, ""+1, User.class);
return Result.success(user);
}
@RequestMapping("/redis/set")
@ResponseBody
public Result<Boolean> redisSet() {
User user=new User();
user.setId(1);
user.setName("1111");
redisService.set(UserKey.getById, ""+1, user);
return Result.success(true);
}
启动项目:输入localhost:8080/demo/redis/set插入一条数据,在输入localhost:8080/demo/redis/get查询出刚才插入的数据。