引言:项目中常见的数据导入导出功能,往往涉及字典数据的转换,每段导出代码都实现一遍字典数据转换无疑是重复劳动,一般项目中的解决方案都是通过通用的封装进行处理。这里就以此为例讲一讲spring自定义注解 和 spring切面编程。
注解 @interface
注解根据使用方式可分为以下几种:
分类 | 解释 | |
---|---|---|
标记注解(Marker Annotation) | 简单地用来标记某个类或方法,不需要提供任何参数。例如,@Component、@Service、@Controller等注解用于标记组件。 | |
值注解(Value Annotation) | 用于指定某个注解需要的参数值。例如,@RequestMapping注解中的value参数用于指定请求的URL路径。 | |
元注解(Meta-Annotation) | 见元注解 | |
注解处理器(Annotation Processor) | 用于在编译时处理注解,生成相应的代码或进行其他操作。例如,可以编写自定义注解处理器来生成根据注解生成文档、进行代码检查等。 |
这些使用方式提供了灵活的机制,使得开发人员可以根据需求使用注解来简化配置、增强功能和提高开发效率。在Spring框架中,注解的使用非常广泛,可以用于依赖注入、AOP、事务管理等方面。
元注解
要定义一个自定义的Spring注解,需要使用Java的元注解来标记该注解。元注解是一种用于标记其他注解的注解
注解 | 解释 | 使用 |
---|---|---|
@Target | 用于指定注解可以应用的元素类型,如类ElementType.TYPE 、方法ElementType.METHOD 、字段ElementType.FIELD 等。 | |
@Retention | 用于指定注解的生命周期,如源代码、编译时、运行时RetentionPolicy.RUNTIME | |
@Documented | 用于指定注解是否包含在Java文档中。 | |
@Inherited | 用于指定注解是否可以被子类继承。 |
自定义注解示例:
1. 创建注解
/** 标记EXCEL数据导出的注解
* description: Excel <br>
* date: 2024/4/16 16:22 <br>
* author: Boo <br>
* version: 1.0 <br>
*/
@Documented
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD }) // 指定注解可以用在 类、方法、字段上
@Retention(RetentionPolicy.RUNTIME) // 指定该注解运行时可用
public @interface Excel {
/**
* 自定义的属性
* 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
*/
public String dictType() default "";
}
2. 使用spring AOP
上面自定义的注解指定了可以用在 类、方法、和字段上
为实现数据的自动转换,我们需要以下几个步骤:
需求 | 分析 | |
---|---|---|
1. | 找到需要转换数据的方法 | 这里使用切面,注解作用在方法上,用于标记"切点"。 注解在这里用作“标记注解” |
2. | 获取它的返回值 | 使用环绕通知,可以获取并处理方法的返回值 |
3. | 将返回值进行转换 | 注解作用于返回值的字段(类属性)上,用于标记是否需要进行数据转换,并根据“dictType”属性转换对应的数据。 注解在这里用作“标记注解和值注解” |
4. | 将转换后的值返回 | 切面结束 |
- 在service的方法上添加注解
@Excel
,声明此方法需要切面处理
@Override
@Excel
public List<BizTestingVo> getTestingList(BizTestingReqVo bizTestingReqVo) {
return bizLimsInfoMapper.selectList(bizTestingReqVo);
}
- 在返回值的类型
BizTestingVo
中,给它的字段加上注解,说明字段需要处理
@Data
public class BizTestingVo {
@ExcelProperty("姓名")
private String name;
@ExcelProperty("性别")
@Excel(dictType = "gender") // 说明字段需要处理, 并给注解的“dictType”属性设置了一个值(性别的字典类型)
private String gender;
@ExcelProperty("创建时间")
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@ExcelIgnore
private String organizationId;
}
- 创建切面类
@Component
@Aspect
@Slf4j
public class ExcelAspect {
@Autowired
private RemoteDictService dictService;
/**
* 只要用到了 Excel 这个注解的,就是目标类
*/
@Pointcut("@annotation(com.pig4cloud.pigx.lims.framework.annotation.Excel)")
// 这是以前的写法 @Around("execution"(* *..*Service.*Pager(..))")
// 上面这个已经把这个替代掉了 @Pointcut("@execution(* *.*Controller.add())")
private void MyValid() {
}
@Around("MyValid()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
joinPoint.getTarget();
Object returnValue = joinPoint.proceed(); // 执行目标方法
if (returnValue == null || !(returnValue instanceof List)){
return returnValue;
}
List<Object> list = (List<Object>) returnValue;
if (list.size() < 1){
return returnValue;
}
Field[] fields = list.get(0).getClass().getDeclaredFields();
if (fields == null || fields.length < 1){
return returnValue;
}
for (Object o : list) {
for (Field field : fields) {
Excel excel4Field = field.getAnnotation(Excel.class);
if (excel4Field == null){
continue;
}
String dictType = excel4Field.dictType();
if (StrUtil.isNotBlank(dictType)){
field.setAccessible(true);
field.set(o, getDictLabel(dictType, field.get(o)));
}
}
}
return returnValue;
}
public String getDictLabel(String dictType, Object dictValue){
// TODO 根据字典类型和值获取label
return "";
}
}