在我们日常编程过程中,会用到许多注解,比如@Component、@Bean等注解,这些注解有什么好处呢?最明显的便是简洁代码,省略很多不必要的代码,同时注解也可以实现多样化。一般自定义注解步骤主要是,定义注解类-->注解类使用-->解析注解,在运行时或编译期发现标记进行特殊处理。这样对于整个项目或者多个项目中可以使用自定义注解标记,完成自己需要的特定操作,避免了每个使用处都有大篇幅的解析代码。本文主要讲解自定义注解结合AOP的应用,其中可以利用AOP编程识别注解标记进行处理。
1.定义注解类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogInfo {
/**
* 当前时间
* @return
*/
long time();
/**
* 日志内容
* @return
*/
String info() default "";
/**
* 日志类型
* @return
*/
LogType logtype() default LogType.INFO;
}
其中LogType枚举类:
public enum LogType {
ERROR,
INFO,
DEBUG
}
自定义注解也需要元注解进行标记,元注解可以理解为接口或者抽象类,完成顶层基本功能。常见元注解主要有以下几类
1.@Target
@Target注解,是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的。它使用一个枚举类型定义如下:
public enum ElementType {
/**类,接口(包括注解类型)或枚举的声明*/
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/**属性的声明*/
/** Field declaration (includes enum constants) */
FIELD,
/**方法的声明*/
/** Method declaration */
METHOD,
/**方法形式参数声明*/
/** Formal parameter declaration */
PARAMETER,
/**构造参数的声明*/
/** Constructor declaration */
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
}
2.@Retention
@Retention注解,主要表示在什么时期被识别,即其生命周期,常见生命周期如下枚举类:
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
目前比较常用的是RUNTIME,运行时识别,通过反射原理进行解析实现特定操作。
3.@Documented:是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中,标记则是,反之亦然。
4.@Inherited
@Inherited注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。@Inherited注解只对那些@Target被定义为ElementType.TYPE的自定义注解起作用。
2.注解使用
@RestController
public class TestController{
@RequestMapping("/test")
@LogInfo(time =0,info = "请求测试",logtype = LogType.ERROR)
public void testLimit(){
System.out.println("测试注解功能");
}
}
3.解析注解
对于识别到注解之后进行特定处理,实现多样化操作。
@Component
@Aspect
public class LogAspect {
private Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Pointcut("@annotation(com.carson.cachedemo.annotation.LogInfo)")
public void pointcut(){
}
@Around("pointcut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
String url = request.getRequestURI();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method signatureMethod = signature.getMethod();
LogInfo logInfo = signatureMethod.getDeclaredAnnotation(LogInfo.class);
Date time = new Date(System.currentTimeMillis());
LogType type = logInfo.logtype();
switch (type){
case INFO:
logger.info("用户请求url={},time={},value={}",url,time,logInfo.info());
break;
case DEBUG:
logger.debug("用户请求url={},time={},value={}",url,time,logInfo.info());
break;
case ERROR:
logger.error("用户请求url={},time={},value={}",url,time,logInfo.info());
break;
default:
logger.info("用户请求url={},time={},value={}",url,time,logInfo.info());
break;
}
}
}
其中@Aspect注解表示引入AOP,@Compoment则是把这个类交给spring容器管理。
该类中引入了AOP(切面编程),需要引入以下依赖才会生效(缺一不可)
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
最后补齐springboot启动一个工程最基本的配置(这个就不细说了),然后调用接口,触发请求,得到如下结果:
上图说明注解生效。一个简单的注解例子,有助于帮助了解自定义注解的步骤。具体的需要举一反三,具体场景具体讨论。注解是为了简化冗余代码,也增加了灵活性。