在java中,注解应该是见得最多的一样法宝了,用得好的话可以让代码更加优雅.平时一直沉迷于注解给我们带来的方便中,只知道用,却没有深入了解其原理和实现,本篇就和大家一起探秘jdk中的注解.
一.注解的概念
Java提供了一种原程序中的元素关联任何信息和任何元数据的途径和方法。个人的理解就是:注解可以让你更方便的关联任何类,方法,元素.听起来好像还是不太好懂,没关系,继续往下看,基本上学完自定义注解对注解的理解就比较清晰了.
二.jdk自带的注解
注解是从jdk1.5开始出现的,jdk自带的常见注解有三种,大家也应该都不陌生,这三种注解分别是:@Override,@Deprecated,@SuppressWarnings.
@Override:见得最多的一种了,子类覆盖或重写父类中的方法时使用.
@Deprecated:deprecated这个单词是弃用的意思,见名知意,这个注解主要用于弃用某个方法时使用,也就是说该方法可能已经过时了,但为了系统的兼容性该代码仍被保留,也可以被使用,但不推荐,你可以在Jdk源码中找到大量的这种注解,比如Date类中封装的一些操作日期的方法,值得注意的是一旦被加了该注解,方法在编译器里调用时会出现删除线.
@SuppressWarnings:suppress是压制的意思,这个注解的意思就是关闭不当编译器警告信息,强制把编译器的警告给压制掉,用的非常少了,几乎没怎么碰到过,如果你有代码洁癖,可以在调用使用@Deprecated注解的方法时配合@SuppressWarnings注解,可以屏蔽掉来自编译器的警告.
三.自定义注解
自定义注解是非常强大的一项功能,巧妙使用不仅可以让你减少代码开发量,更能让你的代码充满"逼格".
在学习自定义注解前,先来学习下四种元注解,因为自定义注解的实现借助于它们.
元注解也就是jdk自己提供的注解,这四种元注解分别是:
@Target:使用该注解后,可以指定自定义的注解可以被用于什么地方,具体的由其中的ElementType指定.
@Documented:使用该注解后,自定义的注解也会被生成到javadoc中.
@Retention:指明注解被保留的时期,在RetentionPolicy中指定,一般都是RUNTIME.
@Inhertried:允许子类继承父类中的注解.加了该注解表名子类可以继承到父类中的注解,但该注解仅加在类上有效,方法上无效.
@Target |
表示该注解可以用于什么地方,可能的ElementType参数有: CONSTRUCTOR:构造器的声明 FIELD:域声明(包括enum实例) LOCAL_VARIABLE:局部变量声明 METHOD:方法声明 PACKAGE:包声明 PARAMETER:参数声明 TYPE:类、接口(包括注解类型)或enum声明 |
@Retention |
表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括: SOURCE:注解将被编译器丢弃 CLASS:注解在class文件中可用,但会被VM丢弃 RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。 |
@Documented |
将注解包含在Javadoc中 |
@Inherited |
允许子类继承父类中的注解 |
了解完了四种元注解,我们就实操一下,来自定一个注解玩玩.
自定义注解的格式如图:
自定义注解的格式必须如图,这是jdk制定的,所以别想自己搞一套...我自定义了一个注解@Man,指明该注解只能在字段上使用,而且是在运行期间保留.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Man {
String sex() default "男";
}
然后我们可以通过反射来测试一下该注解,先创建一个类Person,在其字段上加上该注解,然后我们看看是否生效了:
public class Person {
@Man
public String sex;
}
public class Test {
public static void main(String[] args) {
Class clzz = Person.class;
try {
Field field = clzz.getField("sex");
System.out.println(field.getAnnotation(Man.class).sex());
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
可以看到注解已生效,乍一看自定义注解好像没啥太大用,但实际上如果你这么想了只能说明你我脑洞不够大,想象力还不够丰富.
在实际开发中,有很多场景自定义注解都是可以发挥威力的,比如我们熟悉的hibernate,jpa,mybatis等,大家都应该用过@TableFiled(value="xxx")这个注解,就是基于自定义注解实现的.再比如如果让你开发一个权限非常复杂的系统,该系统拥有多级权限,不仅每一级拥有的接口访问权限不一样,接口中看到的内容也不一样,涉及所管辖的区域,这样一套原本比较复杂的权限开发,如果用常规途径去解决可能会比较棘手,但如果用了自定义注解+AOP,问题就变得比较容易了:
AOP统一拦截,然后根据注解值判断所拥有的权限列表,再根据接口请求的参数进行拦截区域内容的权限,问题就解决了,完全不侵入原有代码,只需要在原有接口上加上该注解,对这种优雅的操作方式在下只能说完美!
还有一种妙用,也是今天刚刚从别人那边GET到的,就是自定义的注解上还可以加上@Component注解,将你自定义的注解交给srping容器去管理,然后你就可以通过下面这种方式来获取你用自定义注解标记过的类.
/ ctx 为Spring的ApplicationContext //
Bean Map<String, Object> myAnnotationBeanMap = ctx.getBeansWithAnnotation(Man.class);
这可以在Bean初始化后对一些类做一些特殊操作.