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

这篇具有很好参考价值的文章主要介绍了Java AOP 通过注解实现切面及通过注解改变返回值。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

总结

通过以上两种方式,即可实现通过注解来处理结果,可以解决大多数问题。文章来源地址https://www.toymoban.com/news/detail-627357.html

到了这里,关于Java AOP 通过注解实现切面及通过注解改变返回值的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 注解 + spring aop切面+ SpringEl

    2024年02月09日
    浏览(45)
  • Java Spring 通过 AOP 实现方法参数的重新赋值、修改方法参数的取值

    我创建的项目项目为 SpringBoot 项目 这里以对前端传递过来的加密数据进行解密为例 注解 控制器方法 方式一:通过环绕通知实现 [个人比较推荐] 方式二:通过前置通知 + 反射实现 Java ReflectUtil 反射相关的工具类 由于 JDK 8 中有关反射相关的功能自从 JDK 9 开始就已经被限制了

    2024年02月04日
    浏览(34)
  • 如何将重复方法封装为Aop切面并结合注解使用

    首先要导入依赖 编写注解 编写Aop 方法上添加注解   然后测试就发现添加代码的方法都可以防止重复提交了!!

    2024年02月02日
    浏览(46)
  • 【Spring】javaBean、依赖注入、面向切面AOP、使用注解开发

    有一定规范的Java实体类,类内提供了一些公共方法以便外界对该对象的内部属性进行操作 所有属性都是private,所有的属性都可以通过get/set方法进行访问,同时还需要有一个无参构造(默认就有) 高内聚,低耦合是现代软件的开发的设计模式 之前编写的图书管理系统具有高

    2024年02月08日
    浏览(70)
  • JAVA:使用 Spring AOP 实现面向切面编程

    1、简述 在现代的软件开发中,面向切面编程(AOP)是一种重要的编程范式,它允许我们将横切关注点(如日志记录、性能监控、事务管理等)从应用程序的核心业务逻辑中分离出来,以提高代码的模块化和可维护性。Spring 框架提供了强大的 AOP 支持,使得我们可以轻松地实

    2024年04月13日
    浏览(45)
  • spring-自定义AOP面向切面注解--统一切面处理-登陆信息采集

    2023华为OD统一考试(A+B卷)题库清单-带答案(持续更新)or2023年华为OD真题机考题库大全-带答案(持续更新) 1. 先写一个登陆记录注解(//记录:XXX时间,XXX姓名,XX系统,登录成功) 2. 写一个切面对注解进行处理(业务逻辑处理,记录登陆的信息) 3.写一个登录的控制类,

    2024年02月13日
    浏览(36)
  • Spring Boot 自定义注解,AOP 切面统一打印出入参请求日志

    今天主要说说如何通过自定义注解的方式,在 Spring Boot 中来实现 AOP 切面统一打印出入参日志。小伙伴们可以收藏一波。 废话不多说,进入正题! 在看看实现方法之前,我们先看下切面日志输出效果咋样: 从上图中可以看到,每个对于每个请求,开始与结束一目了然,并且

    2024年02月08日
    浏览(47)
  • 【Spring】使用aop切面编程时要给那些类加注解

    🎄欢迎来到@边境矢梦°的csdn博文🎄  🎄本文主要梳理 Spring 中使用aop切面编程时要给那些类加注解 🎄 🌈我是边境矢梦°,一个正在为秋招和算法竞赛做准备的学生🌈 🎆喜欢的朋友可以关注一下 🫰🫰🫰 ,下次更新不迷路🎆 Ps: 月亮越亮说明知识点越重要 (重要性或者

    2024年02月07日
    浏览(45)
  • 【Spring进阶系列丨第十篇】基于注解的面向切面编程(AOP)详解

    ​ 注意,该类的两个细节: a、@Component注解向容器中注册一个Bean。 b、@Aspect注解表示这个是一个切面类。 c、@Before注解表示的是这个是前置增强/前置通知。 ​ 注意:对于业务Bean,我们也需要通过@Service注解来向容器中注册。 ​ 问题:我们看到对于切面类中定义的通知,有

    2024年04月23日
    浏览(49)
  • Spring5框架——AOP操作:通过Aspectj注解方式和配置文件方式来实现

    o((⊙﹏⊙))o. ** 之前的博客介绍了什么是AOP,以及AOP的底层原理,AOP主要是在原本的基础上添加一些之外的功能但是添加的功能是不会修改原定的代码,接下来为你介绍的是Aspectj注解,Spring 框架一般都是基于 AspectJ 实现 AOP 操作。AspectJ 不是 Spring 组成部分,独立 AOP 框架,一

    2024年02月16日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包