Annontation是Java5开始引入的新特性,中文名称叫注解。html
好比Springjava
Spring、Mybatis使用了大量的注解,这些注解可以让人读懂别人写的代码,特别是框架相关的代码;同时让编程更加简洁,代码更加清晰;也让别人高看一眼。spring
既然自定义注解有这么多好处,那咱们如何去自定义注解?何时用自定义注解?编程
好比公司有个后台管理系统,每一个接口都要去记录它的操做日志,若是按照之前的思惟咱们就每一个接口里面去写相应的操做日志记录代码。这种方式确定很差,这时咱们想到了spring的aop。其实自定义注解的实现本质上是spring aop对自定义注解的支持。json
那么在了解自定义注解以前,咱们先了解下元注解,即注解的注解。app
以Log.java为例:框架
@Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { /** * 模块 * @return */ String title() default ""; /** * 功能 * @return */ String action() default ""; }
下面咱们分别介绍下元注解:ide
@Target 是注解的做用域 :表示该注解能够用于一个类中的那些属性及方法上,若是做用域类型有多个用英文逗号分隔。spring-boot
public enum ElementType { /** 用于描述类、接口(包括注解类型) 或enum声明 */ TYPE, /** 成员变量、对象、属性(包括enum实例) */ FIELD, /** 用于描述方法 */ METHOD, /** 用于描述参数 */ PARAMETER, /** 用于描述构造器 */ CONSTRUCTOR, /** Local variable declaration */ LOCAL_VARIABLE, /** Annotation type declaration */ ANNOTATION_TYPE, /** Package declaration */ PACKAGE, /** * Type parameter declaration * * @since 1.8 */ TYPE_PARAMETER, /** * Use of a type * * @since 1.8 */ TYPE_USE }
@Retention:表示该注解的生命周期。测试
public enum RetentionPolicy { /** * 在编译阶段丢弃。这些注解在编译结束以后就再也不有任何意义,因此它们不会写入字节码。 */ SOURCE, /** * 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式 */ CLASS, /** * 始终不会丢弃,运行期也保留该注解,所以可使用反射机制读取该注解的信息。咱们自定义的注解一般使用这种方式。 */ RUNTIME }
@Inherited:此注解是标识性的元注解,表示当前注解能够由子注解来继承。
@Documented:表示生成javadoc的时候会包含注解。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
@Log(title = "测试", action = "获取")
@RestController @RequestMapping(value = "/v1/demo") public class DemoController { @Log(title = "测试", action = "获取") @GetMapping(value = "/{id}") public BaseResponse selectCouponById(@PathVariable("id") Long id) { System.out.println("controller"); return ResultResponse.success(StatusResultEnum.SUCCESS, id); } }
这个是最主要的类,可使用自定义注解或针对包名实现AOP加强。
1)这里实现了对自定义注解的环绕加强切点,对使用了自定义注解的方法进行AOP切面处理;
2)对方法运行时间进行监控;
3)对方法名,参数名,参数值,对日志描述的优化处理;
在方法上增长@Aspect 注解声明切面,使用@Pointcut 注解定义切点,标记方法。
使用切点加强的时机注解:@Before,@Around,@AfterReturning,@AfterThrowing,@After
@Aspect
做用是把当前类标识为一个切面供容器读取
@Pointcut
定义一个切点
@Before
标识一个前置加强方法,至关于BeforeAdvice的功能
@After
final加强,无论是抛出异常或者正常退出都会执行
@component
把普通pojo实例化到spring容器中,至关于配置文件中的<bean id="" class=""/>
package com.lian.demo.aop; import com.alibaba.fastjson.JSONObject; import com.lian.demo.common.annotation.Log; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 操做日志记录处理 */ @Aspect @Component @Slf4j public class LogAspect { /** * 配置织入点 */ @Pointcut("@annotation(com.lian.demo.common.annotation.Log)") public void logPointCut() { } /** * 环绕加强,至关于MethodInterceptor */ @Around("logPointCut()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { log.info("Around=========="); Object res = null; long time = System.currentTimeMillis(); try { res = joinPoint.proceed(); time = System.currentTimeMillis() - time; log.info("res=========={}", JSONObject.toJSONString(res)); return res; } catch (Throwable e) { log.error("LogAspect 操做失败:", e.getMessage()); e.printStackTrace(); } } @Before("logPointCut()") public void before(JoinPoint joinPoint) { Log logBean = this.getAnnotationLog(joinPoint); log.info("打印: {} 开始前", logBean.title()); } /** * 后置最终通知,final加强,无论是抛出异常或者正常退出都会执行 */ @After("logPointCut()") public void after(JoinPoint jp){ log.info("方法最后执行"); } /** * 处理完请求,返回内容 * @param ret */ @AfterReturning(returning = "ret", pointcut = "logPointCut()") public void doAfterReturning(Object ret) { log.info("方法的返回值 : {}", ret); // TODO 业务代码 // addOperationLog(joinPoint,res,time); } /** * 后置异常通知 * * @param joinPoint * @param e */ @AfterThrowing(value = "logPointCut()", throwing = "e") public void doAfterThrowing(JoinPoint joinPoint, Exception e) { log.error("方法异常时执行 : {}", e); } /** * 是否存在注解,若是存在就获取 */ private Log getAnnotationLog(JoinPoint joinPoint) { Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); if (method != null) { return method.getAnnotation(Log.class); } return null; } }
经过反射获取注解信息
public class Test { public static void main(String[] args) { try { Class clazz = Class.forName("com.lian.demo.controller.DemoController"); // 获取全部的方法 Method[] method = clazz.getMethods(); for(Method m : method) { // 判断方法上是否使用了自定义注解Log boolean isExist = m.isAnnotationPresent(Log.class); if (isExist) { Log log = m.getAnnotation(Log.class); System.out.println(log.title()); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
参考
https://www.cnblogs.com/wenjunwei/p/9639909.html
https://blog.csdn.net/u013825231/article/details/80468167
https://www.cnblogs.com/Qian123/p/5256084.html#_label2