2021-04-30

自定义注解,你会了吗?

日常开发中用到了各式各样的注解,常用的注解@Override、@param、@Autowired、@Service等等,这些都是JDK或者Spring这类框架自带。在类,方法,变量,参数,包都可以用注解来注释。很多小伙伴可能还停留在使用层面,知道怎么用,但并不知道实现原理,更没亲自写过自定义注解运用在实际项目中解决问题。

 

接下来聊聊注解的基础,再聊聊自定义注解在实际项目中的使用。

 

注解作用

1、生成文档,早期最常见的@return,@param

2、在编译时进行检查,例如@Override,检查是否重写父类

3、简化配置文件,使得代码更清晰

 

什么是内置注解,元注解,自定义注解?

 

内置注解

@Override:作用在方法上,声明重写父类的方法

@Deprecated:作用方法,属性,或者类上,标识已过时

@SuppressWarings:用于抑制编译器警告,告知编译器,忽略它们产生了特殊警告

@SafeVarargs:是jdk1.7引入的注解,作用抑制在使用泛型和可变参数搭配使用产生的编译器告警

 

元注解:元注解是由JDK5.0开始提供的,不能更改,用于定义其他注解,也就是对我们自定义注解进行定义

@Target:声明注解的作用范围可以是类,方法,方法参数变量等,也可以通过枚举类ElementType表达作用类型(可以查看源码)

@Retention:声明注解保留时长的作用域,可以理解为运行环境,

    SOURCE(在源文件中有效)

    CLASS(源文件编译成Class类文件中有效)

    RUNTIME(在运行时有效)

@Documented:文档化注解,作用可以被javadoc此类工具文档化

@Inherited:声明此类能否被继承

 

自定义注解

/** * 元注解 * public @interface 注解名称 { * 类型 属性名() default 默认值 * } *///用来声明自定义注解作用范围,变量、方法、类、包...@Target({ElementType.METHOD, ElementType.TYPE})//声明注解在运行时有效@Retention(RetentionPolicy.RUNTIME)//文档化@Documented//此类能否被继承@Inheritedpublic @interface TestAnnotation { String value() default "";}

自定义注解的简单使用

/** * 定义用户注解 */@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic @interface UserAnnontion { String name(); int age() default 18; String[] familyMembers() default {};}/** * 方法注解 */@User(name="阿杰",age = 18,familyMembers = {"xxx","xxx","xxx"})public void getUser() {}

  

 

 

通过反射获取注解信息

public class TestAnnotationReflex { public static void main(String[] args) {  try {   Class stuClass = Class.forName("com.example.demo.User");   Method method = stuClass.getMethod("getUser");   if(method.isAnnotationPresent(UserAnnontion.class)){    UserAnnontion userAnnontion = method.getAnnotation(UserAnnontion.class);    System.out.println("name: " + userAnnontion.name()     + ", age: " + userAnnontion.age() + ", familyMembers: " + userAnnontion.familyMembers()[0]);   }  } catch (Exception e) {   e.printStackTrace();  } }}

以上的只是基础的知识,可能很多人还没搞明白自定义注解到底有什么作用,好像只是简单的注释说明,或者是通过反射获取注解信息。

 

接下来,使用自定义注解结合Spring AOP来讲解一下平时工作中使用的场景,Spring AOP常用于拦截器,事务,日志,权限,AOP就不再讲解了,举个例子讲解一下日志记录。在实际项目中,在调试或者排查异常信息时,日志是否记录着请求入参信息,是否都是使用log.info("请求参数")这样的写法记录日志,如果每次都需要这样手写,很容易遗漏,并且重复代码也比较多,在这种场景下,可以做一个统一日志处理的方案,通过使用自定义注解和切面来实现这个功能。


自定义注解+切面实现统一日志处理

 

自定义日志注解

/** * 自定义操作日志注解 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface OptLog { /**  * 业务  * @return  */ String business(); /**  * 操作类型,增删改查  * @return  */ OptType optType();}

 

声明日志切面组件

import com.alibaba.fastjson.JSONObject;import com.example.demo.annotation.OptLog;import com.example.demo.annotation.OptType;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.core.LocalVariableTableParameterNameDiscoverer;import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Aspect@Componentpublic class OptLogAspect { private static final Logger LOG = LoggerFactory.getLogger(OptLogAspect.class); /**  * 声明切入点,凡是使用该注解都经过拦截  */ @Pointcut("@annotation(com.example.demo.annotation.OptLog)") public void OptLog() { } @Before("OptLog()") public void doOptLogBefore(JoinPoint proceedingJoinPoint) {  LOG.info("前置通知, 在方法执行之前执行..."); } @After("OptLog()") public void doOptLogAfter(JoinPoint proceedingJoinPoint) {  LOG.info("后置通知, 在方法执行之后执行..."); } @AfterReturning("OptLog()") public void doOptLogAfterReturning(JoinPoint proceedingJoinPoint) {  LOG.info("返回通知, 在方法返回结果之后执行..."); } @AfterThrowing("OptLog()") public void doOptLogAfterThrowing(JoinPoint proceedingJoinPoint) {  LOG.info("异常通知, 在方法抛出异常之后执行..."); } /**  * 设置环绕通知,围绕着方法执行  *  * @param proceedingJoinPoint  * @return  */ @Around("OptLog()") public Object optLogAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {  Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();  if (method == null) {   return null;  }  // 获取方法名称  String methodName = proceedingJoinPoint.getSignature().getName();  LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();  // 请求参数名称  String[] parameterNames = discoverer.getParameterNames(method);  // 请求参数值  Object[] paramValues = proceedingJoinPoint.getArgs();  OptLog optLog = method.getAnnotation(OptLog.class);  this.handle(optLog.optType(), optLog.business(), methodName, parameterNames, paramValues);  return proceedingJoinPoint.proceed(); } /**  * 日志处理  *  * @param optType  * @param business  * @param methodName  * @param parameterNames  * @param paramValues  */ public void handle(OptType optType, String business, String methodName,      String[] parameterNames, Object[] paramValues) {  JSONObject jsonObject = new JSONObject();  if (parameterNames != null && parameterNames.length > 0) {   for (int i = 0; i < parameterNames.length; i++) {    jsonObject.put(parameterNames[i], paramValues[i]);   }  }  LOG.info("optType:" + optType + ",business:" + business + ", methodName:" + methodName + ", params:" + jsonObject); }}

  

 

控制层运行结果

@RestController@RequestMapping("/user/")public class UserController { @OptLog(optType = OptType.CREATE,business = "用户信息") @RequestMapping("create") public String createUser(String userName,int age,String address) {  System.out.println("方法执行中...");  return "success"; }}

  

 


运行结果

15:32:49.494 [http-nio-8080-exec-2] INFO c.e.d.a.OptLogAspect - [handle,91] - optType:CREATE,business:用户信息, methodName:createUser, params:{"address":"广州市","userName":"阿杰","age":18}15:32:49.494 [http-nio-8080-exec-2] INFO c.e.d.a.OptLogAspect - [doOptLogBefore,32] - 前置通知, 在方法执行之前执行...方法执行中...15:32:49.495 [http-nio-8080-exec-2] INFO c.e.d.a.OptLogAspect - [doOptLogAfterReturning,42] - 返回通知, 在方法返回结果之后执行...15:32:49.495 [http-nio-8080-exec-2] INFO c.e.d.a.OptLogAspect - [doOptLogAfter,37] - 后置通知, 在方法执行之后执行...









原文转载:http://www.shaoqun.com/a/715759.html

跨境电商:https://www.ikjzd.com/

shirley:https://www.ikjzd.com/w/1684

墩煌网:https://www.ikjzd.com/w/189


日常开发中用到了各式各样的注解,常用的注解@Override、@param、@Autowired、@Service等等,这些都是JDK或者Spring这类框架自带。在类,方法,变量,参数,包都可以用注解来注释。很多小伙伴可能还停留在使用层面,知道怎么用,但并不知道实现原理,更没亲自写过自定义注解运用在实际项目中解决问题。接下来聊聊注解的基础,再聊聊自定义注解在实际项目中的使用。注解作用1、生成文档
acedota:https://www.ikjzd.com/w/2070
拍拍:https://www.ikjzd.com/w/2205
bol:https://www.ikjzd.com/w/291
6大爆单绝密算法,2019年速卖通营销策略大解析:https://www.ikjzd.com/home/18136
"拓展全球·掘金品类"三头六臂2016亚马逊春季政策及重点品类解读:https://www.kjyunke.com/courses/115
青春之痒 寂寞妹妹被老汉干:http://www.30bags.com/m/a/254782.html

No comments:

Post a Comment