日常开发中用到了各式各样的注解,常用的注解@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