对于数据库中的employee表:
@Data
@Builder//构建器注解,此类的对象可以使用builder方法进行构建
@NoArgsConstructor
@AllArgsConstructor
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String name;
private String password;
private String phone;
private String sex;
private String idNumber;
private Integer status;
//@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
//@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
private Long createUser;
private Long updateUser;
}
@Data
public class EmployeeDTO implements Serializable {
private Long id;
private String username;
private String name;
private String phone;
private String sex;
private String idNumber;
}
Entity:
DTO:
VO:
在增加或查询操作中,设置下列字段时代码冗余,不便于后期维护
技术点:枚举、注解、AOP、反射
/**
* 数据库操作类型
* 枚举类
*/
public enum OperationType {
/**
* 更新操作
*/
UPDATE,
/**
* 插入操作
*/
INSERT
}
/**
* 自定义注解,用于标识某个方法需要进行功能字段自动填充处理
* 这个类可以作为一个注解进行使用
*/
@Target(ElementType.METHOD)//指定当前的自定义注解只能加在方法上
@Retention(RetentionPolicy.RUNTIME)//固定写法
public @interface AutoFill {
//数据库操作类型:UPDATE INSERT(只有增加和修改操作涉及公共字段填充)
OperationType value();
}
/**
* 自定义切面类,实现公共字段自动填充处理逻辑
*/
@Aspect//设置当前类为切面类,告诉spring这个类是用来做AOP的
@Component//通知类(切面类)必须配置成Spring管理的bean
@Slf4j
public class AutoFillAspect {
//此切入点表达式前半部分锁定mapper包下所有类所有方法,后半部分锁定方法中加入了AutoFill注解的方法
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut(){}
/**
* 前置通知,在通知中进行公共字段的赋值
*/
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint){
log.info("开始进行公共字段自动填充");
//1.获取当前被拦截的方法上的数据库操作类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//获得方法签名对象,并向下转型为MethodSignature
AutoFill antoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
OperationType operationType = antoFill.value();//获得数据库操作类型
//这三行的代码最终作用是获得注解中的数据库操作类型,也就是 @AutoFill(value = OperationType.UPDATE)中的value
//2.获取当前被拦截的方法的参数--实体对象
Object[] args = joinPoint.getArgs();//获取通知点方法的参数
if(args == null || args.length == 0){//如果没有参数则直接返回
return;
}
Object entity = args[0];
//3.准备赋值的数据
LocalDateTime now = LocalDateTime.now();//update_time的值
Long currentId = BaseContext.getCurrentId();//update_user的值
//4.根据当前不同的操作类型,为对应的属性通过反射来赋值
if(operationType == operationType.INSERT){
//新增操作为4个公共字段赋值
try {
//4.1通过反射获取这四个方法
//使用常量类AutoFillConstant中的常量代替方法名,防止自己写写错
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//4.2通过反射调用获取到的四个方法为对象属性赋值
setCreateTime.invoke(entity,now);//entity中存的是Employee
//这行代码:使用entity对象调用setCreateTime方法,给方法传递参数now
setCreateUser.invoke(entity,currentId);
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
}else if(operationType == operationType.UPDATE){
//修改操作为2个公共字段赋值
try {
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
1、创建枚举类,其中有插入、修改操作变量
2、创建自定义注解类,其中属性引用枚举类的变量
3、在mapper层中的插入、修改方法上加入自定义注解,并通过属性指定其操作类型(插入或修改)
4、创建切面类,进行切入点表达式和通知的实现
利用阿里云实现文件上传功能,将文件上传并存储到阿里云存储空间,并返回文件的访问地址,以便在前端可以回显文件
@Component
@ConfigurationProperties(prefix = "sky.alioss")//将配置文件中sky.alioss的相关属性值赋给此类中的成员
@Data
public class AliOssProperties {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;//Bucket名称
}
此类实现upload方法,实现文件上传功能,此方法返回文件的访问路径
@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
/**
* 文件上传
*
* @param bytes
* @param objectName
* @return
*/
//将文件上传到阿里云并返回文件的请求路径
//第一个参数:文件的字节数组,第二个参数:拼接uuid后的新文件名
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();
}
}
//文件访问路径规则 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对象:实现aliOssUtil方法,此方法将AliOssProperties对象作为参数传入,方法内读取AliOssProperties中的四个属性,将其包装为AliossUtil对象返回,并将这个AliossUtil交给ioc容器管理,需要时可以直接依赖注入(注入的AliossUtil对象的四个属性已经赋过值)
此类相当于一个中间媒介,关联AliOssProperties类和AliOssUtil,接收一个AliOssProperties并将其转换为一个AliOssUtil后返回,本质就是将AliOssProperties中从.yml中读取到的四个属性值赋给AliOssUtil中相应的四个属性
这四个属性的的去向总结为:
.yml(人为预先定义好四个属性的值)--->AliOssProperties类(利用注解从.yml中读取四个属性)-->OssConfiguration类(从类中方法接收的参数AliOssProperties对象中读取四个属性,并封装为AliOssUtil对象)-->AliOssUtil对象(OssConfiguration类中方法的返回值)
@Configuration
@Slf4j
public class OssConfiguration {
@Bean
@ConditionalOnMissingBean//保证整个容器中只有一个AliOssUtil对象
public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){
log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties);
return new AliOssUtil(aliOssProperties.getEndpoint(),
aliOssProperties.getAccessKeyId(),
aliOssProperties.getAccessKeySecret(),
aliOssProperties.getBucketName());
}
}
此类用来对前端文件上传的请求进行响应,前端传来一个文件参数
@RestController
@RequestMapping("/admin/common")
@Api(tags = "通用接口")
@Slf4j
public class CommonController {
@Autowired
private AliOssUtil aliOssUtil;
/**
* 文件上传
* @param file
* @return
*/
@PostMapping("/upload")
@ApiOperation("文件上传")
public Result upload(MultipartFile file){
log.info("文件上传:{}",file);
try {
//获取原始文件名
String originalFilename = file.getOriginalFilename();
//将原始文件名的后缀截取出来
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
//构造新文件名称(uuid拼接原始文件名后缀)
String objectName = UUID.randomUUID().toString() + extension;
//获取文件的请求路径
String filePath = aliOssUtil.upload(file.getBytes(), objectName);
return Result.success(filePath);
} catch (IOException e) {
log.error("文件上传失败:{}",e);
}
return null;
}
}
文件上传功能的核心为CommonController类和AliOssUtil类