【苍穹外卖笔记】第三天内容
一、课程内容预览公共字段自动填充新增菜品业务代码开发菜品分页查询业务代码开发删除菜品业务代码开发修改菜品业务代码开发笔记1.公共字段自动填充1.1 问题分析观察业务表我们会发现有一些公共字段为了处理这些属性我们要多次在代码中写入如以下代码类似的代码employee.setCreateTime(LocalDateTime.now());employee.setUpdateTime(LocalDateTime.now());employee.setCreateUser(BaseContext.getCurrentId());employee.setUpdateUser(BaseContext.getCurrentId());这显然是不优雅的处理办法会导致代码冗余不便于后期维护那么有没有什么可以一劳永逸解决的办法呢观察这些属性我们可以分成两类一类只在insert里会用到一类insert和update都会用到所以我们可以定义一个自定义注解AutoFill用于根据方法类型自动生成对应公共属性填充代码。为了实现这一目的我们需要使用到自定义切面类AutoFillAspect。1.2 切面类介绍切面是面向切面编程AOP核心概念横切逻辑的集合。在面向对象编程OOP的过程中我们经常遇到一些不属于业务本身却频繁出现在各个方法里面的功能这类代码就叫横切逻辑日志记录权限校验异常捕获等等都是这个范畴而AOP就是为了解决OOP编程中这些业务方法导致的代码冗余耦合过高的问题。下面来解释一些术语术语解释切面Aspect封装横切逻辑的类比如“日志切面”、“事务切面”连接点JoinPoint标识程序中可以被切入的位置比如方法执行前后、异常抛出前切入点Pointcut筛选哪些类/方法要被切面通知Advice切面实际执行的动作一段AOP代码一般执行流程如下定义切入点确定拦截的业务方法范围编写切面编写要织入程序的代码比如日志记录或者权限校验程序运行时自动把切面代码织入目标方法中业务代码中无需再写冗余的代码切面类代码简单模板如下AspectComponentpublicclassLogAspect{// 切面类// 切入点标识拦截业务范围Pointcut(execution(* com.sky.controller.*.*(..)))publicvoidpoint(){}// 环绕通知横切逻辑Around(point())publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{longstartSystem.currentTimeMillis();Objectrespjp.proceed();// 执行目标方法longcostSystem.currentTimeMillis()-start;log.info(接口耗时{}ms,cost);returnres;}}1.3 代码开发首先我们需要建立一个自定义注解在sky-server下新建包annotation并加入自定义注解相关代码/** * 自定义注解用于自动化填充 */Target(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)publicinterfaceAutoFill{OperationTypevalue();}这段代码定义了自定义注解AutoFill其上的两个注解Target(ElementType.METHOD)和Retention(RetentionPolicy.RUNTIME)分别代表此注解只能作用于方法修改此注解中的value可以改为作用于类、属性等、此注解在程序运行时保留如果不写程序运行时注解会被丢弃。同时在注解定义的内部规定了此注解的唯一属性OperationType value();所以此注解使用必须传一个值在使用的时候表现为AutoFill(value ??)这个值的具体意义是标注方法的类型方便在后续写切面代码的时候根据类型织入不同代码。创建好自定义注解类后我们需要写切面代码来插入需要填充的公共字段代码。在sky-server下新建包aspect并写好自定义切面类加上切面类定义注解Aspect/** * 自定义切面实现公共字段自动填充逻辑 */AspectComponentSlf4jpublicclassAutoFillAspect{/** * 切入点 */Pointcut(execution(* com.sky.mapper.*.*(..)) annotation(com.sky.annotation.AutoFill))publicvoidautoFillPointCut(){}}我们在切面类内写入了切入点切入点上的Pointcut注解完成了两件事一是限制了切入的业务范围仅切入Mapper代码二是与自定义注解类AutoFill建立了连接将会切入注解标记的方法。接下来来补充具体的切面通知代码实现填充逻辑Before(autoFillPointCut())publicvoidautoFill(JoinPointjoinPoint){log.info(开始进行公共字段的自动填充...);//获取被拦截的方法上的数据库操作类型MethodSignaturesignature(MethodSignature)joinPoint.getSignature();//方法签名对象AutoFillautoFillsignature.getMethod().getAnnotation(AutoFill.class);//获得方法上注解对象OperationTypeoperationTypeautoFill.value();//获得数据库操作类型//获取当前被拦截方法的参数——实体对象Object[]argsjoinPoint.getArgs();if(argsnull||args.length0){return;}Objectentityargs[0];//准备赋值的数据LocalDateTimenowLocalDateTime.now();LongcurrentIdBaseContext.getCurrentId();//赋值if(operationTypeOperationType.INSERT){try{MethodsetCreateTimeentity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME,LocalDateTime.class);MethodsetCreateUserentity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER,Long.class);MethodsetUpdateTimeentity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME,LocalDateTime.class);MethodsetUpdateUserentity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER,Long.class);setCreateTime.invoke(entity,now);setCreateUser.invoke(entity,currentId);setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);}catch(Exceptione){e.printStackTrace();}}elseif(operationTypeOperationType.UPDATE){try{MethodsetUpdateTimeentity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME,LocalDateTime.class);MethodsetUpdateUserentity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER,Long.class);setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);}catch(Exceptione){e.printStackTrace();}}}这段代码中entity的意思是AutoFill标注的Mapper方法的参数一般是实体类比如EmployeeDish获得这个实体后就可以进行后续操作往其中填入公共数值进而影响实际传入数据库的值。对这段代码中的JoinPoint和MethodSignature进行一下介绍JoinPoint是连接点对象代表了当前被AOP拦截的目标方法的全部信息包括目标方法的所有入参目标方法的签名方法名、返回值、参数列表目标方法所属类、方法对象本身这里外卖就是用joinPoint.getSignature()获得方法签名再强转成MethodSignature。MethodSignature是方法签名对象封装方法详情我们获得这个类对象之后就可以比较方便地获得方法上注解的参数进而分辨两种不同的操作模式写入不同的数据。2.新增菜品业务代码开发这个部分的大部分代码在有之前的知识就可以很轻松地完成但是新增菜品需要上传菜品图片这个部分需要用到云OSS和文件上传的知识。2.1 文件上传接口设计在实现新增菜品功能之前我们要先进行文件上传的接口设计。观看接口文档可以发现新增菜品需要我们返回一个String类型的data这个字符串代表的是图片地址有图片地址前端才能找到图片并渲染出来。同时为了接收文件类型的数据我们需要使用MultipartFile类。MultipartFile是 Spring 封装的文件上传工具自动接收前端上传的二进制文件提供获取文件名、大小、文件流、保存到本地等方法简化文件上传开发。前端以特定格式提交文件后Spring会使用内置类解析请求使其能被后端的MultipartFile类接收这样我们就可以直接调用方法操作文件了。由于文件上传不止是新增菜品会使用到的接口我们将其包装为公共接口代码如下/** * 通用接口 */RestControllerRequestMapping(/admin/common)Api(tags通用接口)Slf4jpublicclassCommonController{AutowiredprivateAliOssUtilaliOssUtil;PostMapping(/upload)ApiOperation(文件上传)publicResultStringupload(MultipartFilefile){log.info(文件上传{},file);try{//原始文件名Stringoriginalfilenamefile.getOriginalFilename();//截取原始文件名的后缀 dfdfdf.pngStringextensionoriginalfilename.substring(originalfilename.lastIndexOf(.));//构造新文件名称StringobjectNameUUID.randomUUID().toString()extension;//文件的请求路径StringfilePathaliOssUtil.upload(file.getBytes(),objectName);returnResult.success(filePath);}catch(IOExceptione){log.error(文件上传失败{},e);}returnResult.error(MessageConstant.UPLOAD_FAILED);}}这样就可以完成文件上传的操作了。其中AliOssUtil是上传阿里云的工具类要使得这个类连接上我们的阿里云OSS我们需要增加一些云数据库相关的配置sky:jwt:# 设置jwt签名加密时使用的秘钥admin-secret-key:itcast# 设置jwt过期时间admin-ttl:7200000# 设置前端传递过来的令牌名称admin-token-name:token# 云数据库相关配置alioss:endpoint:${sky.alioss.endpoint}access-key-id:${sky.alioss.access-key-id}access-key-secret:${sky.alioss.access-key-secret}bucker-name:${sky.alioss.bucket-name}具体的值写在application-dev.yml中来保证安全性工具类获得这些属性后就可以拼接网址把文件上传阿里云OSS。具体的OSS相关的内容这边建议看一下相关博客和视频这里就不过多介绍了。但是关于这个配置类的值的传递链路我这里讲解一下也就是写在配置类里面的明文数据是如何到工具类手里的。完整链路yml 配置 → AliOssProperties → OssConfiguration → AliOssUtil在这个链路上每一个部分都各司其职application.yml配置文件存放阿里云 OSS 账号信息AliOssProperties配置属性类负责读取 yml 配置OssConfiguration配置工厂类接收配置、创建工具 BeanAliOssUtilOSS 上传工具类持有配置、提供上传方法这里给出AliOssProperties的代码package com.sky.properties;ComponentConfigurationProperties(prefixsky.alioss)DatapublicclassAliOssProperties{privateStringendpoint;privateStringaccessKeyId;privateStringaccessKeySecret;privateStringbucketName;}其中ConfigurationProperties(prefix sky.alioss)与我们在配置类中为配置数据分配的位置是一直的通过这个注解就可以为这个类的同名值赋值。然后属性类就可以传递到工厂配置类ConfigurationSlf4jpublicclassOssConfiguration{BeanConditionalOnMissingBeanpublicAliOssUtilaliOssUtil(AliOssPropertiesaliOssProperties){log.info(开始创建阿里云文件上传工具类对象:{},aliOssProperties);returnnewAliOssUtil(aliOssProperties.getEndpoint(),aliOssProperties.getAccessKeyId(),aliOssProperties.getAccessKeySecret(),aliOssProperties.getBucketName());}}这样属性值就顺利传递进入工具类内部了。此外观察工具类的参数我们会发现我们需要同时传入一个文件名。自动生成文件名我们可以使用UUID生成UUID就是 Universally Unique Identifier通用唯一识别码是一串全球几乎不会重复的 36 位字符串由数字 小写字母 4 个横杠组成。解决了文件名的问题我们还需要添加上后缀这个只需要从原文件名上截取下来就可以了。后面的一些开发都是运用已经学过的知识就可以进行完成这里也不多讲解了多动手多实践

相关新闻