文章目录
前言
Java 1.5 引入了注解(annotations)
注解的含义和几个问题
注解,顾名思义,是一种的标注,在实际的使用中要注意注解的四个特性
注解被用于什么地方,类、方法、注解?
被注解修饰的类或者方法是否会体现在 Java 文档中?
注解的可见性,源码可见、class文件可见、运行期可见?
注解是否能被子类继承?
注解的作用域
作为注解,其最为重要的一个作用是起到方便理解代码含义,所以注解本身并非一定要有什么作用的。单从查看注解的角度来看,注解可以分为源码阶段可见、编译阶段可见和运行阶段可见。
注解是否展示在 Java 帮助文档中?
作为注解,其被使用在类或者方法上时,有时候是有必要让阅读 Java 帮助文档的人看到的,你可以通过 javadoc 命令来做测试
注解被用于什么地方?
注解可以被用于方法上、类上、接口上、其他注解的定义上
元注解
元注解即定义注解的注解,元注解有 @Documented、@Target、@Retention、@Inherited
@Inherited
如果定义注解时使用了 @Inherited 标记,然后用定义的注解来标注另一个父类, 父类又有一个子类(subclass),则父类的所有属性将被继承到它的子类中.
@Documented
注解定义使用@Documented,表示 javadoc 命令生成的文档中将显示本注解的标注内容
@Target
@Target(ElementType.METHOD)//指定注解可以被标注的位置,错误的报错会导致IDE报错
@Retention
@Retention 的使用需要结合 RetentionPolicy。形式如 @Retention(RetentionPolicy.SOURCE)
注解保留的阶段描述
- RetentionPolicy.SOURCE —— 注释只在源代码级别保留,编译时被忽略,比如 @Override 这个注解
- RetentionPolicy.CLASS —— 注释将被编译器在类文件中记录但在运行时不需要JVM保留。这是默认的行为
- RetentionPolicy.RUNTIME —— 注释将被编译器记录在类文件中在运行时保留VM,因此可以反读
自己写注解
定义一个注解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnonation {
String name() default {};//在使用本注解时可以添加额外信息
}
自定义注解的使用
public class TestA {
@MyAnnonation(name="1")
public void test(){
}
}
最佳实践
@Documented 的效果
javadoc 命令生成说明文档
MyAnnonation 的 @Documented被注释的话
MyAnnonation 的 @Documented被保留且 TestA 使用该注解@MyAnnonation(name=“1”)
利用反射方法获取注解
这里是获取方法上的注解
import org.junit.Test;
import java.lang.reflect.Method;
public class TestATest {
@Test
public void test(){
TestA testA=new TestA();
try{
Method m=TestA.class.getMethod("test");
MyAnnonation myAnnonation=m.getAnnotation(MyAnnonation.class);
if(myAnnonation!=null){
System.out.println("通过反射机制获取到了方法上的注解");
}
}catch(NoSuchMethodException e){
//TODO 反射方法不存在异常
}
}
}
为注解设置切点
自定义注解后为注解设置切点,然后可以采取其他操作
@Pointcut("@annotation(包路径.MyAnnonation)")
public void MyAnnonationAspect() {
}
Spring 拦截器中注解的应用
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
public class MyHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
if (method.isAnnotationPresent(MyAnnonation.class)){//这是重点
//表示该方法被 注解 @MyAnnonation 修饰了
}
return true;
}
//略其他方法
}
@Target(RetentionPolicy.SOURCE)、@Target(RetentionPolicy.CLASS ) 的辨析
从一般角度来看,@Target(RetentionPolicy.RUNTIME)注解可以使我们的代码在运行期通过反射获得注解,但是 @Target(RetentionPolicy.SOURCE)、@Target(RetentionPolicy.CLASS )都无法为我们提供更多的功能
关于这个问题,我摘取了网上的一段评论
“源代码级别的注解有两个意图,
一是作为文档的补充, 给人看的, 比如@Override注解、@SuppressWarnings注解,
二是作为源代码生成器(java和android都有注解处理器APT)的材料,
比如ButterKnife框架.同样字节码级别的注解, 可以作为字节码修改, 插桩, 代理的依据,
可以使用aspectj, asm等工具进行字节码修改.
比如一些模块间调用, 如果你直接写代码, 会导致耦合,
此时可以加入一个注解, run一下asm这样的工具,
将注解标注的方法字段以generate的方式插入进去.
运行时级别的注解, 显然是用于反射后参与某些业务逻辑用的, 比如spring依赖注入”