Java AOP 通过注解实现切面及通过注解改变返回值

文章介绍了如何使用Java的AOP和注解进行切面编程,包括事务管理、日志处理等场景。通过自定义注解和切面类,实现在查询学生信息时,根据证件类型动态设置性别的功能。同时,展示了如何添加新的注解以在返回值中增加证件类型中文名称,利用JsonSerializer处理JSON响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
}

返回报文

{
    "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": "女"
}

总结

通过以上两种方式,即可实现通过注解来处理结果,可以解决大多数问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值