在学习Effictive Java注解几节的内容之前,我们先看一下Java注解的基础知识。
1. Java的内置注解。
2. 定义注解和元注解。
3. 怎么使用注解。
4. 自定义注解实现依赖注入。
1. Java的内置注解。
a.@Override
这个我们非常熟悉,子类覆盖父类的方法是需要在子类方法上添加这个注解。如果你的方法拼写错误,编译器就会报错。
b.@Deprecated
这个注解我们很少会用到,当部分人肯定会碰到过。表示注解的元素过期了,不再使用了,如果使用的话,编译器会给警告。
c.@SuppressWarnings
这个我们也很熟悉,当我们需要编译器忽略掉这个警告的时候使用它。
总的来说,这三个注解其实并不会影响程序的运行,我们用到的功能比较强大的注解都是一些框架的自定义注解。
2. 定义注解和元注解。
我们看一下下面代码,这是注解@Override的定义。定义注解和接口的定义有点类似,只不过多了一个@符号。@Target, @Retention叫做元注解,就是用来定义注解的注解。我们来看一下各种元注解的作用。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
a.Target
用来表示你的注解用在什么地方,上面的ElementType.METHOD,表示这个注解用在方法上。除了METHOD,还有很多,看下面代码,
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, 修饰参数,比如我们Mybatis框架常用的@param
/** Constructor declaration */
CONSTRUCTOR, 修饰构造器
/** Local variable declaration */
LOCAL_VARIABLE, 修饰局部变量
/** Annotation type declaration */
ANNOTATION_TYPE, 修饰注解类型
/** Package declaration */
PACKAGE, 修饰包
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER, Java8添加的,有兴趣的可以了解一下
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE Java8添加的,有兴趣的可以了解一下
}
b.Retention
@Retention表示注解信息保留到什么时候,取值只能有一个,类型为RetentionPolicy,它是一个枚举,有三个取值:
- SOURCE:只在源代码中保留,编译器将代码编译为字节码文件后就会丢掉
- CLASS:保留到字节码文件中,但Java虚拟机将class文件加载到内存时不一定会在内存中保留
- RUNTIME:一直保留到运行时
如果没有声明@Retention,默认为CLASS。
@Override和@SuppressWarnings都是给编译器用的,所以@Retention都是RetentionPolicy.SOURCE,自定注解一般都为RUNTIME,程序运行的时候可以得到。
c.Documented - 将此注解包含在JavaDoc中
d.Inherited 允许子类继承父类中的注解
前面两个在自定义注解的时候用的比较多。
要使用注解,首先要取得注解的信息才行,看下面代码,Class中有下面这些供注解使用的方法。通过反射来获取注解信息。
//获取所有的注解
public Annotation[] getAnnotations()
//获取所有本元素上直接声明的注解,忽略inherited来的
public Annotation[] getDeclaredAnnotations()
//获取指定类型的注解,没有返回null
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
//判断是否有指定类型的注解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
我们来看一个简单的例子,看下面代码,定义了一个DefaultValue注解,通过反射得到注解信息后,就可以做自己想做的很多事了。
public class Test{
@Target(ElementType.PARAMETER) 修饰参数
@Retention(RetentionPolicy.RUNTIME) 运行时候也可以得到注解信息
static @interface DefaultValue {
String value() default "";
}
public void hello(@DefaultValue("owen") String sort){
...
}
public static void main(String[] args) throws Exception {
Class<?> cls = Test.class; 得到Class对象
Method method = cls.getMethod("hello", String.class); 得到方法hello
Annotation[][] annts = method.getParameterAnnotations(); 得到方法参数的注解
for(int i=0; i < annts.length; i++){
Annotation[] anntArr = annts[i];
for(Annotation annt : anntArr){
if(annt instanceof DefaultValue){
DefaultValue dv = (DefaultValue)annt; 得到DefaultValue注解
System.out.println(dv.annotationType().getSimpleName()+":"+ dv.value()); 打印注解的名字和设的值
}
}
}
}
}
4. 自定义注解实现依赖注入。
Spring框架中的依赖注入可以通过配置文件的方式注入,同样可以通过注解的方式注入,我们用注解来实现一个简单的依赖注入。
@Retention(RUNTIME) 运行时可以得到
@Target(FIELD) 修饰域的
public @interface autowired{
}
public class ServiceA {
@autowired 注入b
ServiceB b;
}
public class ServiceB {
}
ServiceA a = SimpleContainer.getInstance(ServiceA.class); 这个方法是关键
public static <T> T getInstance(Class<T> cls) {
try {
T obj = cls.newInstance(); 实例化对象
Field[] fields = cls.getDeclaredFields(); 取得对象的域
for (Field f : fields) {
if (f.isAnnotationPresent(autowired.class)) { 判断是否被注解修饰
if (!f.isAccessible()) { 让我们有操作这个字段的权限
f.setAccessible(true);
}
Class<?> fieldCls = f.getType(); 取得域的类型
f.set(obj, getInstance(fieldCls)); 实例化类型并注入b,这个是真正起作用的语句
}
}
return obj;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
有了注解的基础,后面我们再看一下Effective Java中注解的知识点。