苍穹外卖-黑马程序员
文章目录
- 苍穹外卖-黑马程序员(笔记)
- Day01
- 1.通过knife4j生成接口文档
- 2.MD5加密
- 3.Swagger常用注解
- Day02
- 1.Mybatis分页管理
- 2.更新数据时使用动态SQL来更新,减少代码
- 3.插入数据利用唯一约束抛异常来捕获,解决用户名唯一问题
- 4.利用Lombok的@Builder注解简化代码
- Day03
- 1.利用AOP自动填充公共字段
- 2.上传文件-阿里云Oss(对象存储服务器)基于SpringBoot
- 注意事项
- 3.mybatis插入数据后返回主键id
- Day05
- 1.Redis基本数据类型
- 2.Spring Data Redis, Java中使用redis
- Day06
- 1.利用用Httpclient实现客户端发送HTTP请求
- 2.小程序微信登录(后端)
- Day07
- 1.Spring Cache整合Redis(SpringBoot)
- SpringCache的使用注意事项
- Day10
- 1.Spring Task(定时任务)
- 2.SpringBoot + WebSocket
苍穹外卖-黑马程序员(笔记)
Day01
1.通过knife4j生成接口文档
- 在SpringMVC配置类中加入
@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/");
}
2.MD5加密
- Spring提供的工具类
// 123456 => e10adc3949ba59abbe56e057f20f883e
password = DigestUtils.md5DigestAsHex(password.getBytes());
3.Swagger常用注解
- @Api 在类上, Controller…
- @ApiModle 在类上, pojo
- @ApiMoldeProprety 在属性上,描述信息
- @ApiOperation 在方法上,描述方法
Day02
1.Mybatis分页管理
- 先导入依赖
<pagehelper>1.3.0pagehelper><dependency><groupId>com.github.pagehelpergroupId><artifactId>pagehelper-spring-boot-starterartifactId><version>${pagehelper}version>
dependency>
- 开启分页配置,自动拦截下一条SQL加limit条件
@Override
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {// 开启分页查询,这条代码是关键PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());// 查询数据Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);return new PageResult(page.getTotal(), page);
}
2.更新数据时使用动态SQL来更新,减少代码
<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>
3.插入数据利用唯一约束抛异常来捕获,解决用户名唯一问题
/*** 插入异常* @param ex* @return*/
@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){// Duplicate entry 'ewqe1' for key 'employee.idx_usString msg = ex.getMessage();log.error("异常信息:{}", msg);if (msg.contains("Duplicate entry")) {return Result.error(msg.split(" ")[2] + "已存在!");} else {return Result.error("未知错误!");}}
4.利用Lombok的@Builder注解简化代码
- 在实体类上加@Builder即可
/*** 更新员工状态* @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);
}
Day03
1.利用AOP自动填充公共字段
-
创建枚举类,区分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);}} }
2.上传文件-阿里云Oss(对象存储服务器)基于SpringBoot
-
在阿里云上注册,并申请 对象存储 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}# AccessKeyaccess-key-id: ${sky.alioss.access-key-id}# AccessSecretaccess-key-secret: ${sky.alioss.access-key-secret}# bucket的名字bucket-name: ${sky.alioss.bucket-name}application-dev.yml中根据自己实际情况更改。
sky:alioss:endpoint: oss-cn-beijing.aliyuncs.comaccess-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/ObjectNameStringBuilder 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// 利用构造器注入aliOssPropertiespublic 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 {@Resourceprivate AliOssUtil aliOssUtil;/*** 文件上传到AliOss* @param file* @return*/@PostMapping("/upload")@ApiOperation("文件上传")public Result<String> upload(MultipartFile file) {log.info("文件上传:{}", file);// 获取UUIDString uuid = UUID.randomUUID().toString();// 原始文件名String originalFilename = file.getOriginalFilename();// 获取源文件后缀String extension = originalFilename.substring(originalFilename.lastIndexOf("."));// 新文件名String filename = uuid + extension;// 上传到阿里云Osstry {// 上传成功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(不行就重启电脑)。
3.mybatis插入数据后返回主键id
-
在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>
Day05
1.Redis基本数据类型
-
字符串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
2.Spring Data Redis, Java中使用redis
-
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: localhostport: 6379database: 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 {@Beanpublic 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 {@Resourceprivate RedisTemplate redisTemplate;@Testpublic 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")); }
Day06
1.利用用Httpclient实现客户端发送HTTP请求
-
导入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();}}
2.小程序微信登录(后端)
-
配置登录所需要的appid和secret,对应的jwt
# application.yml sky:jwt:# jwt用户端配置user-secret-key: itheimauser-ttl: 7200000user-token-name: authorizationwechat:# 小程序IDappid: ${sky.wechat.appid}# 小程序秘钥secret: ${sky.wechat.secret}######################################## application-dev.yml sky:wechat:# 小程序IDappid: 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; //小程序的appidprivate 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 {@Resourceprivate WeChatProperties weChatProperties;@Resourceprivate UserMapper userMapper;/*** 微信登录* @param userLoginDTO* @return*/public User wxLogin(UserLoginDTO userLoginDTO) {// 调用微信接口获取用户openidString 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();// 插入并返回iduserMapper.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;} }
Day07
1.Spring Cache整合Redis(SpringBoot)
-
导入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: localhostport: 6379database: 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");} } -
注解使用例子
- @Cacheable注解
- 调用该方法前,会去检查是否缓存中已经存在,如果有就直接返回,不调用方法。如果没有,就调用方法,然后把结果缓存起来。
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); }- @CacheEvict
- 会清空指定缓存。
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
- 把方法的返回值直接缓存起来。
@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(); } -
SpringCache的使用注意事项
@CacheEvict注解中的allEntries = true属性会将当前片区中的所有缓存数据全部清除,请谨慎使用
@CacheEvict注解适用用于失效模式,也即更新完数据库数据后删除缓存数据
@CachePut注解用于适用于双写模式,更新完数据库后写入到缓存中配置文件中spring.cache.redis.cache-null-values=true一般需要设置(null值缓存),可以有效的防止缓存穿透
Day10
1.Spring Task(定时任务)
-
导入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触发
2.SpringBoot + WebSocket
-
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 {@Beanpublic 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<>();/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("sid") String sid) {System.out.println("客户端:" + sid + "建立连接");sessionMap.put(sid, session);}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, @PathParam("sid") String sid) {System.out.println("收到来自客户端:" + sid + "的信息:" + message);}/*** 连接关闭调用的方法** @param sid*/@OnClosepublic 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 {@Autowiredprivate 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);//判断当前浏览器是否支持WebSocketif('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>…未完
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
