@Bean
public Docket docket() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
/**
* 设置静态资源映射
* @param registry
*/
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
// 123456 => e10adc3949ba59abbe56e057f20f883e
password = DigestUtils.md5DigestAsHex(password.getBytes());
<pagehelper>1.3.0pagehelper>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
<version>${pagehelper}version>
dependency>
@Override
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
// 开启分页查询,这条代码是关键
PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
// 查询数据
Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);
return new PageResult(page.getTotal(), page);
}
<update id="update">
update employee
<set>
<if test="name != null">name = #{name},if>
<if test="username != null">username = #{username},if>
<if test="password != null">password = #{password},if>
<if test="status != null">status = #{status},if>
<if test="phone != null">phone = #{phone},if>
<if test="sex != null">sex = #{sex},if>
<if test="idNumber != null">id_number = #{idNumber},if>
<if test="updateTime != null">update_time = #{updateTime},if>
<if test="updateUser != null">update_user = #{updateUser},if>
set>
where id = #{id}
update>
/**
* 插入异常
* @param ex
* @return
*/
@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
// Duplicate entry 'ewqe1' for key 'employee.idx_us
String msg = ex.getMessage();
log.error("异常信息:{}", msg);
if (msg.contains("Duplicate entry")) {
return Result.error(msg.split(" ")[2] + "已存在!");
} else {
return Result.error("未知错误!");
}
}
/**
* 更新员工状态
* @param status
*/
@Override
public void updateStatus(Integer status, Long id) {
Employee employee = Employee.builder()
.id(id)
.status(status)
.updateTime(LocalDateTime.now())
.updateUser(BaseContext.getCurrentId())
.build();
employeeMapper.update(employee);
}
创建枚举类,区分update和insert
/**
* 数据库操作类型
*/
public enum OperationType {
/**
* 更新操作
*/
UPDATE,
/**
* 插入操作
*/
INSERT
}
利用注解来区分是否需要填充,创建注解@AutoFill
/**
* 用于公共字段自动填充,规定自动填充在参数第一位
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
OperationType value();
}
利用Spring的AOP,切面编程,利用反射给对象赋值
/**
* 公共字段自动填充相关常量
*/
public class AutoFillConstant {
/**
* 实体类中的方法名称
*/
public static final String SET_CREATE_TIME = "setCreateTime";
public static final String SET_UPDATE_TIME = "setUpdateTime";
public static final String SET_CREATE_USER = "setCreateUser";
public static final String SET_UPDATE_USER = "setUpdateUser";
}
/**
* 自定义切面,用来处理公共字段填充
*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
/**
* 切入点
*/
// 第一个*是返回值,*(..)是所有方法的所有参数,并且加了AutoFill的方法
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut() {
}
/**
* 自动填充公共字段
*
* @param joinPoint
*/
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint) {
log.info("自动填充公共字段...");
// 利用Java反射,获取被拦截方法的数据库操作类型
// 方法对象签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取方法上的注解
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
// 获取操作类型
OperationType operationType = autoFill.value();
// 获取实体对象,规定取第一位
Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) {
return;
}
Object entity = args[0];
// 准备赋值的数据
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
if (operationType == OperationType.INSERT) {
// 设置创建时变量
try {
// 获取对应方法
Method setCreateTime = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
// 通过反射为对象赋值
setCreateTime.invoke(entity, now);
setCreateUser.invoke(entity, currentId);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// 设置更新时变量
try {
// 获取对应方法
Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
// 通过反射为对象赋值
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
在阿里云上注册,并申请 对象存储 OSS ,并创建好Bucket。(在Bucket概览里记录好外网访问地址例如:oss-cn-beijing.aliyuncs.com)
创建AccessKey。(记录好key和密钥)
导入阿里云OssMaven依赖
在Maven项目中加入依赖项(推荐方式)
在Maven工程中使用OSS Java SDK,只需在pom.xml中加入相应依赖即可。以3.15.1版本为例,在中加入如下内容:
<dependency>
<groupId>com.aliyun.ossgroupId>
<artifactId>aliyun-sdk-ossartifactId>
<version>3.15.1version>
dependency>
如果使用的是Java 9及以上的版本,则需要添加jaxb相关依赖。添加jaxb相关依赖示例代码如下:
<dependency>
<groupId>javax.xml.bindgroupId>
<artifactId>jaxb-apiartifactId>
<version>2.3.1version>
dependency>
<dependency>
<groupId>javax.activationgroupId>
<artifactId>activationartifactId>
<version>1.1.1version>
dependency>
<dependency>
<groupId>org.glassfish.jaxbgroupId>
<artifactId>jaxb-runtimeartifactId>
<version>2.3.3version>
dependency>
为Oss创建一个配置文件类AliOssProperties,将数据交给spring管理。
@Component
@ConfigurationProperties(prefix = "sky.alioss")
@Data
public class AliOssProperties {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}
在Spring application.yml中加入对应数据。
sky:
alioss:
# 访问的外网地址
endpoint: ${sky.alioss.endpoint}
# AccessKey
access-key-id: ${sky.alioss.access-key-id}
# AccessSecret
access-key-secret: ${sky.alioss.access-key-secret}
# bucket的名字
bucket-name: ${sky.alioss.bucket-name}
application-dev.yml中根据自己实际情况更改。
sky:
alioss:
endpoint: oss-cn-beijing.aliyuncs.com
access-key-id: LTAI5tG3yF...
access-key-secret: 6K6yYA2MM...
bucket-name: sky-take-out...
创建AliOssUtil工具类封装官网实例代码。
官网实例代码。
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import java.io.ByteArrayInputStream;
public class Demo {
public static void main(String[] args) throws Exception {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 强烈建议不要把访问凭证保存到工程代码里,否则可能导致访问凭证泄露,威胁您账号下所有资源的安全。本代码示例以从环境变量中获取访问凭证为例。运行本代码示例之前,请先配置环境变量。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 填写Bucket名称,例如examplebucket。
String bucketName = "examplebucket";
// 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。
String objectName = "exampledir/exampleobject.txt";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
try {
String content = "Hello OSS";
ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content.getBytes()));
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}
封装的AliOssUtil。
package com.sky.utils;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;
@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
/**
* 文件上传
*
* @param bytes
* @param objectName
* @return
*/
public String upload(byte[] bytes, String objectName) {
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
// 创建PutObject请求。
ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
log.info("AliOss创建PutObject请求完毕!");
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
//文件访问路径规则 https://BucketName.Endpoint/ObjectName
StringBuilder stringBuilder = new StringBuilder("https://");
stringBuilder
.append(bucketName)
.append(".")
.append(endpoint)
.append("/")
.append(objectName);
log.info("文件上传到:{}", stringBuilder.toString());
return stringBuilder.toString();
}
}
创建配置类,用于生成AliOssUtil。
package com.sky.config;
import com.sky.properties.AliOssProperties;
import com.sky.utils.AliOssUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 配置类,用于生成AliOssUtil
*/
@Configuration
@Slf4j
public class AliOssConfiguration {
@Bean
// 保证只会创建一个
@ConditionalOnMissingBean
// 利用构造器注入aliOssProperties
public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties) {
log.info("开始创建阿里云Oss上传工具类:{}", aliOssProperties);
return new AliOssUtil(aliOssProperties.getEndpoint(),
aliOssProperties.getAccessKeyId(),
aliOssProperties.getAccessKeySecret(),
aliOssProperties.getBucketName());
}
}
最后创建upload方法,利用UUID解决文件名重复问题。
package com.sky.controller.admin;
import com.sky.result.Result;
import com.sky.utils.AliOssUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.UUID;
@RestController
@RequestMapping("/admin/common")
@Slf4j
@Api(tags = "通用接口")
public class CommonController {
@Resource
private AliOssUtil aliOssUtil;
/**
* 文件上传到AliOss
* @param file
* @return
*/
@PostMapping("/upload")
@ApiOperation("文件上传")
public Result<String> upload(MultipartFile file) {
log.info("文件上传:{}", file);
// 获取UUID
String uuid = UUID.randomUUID().toString();
// 原始文件名
String originalFilename = file.getOriginalFilename();
// 获取源文件后缀
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
// 新文件名
String filename = uuid + extension;
// 上传到阿里云Oss
try {
// 上传成功
String filePath = aliOssUtil.upload(file.getBytes(), filename);
return Result.success(filePath);
} catch (IOException e) {
log.error("文件上传失败:{}", e.toString());
return Result.error("文件上传失败");
}
}
}
阿里云Oss默认只能上传1MB大小的文件,可以在application.yml加入:
spring:
servlet:
multipart:
# 单个图片最大空间
max-file-size: 2MB
# 多个图片最大空间
max-request-size: 10MB
如果使用了Nginx作为代理,Nginx默认请求体大小也是最大1MB,可以在nginx.conf里加入:
# 上传文件大小限制
client_max_body_size 10m;
# 配置请求体缓存区大小
client_body_buffer_size 1m;
# http server location三个地方都可以加,作用范围不同
http {
#上传文件大小限制
client_max_body_size 10m;
#配置请求体缓存区大小
client_body_buffer_size 1m;
server {
#上传文件大小限制
client_max_body_size 10m;
#配置请求体缓存区大小
client_body_buffer_size 1m;
location / {
#上传文件大小限制
client_max_body_size 10m;
#配置请求体缓存区大小
client_body_bu ffer_size 1m;
}
}
最后记得重启Nginx服务器cmd: nginx -s reload(不行就重启电脑)。
在mapper.xml中insert 上配置
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into Dish (name, category_id, price, image, description, status, create_time, update_time, create_user, update_user)
values (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})
insert>
字符串String, key对应一个String, key => String
// 设置键
set key value => set name 小明
// 获取键
get key => get name
// 设置键和过期时间
setex key value time => setex code 669090 400
// 只有不存在时,设置键
setnx key value => setnx name 小明
哈希Hash,key对应一个HashMap, key => HashMap(field, value)
// 将Hash表key中field字段设置为value
hset key field value => hset class1 stu1 小明
// 获取hash指定field字段的值
hget key field => hget class1 stu1
// 删除hash表中指定字段
hdel key field => hdel class1 stu1
// 获取hash表中所有字段
hkeys key => hkeys key
// 获取hash表中所有值
hvals key => hvals key
列表List,key对应一个LinkedList, key => LinkedList(双向列表)
// 从左侧插入值 : 从左边开始放数据 , value2 在 value1 左边 , value3 在 value2 左边 ;
lpush key value1 value2 value3 ...
// 从右侧插入值 : 从右边开始放数据 , value2 在 value1 右边 , value3 在 value2 右边 ;
rpush key value1 value2 value3 ...
// 从左侧移除值 : 从 List 列表左侧移除一个值 , 如果所有的值都被移除 , 则 键 Key 也随之消亡 ;
lpop key
// 从右侧移除值 : 从 List 列表右侧移除一个值 , 如果所有的值都被移除 , 则 键 Key 也随之消亡 ;
rpop key
// 获取 key 列表 的长度 ;
llen key
// 获取指定下标索引的元素
lindex key index
// 根据下标获取元素 : 获取从 start 索引开始 , 到 stop 索引结束的元素值 ;
lrange key start stop
// 查询键对应的 List 列表值
lrange key 0 -1
// 在 key 列表 的 value 值 前面 / 后面 插入新值 newValue
linsert key before/after value newValue
// 在 key 列表 中 删除左侧的 n 个 value 值
lrem key n value
// key 列表 中 将 第 index 索引位置 的 元素 替换为 value 值
lset key index value
// 从 key1 列表右边取出一个值 , 放在 key2 列表的左边 ;
rpoplpush key1 key2
集合Set, key对应一个HashSet, key => HashSet(member)
// 向集合添加多个值
sadd key member1 member2 ...
// 返回集合所有成员
smembers key
// 返回集合成员数
scard key
// 返回所有集合的交集
sinter key1 key2 ...
// 返回所有集合的并集
sunion key1 key2 ...
// 删除集合中一个或多个成员
srem key member1 member2 ...
有序集合ZSet, key对应一个TreeSet, key => TreeSet(score, member), 根据score排序
// 添加一个或多个成员
zadd key score1 member1 score2 member2 ...
// 通过索引返回区间内成员,选择是否返回分数
zrange key start stop [withscores]
// 给指定成员加上增量increment
zincrby key increment member
// 删除集合中一个或多个成员
zrem key member1 member2 ...
通用命令
// 查询所有给定模式(pattern)的key
keys pattern
// 检查key是否存在
exists key
// 返回key存储的值的类型
type key
// 删除key
del key
maven中导入坐标
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
yml配置redis数据源
# application.yml
spring:
redis:
host: ${sky.redis.host}
port: ${sky.redis.port}
database: ${sky.redis.database}
password: ${sky.redis.password}
# application-dev.yml
sky:
redis:
host: localhost
port: 6379
database: 0
编写配置类创建RedisTemplate对象
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* 创建RedisTemplate对象
*/
@Configuration
@Slf4j
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
log.info("开始创建RedisTemplate:{}", redisConnectionFactory);
RedisTemplate redisTemplate = new RedisTemplate<>();
// 设置redis工厂连接
redisTemplate.setConnectionFactory(redisConnectionFactory);
// key的序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
// value 的序列化器
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
}
调用RedisTemplate操作redis
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import javax.annotation.Resource;
@SpringBootTest
public class SpringDataRedisTest {
@Resource
private RedisTemplate redisTemplate;
@Test
public void testRedis(){
redisTemplate.opsForValue().set("name", "小明");
Object name = redisTemplate.opsForValue().get("name");
System.out.println(name);
}
}
对于key和value的序列化都为String的可以使用StringRedisTemplate,spring已经自动创建好了
@Test
public void testStringRedisTemplate(){
stringRedisTemplate.opsForValue().set("key", "阿萨德");
System.out.println(stringRedisTemplate.opsForValue().get("key"));
}
导入Maven坐标
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.4.1version>
dependency>
封装成HttpclientUtil
package com.sky.utils;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Http工具类
*/
public class HttpClientUtil {
static final int TIMEOUT_MSE = 5 * 1000;
/**
* 发送GET方式请求
* @param url
* @param paramMap
* @return
*/
public static String doGet(String url,Map<String,String> paramMap){
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
String result = "";
CloseableHttpResponse response = null;
try{
URIBuilder builder = new URIBuilder(url);
if(paramMap != null){
for (String key : paramMap.keySet()) {
builder.addParameter(key,paramMap.get(key));
}
}
URI uri = builder.build();
//创建GET请求
HttpGet httpGet = new HttpGet(uri);
//发送请求
response = httpClient.execute(httpGet);
//判断响应状态
if(response.getStatusLine().getStatusCode() == 200){
result = EntityUtils.toString(response.getEntity(),"UTF-8");
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
response.close();
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 发送POST方式请求
* @param url
* @param paramMap
* @return
* @throws IOException
*/
public static String doPost(String url, Map<String, String> paramMap) throws IOException {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (paramMap != null) {
List<NameValuePair> paramList = new ArrayList();
for (Map.Entry<String, String> param : paramMap.entrySet()) {
paramList.add(new BasicNameValuePair(param.getKey(), param.getValue()));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
httpPost.setEntity(entity);
}
httpPost.setConfig(builderRequestConfig());
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (Exception e) {
throw e;
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
/**
* 发送POST方式请求
* @param url
* @param paramMap
* @return
* @throws IOException
*/
public static String doPost4Json(String url, Map<String, String> paramMap) throws IOException {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
if (paramMap != null) {
//构造json格式数据
JSONObject jsonObject = new JSONObject();
for (Map.Entry<String, String> param : paramMap.entrySet()) {
jsonObject.put(param.getKey(),param.getValue());
}
StringEntity entity = new StringEntity(jsonObject.toString(),"utf-8");
//设置请求编码
entity.setContentEncoding("utf-8");
//设置数据类型
entity.setContentType("application/json");
httpPost.setEntity(entity);
}
httpPost.setConfig(builderRequestConfig());
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (Exception e) {
throw e;
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
private static RequestConfig builderRequestConfig() {
return RequestConfig.custom()
.setConnectTimeout(TIMEOUT_MSE)
.setConnectionRequestTimeout(TIMEOUT_MSE)
.setSocketTimeout(TIMEOUT_MSE).build();
}
}
配置登录所需要的appid和secret,对应的jwt
# application.yml
sky:
jwt:
# jwt用户端配置
user-secret-key: itheima
user-ttl: 7200000
user-token-name: authorization
wechat:
# 小程序ID
appid: ${sky.wechat.appid}
# 小程序秘钥
secret: ${sky.wechat.secret}
#######################################
# application-dev.yml
sky:
wechat:
# 小程序ID
appid: wxb6****
# 小程序秘钥
secret: 2058e****
创建对应的配置类,交给spring管理
package com.sky.properties;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "sky.wechat")
@Data
public class WeChatProperties {
private String appid; //小程序的appid
private String secret; //小程序的秘钥
}
实现微信登录
package com.sky.constant;
/**
* 微信相关常量
*/
public class WXConstant {
public static final String WX_LONGIN_PATH = "https://api.weixin.qq.com/sns/jscode2session";
public static final String APP_ID = "appid";
public static final String SECRET = "secret";
public static final String JS_CODE = "js_code";
public static final String GRANT_TYPE= "grant_type";
public static final String OPEN_ID= "openid";
}
package com.sky.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sky.constant.MessageConstant;
import com.sky.constant.WXConstant;
import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;
import com.sky.exception.LoginFailedException;
import com.sky.mapper.UserMapper;
import com.sky.properties.WeChatProperties;
import com.sky.service.UserService;
import com.sky.utils.HttpClientUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.StringUtil;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* C端用户相关接口,服务层
*/
@Service
public class UserServiceImpl implements UserService {
@Resource
private WeChatProperties weChatProperties;
@Resource
private UserMapper userMapper;
/**
* 微信登录
* @param userLoginDTO
* @return
*/
public User wxLogin(UserLoginDTO userLoginDTO) {
// 调用微信接口获取用户openid
String openid = getOpenid(userLoginDTO);
// 判断openid是否合法
if (StringUtils.isBlank(openid)) {
// openid为空
throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
}
// 判断是否为新用户
User user = userMapper.getByOpenId(openid);
if (user == null) {
// 新用户注册
user = User.builder()
.openid(openid)
.createTime(LocalDateTime.now())
.build();
// 插入并返回id
userMapper.insert(user);
}
// 返回用户对象
return user;
}
/**
* 获取openid
* @param userLoginDTO
* @return
*/
private String getOpenid(UserLoginDTO userLoginDTO) {
// 封装请求参数
Map<String, String> map = new HashMap<>();
// 微信官方小程序已提供规范,去官网查看即可
map.put(WXConstant.APP_ID, weChatProperties.getAppid());
map.put(WXConstant.SECRET, weChatProperties.getSecret());
map.put(WXConstant.JS_CODE, userLoginDTO.getCode());
map.put(WXConstant.GRANT_TYPE, "authorization_code");
// 调用HttpClient请求微信接口
String json = HttpClientUtil.doGet(WXConstant.WX_LONGIN_PATH, map);
// fastjson变成JSONObject对象
JSONObject jsonObject = JSON.parseObject(json);
String openid = jsonObject.getString(WXConstant.OPEN_ID);
return openid;
}
}
导入Maven坐标
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
在配置文件中加入Redis的配置信息
# application.yml
spring:
redis:
host: ${sky.redis.host}
port: ${sky.redis.port}
database: ${sky.redis.database}
#password: ${sky.redis.password}
# application-dev.yml
sky:
redis:
host: localhost
port: 6379
database: 7
#password: 123456
在SpringBoot的启动类上加上注解@EnableCaching,即可开启Spring cache
@SpringBootApplication
@EnableTransactionManagement //开启注解方式的事务管理
@EnableCaching // 开启Spring Cache
@Slf4j
public class SkyApplication {
public static void main(String[] args) {
SpringApplication.run(SkyApplication.class, args);
log.info("server started");
}
}
注解使用例子
value、cacheNames:标明该缓存的的片区(两个属性一样的作用)
key:标明该缓存的key值,该值是Spel表达式,不是普通的字符串,如果我们要手动显示指定的话,必须用小括号才可以正常使用,如下所示:
@Cacheable(value = “category”, key = “#id”),框架为我们默认设置了一套规 则,常见的有:
key = “#root.methodName”、 key = "#root.args[1]"等,可参考官网说明
sync:当值为true时,相当于添加了本地锁,可以有效地解决缓存击穿问题
public static final String DISH_PREFIX = "dish";
@GetMapping("/list")
@Cacheable(cacheNames = DISH_PREFIX, key = "#categoryId")
@ApiOperation("根据分类id查询菜品")
public Result<List<DishVO>> list(Long categoryId) {
Dish dish = new Dish();
dish.setCategoryId(categoryId);
dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品
List<DishVO> list = dishService.listWithFlavor(dish);
return Result.success(list);
}
1.删除指定缓存
@CacheEvict(value = {“category”},key=“#id”)
2.删除value下所有的缓存
@CacheEvict(value = {“category”},allEntries = true)
public static final String DISH_PREFIX = "dish";
@PostMapping
@CacheEvict(cacheNames = DISH_PREFIX, key = "#dishDTO.categoryId")
@ApiOperation("新增菜品")
public Result saveWithFlavor(@RequestBody DishDTO dishDTO) {
dishService.saveWithFlavor(dishDTO);
return Result.success();
}
@DeleteMapping
@CacheEvict(cacheNames = DISH_PREFIX, allEntries = true)
@ApiOperation("批量删除菜品")
public Result delete(@RequestParam List<Long> ids) {
dishService.delete(ids);
return Result.success();
}
@CachePut(value = {“category”},key=“#id”)
public static final String SET_MEAL_PREFIX = "setmeal";
@PostMapping
@CachePut(cacheNames = SET_MEAL_PREFIX, key = "#setmealDTO.id")
@ApiOperation("新增套餐")
public Result save(@RequestBody SetmealDTO setmealDTO) {
log.info("新增套餐:{}", setmealDTO);
setmealService.save(setmealDTO);
return Result.success();
}
@CacheEvict注解中的allEntries = true属性会将当前片区中的所有缓存数据全部清除,请谨慎使用
@CacheEvict注解适用用于失效模式,也即更新完数据库数据后删除缓存数据
@CachePut注解用于适用于双写模式,更新完数据库后写入到缓存中配置文件中spring.cache.redis.cache-null-values=true一般需要设置(null值缓存),可以有效的防止缓存穿透
导入Maven坐标Spring context包下已有,
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>${mybatis.spring}version>
dependency>
在启动类加上@EnableScheduling
@SpringBootApplication
@EnableScheduling // 开启Spring Task
@Slf4j
public class SkyApplication {
public static void main(String[] args) {
SpringApplication.run(SkyApplication.class, args);
log.info("server started");
}
}
编写定时任务类。
@Component
@Slf4j
public class MyTask {
// 每五秒执行一次
@Scheduled(cron = "0/5 * * * * ?")
public void testTask() {
log.info("定时任务:{}", LocalDateTime.now());
}
}
cron表达式,按cron表达式来执行定时任务。
字段 允许值 允许的特殊字符 秒(Seconds) 0~59的整数 , - * / 四个字符 分(Minutes) 0~59的整数 , - * / 四个字符 小时(Hours) 0~23的整数 , - * / 四个字符 日期(DayofMonth) 1~31的整数(但是你需要考虑你月的天数) ,- * ? / L W C 八个字符 月份(Month) 1~12的整数或者 JAN-DEC , - * / 四个字符 星期(DayofWeek) 1~7的整数或者 SUN-SAT (1=SUN) , - * ? / L C # 八个字符 年(可选,留空)(Year) 1970~2099 , - * / 四个字符 cron表达式详解_**星光*的博客-CSDN博客
特殊字符 含义 示例 *
所有可能的值。 在月域中, *
表示每个月;在星期域中,*
表示星期的每一天。,
列出枚举值。 在分钟域中, 5,20
表示分别在5分钟和20分钟触发一次。-
范围。 在分钟域中, 5-20
表示从5分钟到20分钟之间每隔一分钟触发一次。/
指定数值的增量。 在分钟域中, 0/15
表示从第0分钟开始,每15分钟。在分钟域中3/20
表示从第3分钟开始,每20分钟。?
不指定值,仅日期和星期域支持该字符。 当日期或星期域其中之一被指定了值以后,为了避免冲突,需要将另一个域的值设为 ?
。L
单词Last的首字母,表示最后一天,仅日期和星期域支持该字符。说明 指定 L
字符时,避免指定列表或者范围,否则,会导致逻辑问题。在日期域中, L
表示某个月的最后一天。在星期域中,L
表示一个星期的最后一天,也就是星期日(SUN
)。如果在L
前有具体的内容,例如,在星期域中的6L
表示这个月的最后一个星期六。W
除周末以外的有效工作日,在离指定日期的最近的有效工作日触发事件。 W
字符寻找最近有效工作日时不会跨过当前月份,连用字符LW
时表示为指定月份的最后一个工作日。在日期域中 5W
,如果5日是星期六,则将在最近的工作日星期五,即4日触发。如果5日是星期天,则将在最近的工作日星期一,即6日触发;如果5日在星期一到星期五中的一天,则就在5日触发。#
确定每个月第几个星期几,仅星期域支持该字符。 在星期域中, 4#2
表示某月的第二个星期四。
{秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}
(1)0 0 2 1 * ? * 表示在每月的1日的凌晨2点调整任务
(2)0 15 10 ? * MON-FRI 表示周一到周五每天上午10:15执行作业
(3)0 15 10 ? 6L 2002-2006 表示2002-2006年的每个月的最后一个星期五上午10:15执行
(4)0 0 10,14,16 * * ? 每天上午10点,下午2点,4
(5)0 0/30 9-17 * * ? 朝九晚五工作时间内每半小
(6)0 0 12 ? * WED 表示每个星期三中午12
(7)0 0 12 * * ? 每天中午12点触
(8)0 15 10 ? * * 每天上午10:15触
(9)0 15 10 * * ? 每天上午10:15触
(10)0 15 10 * * ? * 每天上午10:15触
(11)0 15 10 * * ? 2005 2005年的每天上午10:15触
(12)0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触
(13)0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触
(14)0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触
(15)0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触
(16)0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
(17)0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
(18)0 15 10 15 * ? 每月15日上午10:15触发
(19)0 15 10 L * ? 每月最后一日的上午10:15触发
(20)0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
(21)0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
(22)0 15 10 ? * 6 #3 每月的第三个星期五上午10:15触发
websocket可以实现全双工通信。
导入Maven坐标
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
编写配置类将webSocket交给Spring管理
package com.sky.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebSocket配置类,用于注册WebSocket的Bean
*/
@Configuration
public class WebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
WebSocket服务
package com.sky.websocket;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* WebSocket服务
*/
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {
//存放会话对象
private static Map<String, Session> sessionMap = new HashMap<>();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
System.out.println("客户端:" + sid + "建立连接");
sessionMap.put(sid, session);
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, @PathParam("sid") String sid) {
System.out.println("收到来自客户端:" + sid + "的信息:" + message);
}
/**
* 连接关闭调用的方法
*
* @param sid
*/
@OnClose
public void onClose(@PathParam("sid") String sid) {
System.out.println("连接断开:" + sid);
sessionMap.remove(sid);
}
/**
* 群发
*
* @param message
*/
public void sendToAllClient(String message) {
Collection<Session> sessions = sessionMap.values();
for (Session session : sessions) {
try {
//服务器向客户端发送消息
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
定时任务测试
package com.sky.task;
import com.sky.websocket.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Component
public class WebSocketTask {
@Autowired
private WebSocketServer webSocketServer;
/**
* 通过WebSocket每隔5秒向客户端发送消息
*/
@Scheduled(cron = "0/5 * * * * ?")
public void sendMessageToClient() {
webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));
}
}
前端测试页面
DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Demotitle>
head>
<body>
<input id="text" type="text" />
<button onclick="send()">发送消息button>
<button onclick="closeWebSocket()">关闭连接button>
<div id="message">
div>
body>
<script type="text/javascript">
var websocket = null;
var clientId = Math.random().toString(36).substr(2);
//判断当前浏览器是否支持WebSocket
if('WebSocket' in window){
//连接WebSocket节点
websocket = new WebSocket("ws://localhost:8080/ws/"+clientId);
}
else{
alert('Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};
//连接成功建立的回调方法
websocket.onopen = function(){
setMessageInnerHTML("连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}
//连接关闭的回调方法
websocket.onclose = function(){
setMessageInnerHTML("close");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
websocket.close();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '
';
}
//发送消息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
//关闭连接
function closeWebSocket() {
websocket.close();
}
script>
html>
…未完