注解是从JDK1.5以后引入的,现在它早已经成为JAVA开发平台中非常重要的一部分。对于稍有经验的开发工程师来说,我们在平时工作中都大量运用的注解。那么我们不禁要提出这样的疑问:
1.那么注解是什么?
2.JAVA为什么要引入注解?
3.注解的本质是什么?
4.我们平时都有哪些常见的注解?
5.我们如何编写自定义的注解?
一、什么是注解
Annontation,中文成为注解,它提供可一种安全的雷石玉注释的机制,用来将任何的信息或者元数据(metadata)与程序元素(类、方法、变量等)进行关联,为程序的元素加上更加直观明了的说明。Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。
二、JAVA为什么要引入注解
我们先举一个例子,在JDK1.5之前,我们要在spring中生命一个bean,就只能采用xml的方式:
<bean id="userServcie" class="com.demo.service"> </bean>
当有了注解之后,我们就可以不用写XML的配置文件,直接在UserService的类上加一个@Service的注解就可以。
@Service
public class UserService{
}
从上面的例子中可以看出:
- 通过XML的方式,配置和代码被隔离开了,所以XML比较适合做一些全局的、与具体代码无关的配置;
- 通过注解的方式,注解和代码紧密结合,所以一些与代码相关都比较高的操作,建议用注解。
除此之外,我们看到,通过注解的方式进行编码,特别快,大量减少了代码量。
综上所述,我认为之所以要引入注解:
1.通过注解的方式进行编码,特别快,大量减少了代码量;
2.当开发人员希望元数据与代码紧密耦合,使用注解更加方便;
3.注解的本质是什么
上面我们了解了注解的基本概念和简单的示例,那么注解本质上是什么呢?是一个类还是接口呢?我们知道,注解也被称为声明式接口。我这里定一个了一个注解,从它的继承关系看到:
@OperateLog继承了Annotation 接口,那基本就可以确定它是一个接口了。
我们通过它的字节码看到,这里调用了invokeinterface指令,所以这就是一个接口。即注解是一个接口,并且是一个继承自Annotation的接口。
4.我们平时都有哪些常见的注解
4.1 四种元注解
java.lang.annotation提供了四种元注解,专门注解其他的注解,他们分别是:@Documented,@Retention,@Target,@Inherited。下面我们简要说一下:
- @Documented:它的作用是能够将注解中的元素包含到 Javadoc 中去;
- @Retention:Retention 的英文意为保留期的意思, @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。它的取值如下:
RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。 - @Target:Target 是目标的意思,@Target 指定了注解运用的地方。当一个注解被 @Target 注解时,这个注解就被限定了运用的场景,它的取值如下:ElementType.ANNOTATION_TYPE 可以给一个注解进行注解;
ElementType.CONSTRUCTOR 可以给构造方法进行注解;
ElementType.FIELD 可以给属性进行注解;
ElementType.LOCAL_VARIABLE 可以给局部变量进行注解;
ElementType.METHOD 可以给方法进行注解;
ElementType.PACKAGE 可以给一个包进行注解;
ElementType.PARAMETER 可以给一个方法内的参数进行注解;
ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举。 - @Inherited:Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
4.2 Java 预置的注解
@Deprecated:这个元素是用来标记过时的元素,。编译器在编译阶段遇到这个注解时会发出提醒警告,告诉开发者正在调用一个过时的元素比如过时的方法、过时的类、过时的成员变量,这个注解我们基本都都用到过或者见到过;
@Override:提示子类要复写父类中被 @Override 修饰的方法,这个是我们开发人员平时遇到最多的了;
@SuppressWarnings:阻止或者压制警告,阻止编译器发出警报信息;
5.我们如何编写自定义的注解
这里我做一个简单记录用户操作记录的注解@OperateLog
1.首先定义注解类OperateLog
package com.demo.annotation;
import java.lang.annotation.*;
/**
* 这是一个自定义注解,标记操作日志
*
* @author lxc
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface OperateLog {
/**
* 模块
*/
public String title() default "";
/**
* 操作方式
*
* @return
*/
public String businessType() default "更新";
/**
* 是否保存方法中的参数
*/
public boolean isSaveParamData() default true;
}
写一个test类解析注解:
import com.demo.annotation.OperateLog;
import java.lang.reflect.Method;
/**
* 这是一个自定义注解测试类
*
* @author lxc
*/
public class OperateLogTest {
public static void main(String[] args) {
Method[] declaredMethods = UserService.class.getDeclaredMethods();
for (Method method : declaredMethods) {
if (method.isAnnotationPresent(OperateLog.class)) {
OperateLog operateLog = method.getAnnotation(OperateLog.class);
System.out.println(operateLog.title() + ":" + operateLog.businessType() + ":" + operateLog.isSaveParamData());
}
}
}
}
运行结果:
用户模块:删除:true
用户模块:新增:true
用户模块:更新:true
在生产中我们结合AOP,在切点处,记录每次的操作记录。