一、注解(Annotation)
用一个词就可以描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据。
注解这一概念是在java1.5版本提出的,说明Java提供了一种原程序中的元素关联任何信息和任何元数据的途径的方法。
Java中的类、方法、变量、参数、包都可以被注解。
注解仅仅是元数据,和业务逻辑无关,所以当你查看注解类时,发现里面没有任何逻辑处理。
二、注解的分类
1)按照运行机制划分:
【源码注解→编译时注解→运行时注解】
源码注解:只在源码中存在,编译成.class文件就不存在了。
编译时注解:在源码和.class文件中都存在。像前面的@Override、@Deprecated、 @SuppressWarnings,他们都属于编译时注解。
运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解。像@Autowired自动注入的 这样一种注解就属于运行时注解,它会在程序运行的时候把你的成员变量自动的注入进来。
2)按照来源划分:
【来自JDK的注解——来自第三方的注解——自定义注解】
3)元注解:
元注解是给注解进行注解,可以理解为注解的注解就是元注解。
三、JDK 内置的 Annotation
JDK内置的注解主要有以下三个,详情介绍如下:
@Override,表示当前的方法定义将覆盖超类中的方法。源码如下:
package java.lang;
import java.lang.annotation.*;
/**
* @since 1.5
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Deprecated,此注解为它的元素编译器将发出警告,注解@Deprecated表示不赞成使用的代码,被弃用的代码。源码如下:
package java.lang;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
/**
* @since 1.5
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
@SuppressWarnings,关闭不当编译器警告信息。源码如下:
package java.lang;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
/**
* @since 1.5
*/
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
以上三个注解是我们在代码开发过程中常常用到的,那么在这几个注解类头部的是什么东东呢?其实它就是 JDK 提供的元注解,它负责注解的创建.
四、元注解
定义: 可以理解为定义注解的东东,如下为每个元注解的具体描述:
注解 | 描述 |
---|---|
@Target | 表示该注解可以用于什么地方,可能的ElementType参数有:CONSTRUCTOR:构造器的声明; FIEL 域声明(包括enum实例); LOCAL_VARIABLE:局部变量声明 METHOD; 方法声明 PACKAGE:包声明 ; PARAMETER:参数声明 ; TYPE:类、接口(包括注解类型)或enum声明 |
@Retention | 表示需要在什么级别保存该注解信息,可以理解为表示生命周期。可选的RetentionPolicy参数包括:SOURCE:注解将被编译器丢弃; CLASS 注解在class文件中可用,但会被JVM丢弃; RUNTIME:JVM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。 |
@Documented | 将注解包含在Javadoc中.它属于标记注解,没有成员 |
@Inherited | 允许子类继承父类中的注解.它属于标记注解,没有成员 |
五、注解的定义、实例以及注意点
1.注解的格式
元注解
public @interface 注解名称{
定义体;
}
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum、Annotation类型、以上所有类型的数组)。可以通过default来声明参数的默认值。
注意:
1.定义体可以不存在.
2.注解类中的方法只能用public或者默认这两个访问权修饰,不写public就是默认,如下示例:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyTestAnnotation {
public enum Sex { BOY, GIRL}
Sex getSex() default Sex.BOY;
}
3.如果注解类中只有一个成员,最好把方法名设置为"value",如下示例:
import java.lang.annotation.*;
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Name {
String value() default "";
}
4.注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此, 使用空字符串或0作为默认值是一种常用的做法。
六、注解的使用示例
按照上述五中定义的注解示例使用如下:
@Name(value = "say to Annotation")
public String say(){
return "Hello Annotation";
}
@MyTestAnnotation(getSex=MyTestAnnotation.Sex.GIRL)
public void doSome(){
}
七、注解的解析(原理)
如下示例解析步骤为:
1.获取注解类;
2.获取注解类所有的方法;
3.循环遍历方法获取方法上的注解;
4.获取注解信息
public static void main(String [] args){
getMyAnnotation();
}
/**
* 解析使用注解的类,获取通过注解设置的属性
*/
private static void getMyAnnotation() {
Class annotationTestClass = AnnotationTest.class;
for(Method method : annotationTestClass.getMethods()) {
MyTestAnnotation testAnnotation = method.getAnnotation(MyTestAnnotation.class);
Name nameAnnotation = method.getAnnotation(Name.class);
/**
* 检查方法上是否有 MyTestAnnotation 注解
*/
if(testAnnotation != null && method.isAnnotationPresent(MyTestAnnotation.class)){
System.out.println(" Method Name : " + method.getName());
System.out.println(" Annotation value : " + testAnnotation.getSex());
System.out.println(" --------------------------- ");
}
/**
* 检查方法上是否有 Name 注解
*/
if(nameAnnotation != null && method.isAnnotationPresent(Name.class)){
System.out.println(" Method Name : " + method.getName());
System.out.println(" Annotation value : " + nameAnnotation.value());
System.out.println(" --------------------------- ");
}
}
}
运行结果如下:
Method Name : say
Annotation value : say to Annotation
---------------------------
Method Name : doSome
Annotation value : GIRL
---------------------------
八、自定义注解与 AOP 的结合使用示例
第一步定义一个注解(以记录日志为例),代码示例如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String description() default "";
}
第二步编写 AOP 代码,代码示例如下:
@Aspect
@Component
public class WebLogAspect {
@Resource
private TSysMqiLogService mqiLogService;
private long currentTime = 0L;
/**
* AOP切入点
*/
@Pointcut("@annotation(com.xxx.mqi.common.aop.Log)")
public void logPointcut(){}
/**
* 配置环绕通知,使用在方法logPointcut()上注册的切入点
* @param joinPoint join point for advice
*/
@Around("logPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint){
Object result = null;
currentTime = System.currentTimeMillis();
try {
result = joinPoint.proceed();
} catch (Throwable e) {
throw new BadRequestException(e.getMessage());
}
TSysMqiLogEntity mqiLogEntity = new TSysMqiLogEntity("INFO", System.currentTimeMillis() - currentTime);
mqiLogService.saveLog(joinPoint, mqiLogEntity);
return result;
}
}
第三步就是在需要记录日志的方法上使用此注解即可,代码示例如下:
@Log(description = "目标应用系统列表查询")
@RequestMapping(value = "/list", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
public R list(HttpServletRequest request){
Object obj = cacheServiceI.get(CacheServiceI.FOREVER_CACHE, DYNAMIC_DB_CONFIGS_FOREVER_CACHE_KEY);
return R.ok().put("data", obj);
}
写在最后:以上为个人学习的一点小随笔,如有错误敬请谅解.如大佬能够指点小生更加感激不尽,谢谢!