主页 > 电脑硬件  > 

苍穹外卖知识点

苍穹外卖知识点

导入依赖

@Component @Aspect public class MyselfAspect{ @Before("excution(* com.services.*.(..))") public myBefore(JointPoint jointPoint){ System.out.println("在前面执行"); } }

只要注意如何使用@Before注解就行了,里面存放的是*(方法返回类型)+类路径+方法名+(函数参数) 在Service方法前面执行。 后面执行:@After 环绕执行:@Around

@Component @Aspect public class MyselfAspect{ @Around("excution(* com.services.*.(..))") public myBefore(ProceedingJointPoint jointPoint){ System.out.println("在前面执行"); jointPoint.proceed(); System.out.println("在后面执行"); } } 公共字段自动填充 需求背景

在进行插入和更新的时候,数据库有的表中有 创建时间、更新时间、创建用户ID、更新用户ID的字段; 每次进行写的时候都要手动的写一摸一样的代码,所以需要自动注入的功能;

选用方法

使用JDK自带的动态代理也能实现,但是复杂; 使用Spring-Boot-Web-Strater-Aop中的动态代理简便

定义操作类型的枚举类 public enum OperationType{ UPDATE, INSERT, } 定义一个注解@AutoFill @Target(ElementType.METHOD)//使用范围为方法 @Retention(RetentionPolicy.RUNTIME)//生命周期为运行时,也是最长的周期,一般定义注解都是这个周期 public @interface AutoFill{ OperationType value();//前面为value的类型为OperationType,参数名称为value } 定义切面类

注意这种代码的写法、如何获得注解、获得注解中的值

@Aspect @Componet public class AutoFillAspect{ @PointCut("execution(* *.*.*(..)) && @annotation(com.itheima.annotation.AutoFill)") public void autoFillPointCut(); @Before("autoFillPointCut()")//在切入点之前执行 public void autoFill(JoinPoint joinPoint){ log.info("开始进行公共字段自动填充..."); //1.获取到当前被拦截的方法上的数据库操作类型(比如是Insert还是Update,不同的类型需要给不同的参数赋值) MethodSignature signature = (MethodSignature) joinPoint.getSignature();//通过连接点对象来获取签名,向下转型为MethodSignature AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象 OperationType operationType = autoFill.value();//获得数据库操作类型(Insert or Update) //2.获取到当前被拦截的方法的参数--实体对象(比如传入的参数是员工还是菜品还是其它的) Object[] args = joinPoint.getArgs(); //获得了方法所有的参数 if(args == null || args.length==0 ){ //没有参数 return; } Object entity = args[0];//现在约定实体放在第1个位置,传入实体可能不同所以用Object //3.准备赋值的数据(给公共字段赋值的数据,比如时间就是系统时间,用户ID是从ThreadLocal获取) LocalDateTime now = LocalDateTime.now(); Long currentId = BaseContext.getCurrentId(); if(operationType == OperationType.INSERT){ //为4个公共字段赋值 try { Method setCreateTime = entity.getClass().getDeclaredMethod("SET_CREATE_TIME", LocalDateTime.class); //把方法名全部换成常量类,防止写错 Method setCreateUser = entity.getClass().getDeclaredMethod("SET_CREATE_USER", Long.class); Method setUpdateTime = entity.getClass().getDeclaredMethod("SET_UPDATE_TIME", LocalDateTime.class); Method setUpdateUser = entity.getClass().getDeclaredMethod("SET_UPDATE_USER", Long.class); //4.根据当前不同的操作类型,为对应的属性通过反射来赋值 setCreateTime.invoke(entity,now); setCreateUser.invoke(entity,currentId); setUpdateTime.invoke(entity,now); setUpdateUser.invoke(entity,currentId); }catch (Exception e){ e.printStackTrace(); } }else if(operationType == OperationType.UPDATE){ try { //为2个公共字段赋值 Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class); Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class); //4.根据当前不同的操作类型,为对应的属性通过反射来赋值 setUpdateTime.invoke(entity, now); setUpdateUser.invoke(entity, currentId); }catch (Exception e){ e.printStackTrace(); } } } } 最后在mapper类的方法上加上@AutoFill(value= OperationType.INSERT)注解 OSS文件上传 OSS的配置类 @Component @ConfigurationProperties(prefix = "sky.alioss") @Data public class AliOssProperties { private String endpoint; private String accessKeyId; private String accessKeySecret; private String bucketName; } 配置文件中写数据

注意大写使用-来表明

alioss: endpoint: oss-cn-shenzhen.aliyuncs access-key-id: LTAI5tHzX4fMjySKcCoCvYci access-key-secret: vPKhVa4Kux8jUP6fU4614CQ3FW0wiC bucket-name: cangqiongwaimaipbj 上传工具类 @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)); } 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(); } } //文件访问路径规则 BucketName.Endpoint/ObjectName StringBuilder stringBuilder = new StringBuilder(" "); stringBuilder .append(bucketName) .append(".") .append(endpoint) .append("/") .append(objectName); log.info("文件上传到:{}", stringBuilder.toString()); return stringBuilder.toString(); } } OSS的工具类注册对象

@Bean 注解用于在配置类中声明一个 Bean。当配置类(带有 @Configuration)被 Spring 容器加载时,其中带有 @Bean 注解的方法会被自动调用,其返回值会注册为一个 Bean,这些 Bean 然后可以在 Spring 容器中被其他部分使用。 @ConditionalOnMissingBean注解的意思:当没有这个对象的时候再去创建。

@Configuration @Slf4j public class OssConfiguration { @Bean @ConditionalOnMissingBean public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){ log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties); return new AliOssUtil(aliOssProperties.getEndpoint(), aliOssProperties.getAccessKeyId(), aliOssProperties.getAccessKeySecret(), aliOssProperties.getBucketName()); } } 接口中使用OSS

由于UUID具有固定的大小并包含时间字段,在特定算法下,随着时间推移,理论上在大约公元3400年左右会出现值的循环,所以问题不大。

@RestController @RequestMapping("/admin/common") @Api(tags="通用接口") @Slf4j public class CommonController { @Autowired private AliOssUtil aliOssUtil; //文件上传 @PostMapping("/upload") @ApiOperation("文件上传") public Result<String> upload(MultipartFile file){ log.info("文件上传:{}",file); try { String originalFilename = file.getOriginalFilename();//原始文件名 //截取原始文件名的的后缀 String extention = originalFilename.substring(originalFilename.lastIndexOf(".")); //构造新文件名称 String objectName = UUID.randomUUID().toString()+extention; //文件的请求路径 String filePath = aliOssUtil.upload(file.getBytes(), objectName); return Result.success(filePath); }catch(IOException e){ log.error("文件上传失败:{}",e); } return null; } } 使用事务 启动类上面加上@EnableTransactionManagement注解 Service实现类上面加上 @Transactional 注解 HttpClient 这个是用来发送Http请求的接口 大致使用方式 导入坐标 <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> 创建HttpClient类、根据请求的类型去创建GetClient类或者PostClient类 Get请求 public class HttpClientTest{ @Test public void testGET() throws Exception { //创建httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); //创建请求对象 HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status"); //发送请求 CloseableHttpResponse response = httpClient.execute(httpGet); //获取服务端返回的状态码 int statusCode = response.getStatusLine().getStatusCode(); System.out.println("服务端返回的状态码为:"+statusCode); //获取服务端返回的数据 HttpEntity entity = response.getEntity(); String body = EntityUtils.toString(entity); System.out.println("服务端返回的数据为:"+body); //关闭资源 response.close(); httpClient.close(); } } Post请求 public class HttpClientTest{ @Test public void testGET() throws Exception { //创建httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); //创建请求对象 HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status"); //发送请求 CloseableHttpResponse response = httpClient.execute(httpGet); //获取服务端返回的状态码 int statusCode = response.getStatusLine().getStatusCode(); System.out.println("服务端返回的状态码为:"+statusCode); //获取服务端返回的数据 HttpEntity entity = response.getEntity(); String body = EntityUtils.toString(entity); System.out.println("服务端返回的数据为:"+body); //关闭资源 response.close(); httpClient.close(); } } 微信小程序登录流程

登录流程为小程序前端调用wx.login()函数,然后获得授权码;授权码可以获得微信用户的唯一标识openid;授权码只能使用一次; 前端获得授权码之后,传入到后端的接口中; 后端接口根据配置的微信appid和密钥,授权码使用HttpClient调用Get方法获得openid; 根据openid从数据库中查找是否为新用户,并自动注册; 返回jwt token

插入数据库的时候,返回插入对象自增的ID useGenerateKeys=true, keyProperty = “id”

SpringCache

SpringCache是Spring提供的缓存框架。提供了基于注解的缓存功能。 SpringCache提供了一层抽象,底层可以切换不同的缓存实现(只需要导入不同的Jar包即可),如EHCache,Caffeine,Redis。

引入坐标 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> @CachePut示例 @PostMapping @CachePut(cacheNames="userCache",key="#user.id") //如果使用spring Cache缓存数据,redis中实际的key为:userCache::1。user是从参数取到的。 //并且方法执行完毕之后才会缓存;创建的树形key为userCache::1,值为user;并且当前存储的key为树形结构,::代表为空的一级 //@CachePut(cacheNames="userCache",key="#result.id") //result是从返回值return取到的 //@CachePut(cacheNames="userCache",key="#p0.id")//参数一 //@CachePut(cacheNames="userCache",key="#a0.id")//参数一的另一种写法 //@CachePut(cacheNames="userCache",key="#root.args[0].id")//参数一的另一种写法 public User save(@RequestBody User user){ userMapper.insert(user); return user; } 创建的展示

另一个创建多级key的展示

这里创建树形的key

运行接口中的方法体之前先从redis中查询id,如果有就直接返回,没有再查数据库(@Cacheabel)

它底层还是使用代理的功能

清理缓存 @CacheEvict

执行流程为先进行接口的方法体中的逻辑,然后再执行Redis中清楚缓存的操作;

删除所有redis中的数据

用户微信下单 订单支付流程

好像流程比较固定,微信支付代码也比较繁琐,所以就不展示了。

定时任务 CRON表达式

cron表达式是一个字符串,通过cron表达式可以定义任务触发的时间。 构成规则:分为6或7个域,由空格分隔开,每个域代表一个含义。 每个域的含义分别为:秒、分钟、小时、日、月、周、年(可选) cron表达式可以上在线Cron表达式生成器生成。CRON生成工具

大概流程

导入maven坐标,spring-context(已存在) 启动类添加注解@EnableScheduling开启任务调度 自定义定时任务类

@Component //实例化,自动生成bean交给容器管理 @Slf4j public class MyTask { @Scheduled(cron="0/5 * * * * ?") public void executeTask(){ log.info("定时任务开始执行:{}",new Date()); } }

0/5的意思是从0秒开始,每隔5秒触发一次。

WebSocket

前端好像很简单,就是new 一个WebSocket对象,建立ws协议的链接; 然后通过WebSocket对象设置回调函数,比如接受到消息调用自己定义的显示函数;

流程

导入maven坐标

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> 前端代码

导入WebSocket服务端组件WebSocketServer,用于和客户端通信 @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(); } } } } 上面为组件,有建立链接的方法,发送的方法,那么这些方法肯定是一个对象调用的,所以还要引入webSocket的配置类 /** * WebSocket配置类,用于注册WebSocket的Bean */ @Configuration public class WebSocketConfiguration { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } 来单提醒 在ServiceImpl中,支付成功之后发消息; 为什么可以推消息?因为管理端登录之后,就建立websocket链接; //通过websocket向客户端浏览器推送消息 type orderId content Map map = new HashMap(); map.put("type",1); map.put("orderId",this.orders.getId()); map.put("content","订单号:"+this.orders.getNumber()); String json = JSON.toJSONString(map); webSocketServer.sendToAllClient(json);
标签:

苍穹外卖知识点由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“苍穹外卖知识点