iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >怎么用自定义注解
  • 434
分享到

怎么用自定义注解

2023-06-16 00:06:30 434人浏览 安东尼
摘要

本篇内容介绍了“怎么用自定义注解”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!基本知识在Java中,注解分为两种,元注解和自定义注解。很多人

本篇内容介绍了“怎么用自定义注解”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

基本知识

在Java中,注解分为两种,元注解和自定义注解。

很多人误以为自定义注解就是开发者自己定义的,而其它框架提供的不算,但是其实上面我们提到的那几个注解其实都是自定义注解。

关于"元"这个描述,在编程世界里面有都很多,比如"元注解"、"元数据"、"元类"、"元表"等等,这里的"元"其实都是从meta翻译过来的。

一般我们把元注解理解为描述注解的注解,元数据理解为描述数据的数据,元类理解为描述类的类…

所以,在Java中,除了有限的几个固定的"描述注解的注解"以外,所有的注解都是自定义注解。

jdk中提供了4个标准的用来对注解类型进行注解的注解类(元注解),他们分别是:

@Target  @Retention  @Documented  @Inherited

除了以上这四个,所有的其他注解全部都是自定义注解。

这里不准备深入介绍以上四个元注解的作用,大家可以自行学习。

本文即将提到的几个例子,都是作者在日常工作中真实使用到的场景,这例子有一个共同点,那就是都用到了springaop技术。

什么是AOP以及他的用法相信很多人都知道,这里也就不展开介绍了。

使用自定义注解做日志记录

不知道大家有没有遇到过类似的诉求,就是希望在一个方法的入口处或者出口处做统一的日志处理,比如记录一下入参、出参、记录下方法执行的时间等。

如果在每一个方法中自己写这样的代码的话,一方面会有很多代码重复,另外也容易被遗漏。

这种场景,就可以使用自定义注解+切面实现这个功能。

假设我们想要在一些WEB请求的方法上,记录下本次操作具体做了什么事情,比如新增了一条记录或者删除了一条记录等。

首先我们自定义一个注解:

  @Target(ElementType.METHOD)  @Retention(RetentionPolicy.RUNTIME)  public @interface OpLog {            public OpType opType();            public String opItem();            public String opItemIdExpression();  }

因为我们不仅要在日志中记录本次操作了什么,还需要知道被操作的对象的具体的唯一性标识,如订单号信息。

但是每一个接口方法的参数类型肯定是不一样的,很难有一个统一的标准,那么我们就可以借助Spel表达式,即在表达式中指明如何获取对应的对象的唯一性标识。

有了上面的注解,接下来就可以写切面了。主要代码如下:

 @Aspect  @Component  public class OpLogAspect {      private static final Logger LOGGER = LoggerFactory.getLogger(OpLogAspect.class);      @Autowired      httpservletRequest request;      @Around("@annotation(com.hollis.annotation.OpLog)")      public Object log(ProceedingJoinPoint pjp) throws Exception {          Method method = ((MethodSignature)pjp.getSignature()).getMethod();          OpLog opLog = method.getAnnotation(OpLog.class);          Object response = null;          try {              // 目标方法执行              response = pjp.proceed();          } catch (Throwable throwable) {              throw new Exception(throwable);          }           if (StringUtils.isNotEmpty(opLog.opItemIdExpression())) {              SpelExpressionParser parser = new SpelExpressionParser();              Expression expression = parser.parseExpression(opLog.opItemIdExpression());              EvaluationContext context = new StandardEvaluationContext();              // 获取参数值              Object[] args = pjp.getArgs();              // 获取运行时参数的名称              LocalVariableTableParameterNameDiscoverer discoverer                  = new LocalVariableTableParameterNameDiscoverer();              String[] parameterNames = discoverer.getParameterNames(method);              // 将参数绑定到context中              if (parameterNames != null) {                  for (int i = 0; i < parameterNames.length; i++) {                      context.setVariable(parameterNames[i], args[i]);                  }              }              // 将方法的resp当做变量放到context中,变量名称为该类名转化为小写字母开头的驼峰形式              if (response != null) {                  context.setVariable(                      CaseFORMat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, response.getClass().getSimpleName()),                      response);              }              // 解析表达式,获取结果              String itemId = String.valueOf(expression.getValue(context));              // 执行日志记录             handle(opLog.opType(), opLog.opItem(), itemId);          }          return response;     }      private void handle(OpType opType,  String opItem, String opItemId) {        // 通过日志打印输出        LOGGER.info("opType = " + opType.name() +",opItem = " +opItem + ",opItemId = " +opItemId);      }  }

以上切面中,有几个点需要大家注意的:

  1、使用@Around注解来指定对标注了OpLog的方法设置切面。

  2、使用Spel的相关方法,通过指定的表示,从对应的参数中获取到目标对象的唯一性标识。

  3、再方法执行成功后,输出日志。

有了以上的切面及注解后,我们只需要在对应的方法上增加注解标注即可,如:

@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})  @OpLog(opType = OpType.QUERY, opItem = "order", opItemIdExpression = "#id")  public @ResponseBody  HashMap view(@RequestParam(name = "id") String id)      throws Exception {  }

上面这种是入参的参数列表中已经有了被操作的对象的唯一性标识,直接使用#id指定即可。

如果被操作的对象的唯一性标识不在入参列表中,那么可能是入参的对象中的某一个属性,用法如下:

@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})  @OpLog(opType = OpType.QUERY, opItem = "order", opItemIdExpression = "#orderVo.id")  public @ResponseBody  HashMap update(OrderVO orderVo)      throws Exception {  }

以上,即可从入参的OrderVO对象的id属性的值获取。

如果我们要记录的唯一性标识,在入参中没有的话,应该怎么办呢?最典型的就是插入方法,插入成功之前,根本不知道主键ID是什么,这种怎么办呢?

我们上面的切面中,做了一件事情,就是我们把方法的返回值也会使用表达式进行一次解析,如果可以解析得到具体的值,也是可以。如以下写法:

@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})  @OpLog(opType = OpType.QUERY, opItem = "order", opItemIdExpression = "#insertResult.id")  public @ResponseBody  InsertResult insert(OrderVO orderVo)      throws Exception {      return orderDao.insert(orderVo);  }

以上,就是一个简单的使用自定义注解+切面进行日志记录的场景。下面我们再来看一个如何使用注解做方法参数的校验。

使用自定义注解做前置检查

当我们对外部提供接口的时候,会对其中的部分参数有一定的要求,比如某些参数值不能为空等。大多数情况下我们都需要自己主动进行校验,判断对方传入的值是否合理。

这里推荐一个使用HibernateValidator + 自定义注解 + AOP实现参数校验的方式。

首先我们会有一个具体的入参类,定义如下:

public class User {      private String idempotentNo;      @NotNull(          message = "userName can't be null"      )      private String userName;  }

以上,对userName参数注明不能为null。

然后再使用Hibernate Validator定义一个工具类,用于做参数校验。

  public class BeanValidator {      private static Validator validator = Validation.byProvider(HibernateValidator.class).configure().failFast(true)          .buildValidatorFactory().getValidator();            public static void validateObject(Object object, Class<?>... groups) throws ValidationException {          Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);         if (constraintViolations.stream().findFirst().isPresent()) {              throw new ValidationException(constraintViolations.stream().findFirst().get().getMessage());          }      }  }

以上代码,会对一个bean进行校验,一旦失败,就会抛出ValidationException。

接下来定义一个注解:

  @Target(ElementType.METHOD)  @Retention(RetentionPolicy.RUNTIME)  public @interface Facade {  }

这个注解里面没有任何参数,只用于标注那些方法要进行参数校验。

接下来定义切面:

  @Aspect  @Component  public class FacadeAspect {      private static final Logger LOGGER = LoggerFactory.getLogger(FacadeAspect.class);      @Autowired      HttpServletRequest request;      @Around("@annotation(com.hollis.annotation.Facade)")      public Object facade(ProceedingJoinPoint pjp) throws Exception {          Method method = ((MethodSignature)pjp.getSignature()).getMethod();          Object[] args = pjp.getArgs();          Class returnType = ((MethodSignature)pjp.getSignature()).getMethod().getReturnType();          //循环遍历所有参数,进行参数校验          for (Object parameter : args) {              try {                  BeanValidator.validateObject(parameter);              } catch (ValidationException e) {                  return getFailedResponse(returnType, e);              }          }          try {              // 目标方法执行              Object response = pjp.proceed();              return response;          } catch (Throwable throwable) {              return getFailedResponse(returnType, throwable);          }      }            private Object getFailedResponse(Class returnType, Throwable throwable)          throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {          //如果返回值的类型为BaseResponse 的子类,则创建一个通用的失败响应          if (returnType.getDeclaredConstructor().newInstance() instanceof BaseResponse) {              BaseResponse response = (BaseResponse)returnType.getDeclaredConstructor().newInstance();              response.setSuccess(false);              response.setResponseMessage(throwable.toString());              response.setResponseCode(GlobalConstant.BIZ_ERROR);              return response;          }          LOGGER.error(              "failed to getFailedResponse , returnType (" + returnType + ") is not instanceof BaseResponse");          return null;      }  }

以上代码,和前面的切面有点类似,主要是定义了一个切面,会对所有标注@Facade的方法进行统一处理,即在开始方法调用前进行参数校验,一旦校验失败,则返回一个固定的失败的Response。

特别需要注意的是,这里之所以可以返回一个固定的BaseResponse,是因为我们会要求我们的所有对外提供的接口的response必须继承BaseResponse类,这个类里面会定义一些默认的参数,如错误码等。

之后,只需要对需要参数校验的方法增加对应注解即可:

@Facade  public TestResponse query(User user) {  }

这样,有了以上注解和切面,我们就可以对所有的对外方法做统一的控制了。

其实,以上这个facadeAspect我省略了很多东西,我们真正使用的那个切面,不仅仅做了参数检查,还可以做很多其他事情。比如异常的统一处理、错误码的统一转换、记录方法执行时长、记录方法的入参出参等等。

总之,使用切面+自定义注解,我们可以统一做很多事情。除了以上的这几个场景,我们还有很多相似的用法,比如:

统一的缓存处理。如某些操作需要在操作前查缓存、操作后更新缓存。这种就可以通过自定义注解+切面的方式统一处理。

代码其实都差不多,思路也比较简单,就是通过自定义注解来标注需要被切面处理的累或者方法,然后在切面中对方法的执行过程进行干预,比如在执行前或者执行后做一些特殊的操作。

使用这种方式可以大大减少重复代码,大大提升代码的优雅性,方便我们使用。

“怎么用自定义注解”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: 怎么用自定义注解

本文链接: https://www.lsjlt.com/news/281899.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 怎么用自定义注解
    本篇内容介绍了“怎么用自定义注解”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!基本知识在Java中,注解分为两种,元注解和自定义注解。很多人...
    99+
    2023-06-16
  • Java怎么自定义注解
    这篇文章主要介绍“Java怎么自定义注解”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java怎么自定义注解”文章能帮助大家解决问题。注解注解为我们在代码中添加信息提供一种形式化的方法,使我们可以在...
    99+
    2023-07-05
  • Java注解怎么自定义使用
    这篇文章主要介绍了Java注解怎么自定义使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java注解怎么自定义使用文章都会有所收获,下面我们一起来看看吧。注解注解基本介绍注解概述:Java 注解(Annota...
    99+
    2023-07-05
  • 怎么使用MyBatis的自定义注解
    要使用MyBatis的自定义注解,首先需要定义一个注解并在需要使用的地方进行标注。然后在MyBatis的配置文件中设置对应的处理器,...
    99+
    2024-03-08
    MyBatis
  • 怎么在java中自定义注解
    这篇文章给大家介绍怎么在java中自定义注解,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Java是什么Java是一门面向对象编程语言,可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序。1、@Val...
    99+
    2023-06-07
  • Java自定义注解
    目录 一、什么是自定义注解 1)Java注解简介 2)Java注解分类 JDK基本注解 JDK元注解 自定义注解 如何自定义注解? 二、自定义注解 1)获取类上注解值 2)获取类属性上的注解属性值 3)获取方法上的注解值  4)获取参数修饰...
    99+
    2023-09-06
    java 开发语言
  • Java怎么实现自定义注解
    本文小编为大家详细介绍“Java怎么实现自定义注解”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java怎么实现自定义注解”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。概念概念:说明程序的。给计算机看的注释:用...
    99+
    2023-07-02
  • java中怎么自定义注解详解
    在Java中,可以使用`@interface`关键字来定义注解。自定义注解的语法如下: public @interface Cust...
    99+
    2023-10-28
    java
  • Feign怎么自定义注解翻译器
    本篇内容主要讲解“Feign怎么自定义注解翻译器”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Feign怎么自定义注解翻译器”吧!Feign自定义注解翻译器新建自定义注解MyUrlpackage...
    99+
    2023-06-29
  • Java中自定义注解
    当使用Java编写应用程序时,我们常常使用注解来为程序添加附加信息,并且可以在运行时读取这些注解。除了Java提供的预定义注解外,我们还可以自定义注解来满足自己的需求。在本文中,我们将介绍Java中自定义注解的基础知识。 一、什么是注解? ...
    99+
    2023-09-24
    java 开发语言
  • Feign怎么利用自定义注解实现路径转义
    本篇内容主要讲解“Feign怎么利用自定义注解实现路径转义”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Feign怎么利用自定义注解实现路径转义”吧!背景近期由于项目中需要,所以需要通过Feig...
    99+
    2023-07-02
  • mybatis代码生成+自定义注解+自定义注释实例
    目录mybatis代码生成配置文件配置类自定义的lombok注解配置代码注释配置mybatis代码生成 <!--mybatis的包和反向生成的包__用来生成...
    99+
    2024-04-02
  • 【Java 注解】自定义注解(注解属性与使用)
    文章目录 前言一、自定义注解与元注解1.注解属性类型 二、注解的生命周期以及作用目标1.生命周期2.作用目标 三,简单使用四,注解属性赋值简化 前言 Java注解是一种元数据(m...
    99+
    2023-10-21
    java spring spring boot log4j 经验分享 笔记 后端
  • 怎么在Java中实现自定义注解
    怎么在Java中实现自定义注解?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。注解是什么?①、引用自维基百科的内容:Java注解又称Java标注,是JDK5.0版本开始支持加入源...
    99+
    2023-06-14
  • spring怎么自定义bean注入
    在Spring中,我们可以使用两种方式来自定义Bean的注入: 使用注解方式:通过在Bean定义类上添加注解来告诉Spring容...
    99+
    2023-10-26
    spring
  • JAVA自定义注解详情
    目录原理:元注解:@Retention参数讲解:案例:给一个类的String属性设置默认值总结原理: 注解的本质是继承Annotation的特殊接口,其具体实现类是Java运行时生成...
    99+
    2024-04-02
  • Spring如何自定义注解
    这篇文章将为大家详细讲解有关Spring如何自定义注解,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。字段注解字段注解一般是用于校验字段是否满足要求,hibernate-validate依赖就提供了很多校验...
    99+
    2023-06-15
  • Java自定义注解的详解
    Java自定义注解Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容。在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解。Java1.5引入了注解,当前许多java框架中大量使用...
    99+
    2023-05-31
    java 自定义 注解
  • java中什么是自定义注解
    今天就跟大家聊聊有关java中什么是自定义注解,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Java可以用来干什么Java主要应用于:1. web开发;2. Android开发;3....
    99+
    2023-06-14
  • 怎么在java项目中对注解自定义
    这期内容当中小编将会给大家带来有关怎么在java项目中对注解自定义,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。java  自定义注解的实例详解Java的Annotation是在5.0版本之后引...
    99+
    2023-05-31
    java 中对 ava
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作