Java AOP 通过注解实现切面及通过注解改变返回值
AOP简介
学习过java的小伙伴都知道Spring的重要知识点之一就是AOP,AOP也就是切面编程,切面编程它能够帮助我们实现非侵入式的功能增强,解耦现有的业务逻辑和要新增的功能增强。
实际应用中的场景
事务管理、拦截器、日志处理、权限控制等。
AOP的增强方式
前置增强、后置增强、异常增强、环绕增强
AOP的通知方式及执行顺序
通知方式:前置通知、后置通知、环绕通知、返回通知、异常通知。
执行顺序:前置通知、环绕通知,如果发生异常执行异常通知,如果没有发生异常执行后置通知(after),最后执行返回通知(afterReturning)。
AOP实战
假设场景1
查询学生类(包括证件类型、证件号、性别、名称等),如果证件类型为 “01” ,也就是身份证,那么性别由身份证第17位决定,即使数据库有性别字段值,也会被覆盖,证件类型不是**“01”**身份证,则不做处理,性别为数据库存储的性别。
处理逻辑1
自定义一个注解SexType,用在方法上,写一个切面类,切点就是SexType注解,在返回通知(afterReturning)阶段进行增强。
实现逻辑1
定义SexType注解:
@Inherited
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SexType {
}
使用SexType的示例:
@SexType
public Students selectById(Integer id) {
Students students = studentsMapper.selectById(id);
return students;
}
切面类StudentAop:
@Aspect
@Component
@Order(1)
public class StudentAop {
@Pointcut(value = "@annotation(com.qiaofc.sharding.aop.SexType)")
private void setSex() {}
@Around("setSex()")
public static Object logStart(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("执行切面");
return joinPoint.proceed();
}
@AfterReturning(value = "setSex()",returning = "methodResult")
public static void setSexType(JoinPoint joinPoint, Object methodResult) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
String sexResult = "";
//拿到setSex方法,方便后面通过反射调用sexSex方法给属性赋值
Method setSex = methodResult.getClass().getMethod("setSex", String.class);
//拿到属性值
Field fieldIdentifyType = methodResult.getClass().getDeclaredField("identifyType");
//设置为true可以拿到private的属性值,否则拿不到
fieldIdentifyType.setAccessible(true);
String identifyType = (String)fieldIdentifyType.get(methodResult);
Field fieldIdentifyNumber = methodResult.getClass().getDeclaredField("identifyNumber");
fieldIdentifyNumber.setAccessible(true);
String identifyNumber = (String)fieldIdentifyNumber.get(methodResult);
Field fieldSex = methodResult.getClass().getDeclaredField("sex");
fieldSex.setAccessible(true);
String sex = (String)fieldSex.get(methodResult);
if (methodResult != null) {
if ("01".equals(identifyType) && !StringUtils.isEmpty(identifyNumber) && identifyNumber.length() == 18) {
String sexN = identifyNumber.substring(16,17);
try {
int sexI = Integer.valueOf(sexN);
if (sexI % 2 == 1) {
sexResult = "男";
} else {
sexResult = "女";
}
} catch (Exception e) {
sexResult = "";
}
}
if (StringUtils.isEmpty(sexResult)) {
if (!StringUtils.isEmpty(sex)) {
if ("0".equals(sex)) {
//通过反射的方法给属性赋值
setSex.invoke(methodResult,"男");
} else {
setSex.invoke(methodResult,"女");
}
} else {
setSex.invoke(methodResult,"未知");
}
} else {
setSex.invoke(methodResult,sexResult);
}
} else {
System.out.println("返回值为空!");
}
}
}
测试
请求报文
{
"sid": 1
}
返回报文文章来源:https://www.toymoban.com/news/detail-627357.html
{
"sid": 1,
"sname": "小明",
"address": null,
"identifyType": "01",
"identifyNumber": "510123196908151288",
"sex": "女"
}
假设场景2
在场景一的基础上,我们又有了一个新的需求,为了方便展示,后端在返回identifyType=01的基础上再返回一个证件类型的中文名称。
处理逻辑2
添加一个注解,可以直接操作json,添加一个字段,返回证件的中文名称。
实现逻辑2
添加注解IdentifyTypeChange,将使用此注解的参数,在IdentifyTypeSerialize类中统一处理
@Inherited
@Documented
@Target({ElementType.FIELD})
@JsonSerialize(using = IdentifyTypeSerialize.class)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
public @interface IdentifyTypeChange {
String identifyType() default "";
}
定义IdentifyTypeSerialize类,继承JsonSerializer,并实现接口ContextualSerializer,在serialize中通过jsonGenerator.writeStringField方法直接添加一个参数,并赋值
public class IdentifyTypeSerialize<T> extends JsonSerializer<T> implements ContextualSerializer {
private static Map<String,String> identifyTypeMap = new HashMap<>();
static {
//定义常用的证件类型,或者通过数据库查询
identifyTypeMap.put("01","身份证");
}
private String fieldName = "";
//无参构造方法必须要有,否则报错
private IdentifyTypeSerialize() {}
public IdentifyTypeSerialize(String fieldName) {
this.fieldName = fieldName;
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
IdentifyTypeChange identifyTypeChange = beanProperty.getAnnotation(IdentifyTypeChange.class);
if (Objects.nonNull(identifyTypeChange) && Objects.equals(String.class,beanProperty.getType().getRawClass())) {
//拿到注解下面的参数名,并且赋值给fieldName,serialize方法会用到
return new IdentifyTypeSerialize(beanProperty.getName());
}
return null;
}
@Override
public void serialize(T value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
//如果原参数的值为空,那么还是写回null
if (Objects.isNull(value)) {
jsonGenerator.writeNull();
return;
}
//value注解下的参数的值
String valueStr = value.toString();
if (StringUtils.isBlank(valueStr)) {
//将原参数值写回,如果不写回,原有identifyType的值将会为空
jsonGenerator.writeString(valueStr);
return;
}
if (StringUtils.isNotBlank(this.fieldName)) {
//根据参数值获取对应的证件名字,然后再添加一个字段并赋值
String identifyTypeName = identifyTypeMap.get(valueStr);
if (StringUtils.isNotBlank(identifyTypeName)) {
//这里写注解下参数的值,也可以直接在这里修改注解下参数的值,例如现在identifyType的值为01,可以在后面直接加上身份证,就是:valueStr + identifyTypeName
jsonGenerator.writeString(valueStr);
//根据上面拿到的注解下参数名,添加一个反参
jsonGenerator.writeStringField(this.fieldName + "Name", identifyTypeName);
return;
}
} else {
jsonGenerator.writeString(valueStr);
}
}
}
测试
请求报文
{
"sid": 1
}
返回报文
{
"sid": 1,
"sname": "小明",
"address": null,
"identifyType": "01",
"identifyTypeName": "身份证",
"identifyNumber": "510123196908151288",
"sex": "女"
}
总结
通过以上两种方式,即可实现通过注解来处理结果,可以解决大多数问题。文章来源地址https://www.toymoban.com/news/detail-627357.html
到了这里,关于Java AOP 通过注解实现切面及通过注解改变返回值的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!