Annotation基础知识
1.Annotation介绍
Annontation是Java5开始引入的新特征。中文名称一般叫注解。
Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。 Java5版本开始,自带了三种标准annontation类型,@Override、@Deprecated和@SuppressWarnings。
@Override:用于修饰此方法覆盖了父类的方法;
@Deprecated:用于修饰已经过时的方法;
@SuppressWarnnings:用于通知java编译器禁止特定的编译警告。@SuppressWarnnings与上两个注解不同的地方是,上面2个注解不需要加参数,而@SuppressWarnnings需要加上参数,例如可以在方法的前面加上
@SuppressWarnings(value={ "rawtypes", "unchecked" }),使用非泛型的容器类时将不会再出现警告信息。
除了Java自带的Annontation,我们在框架,比如Spring中会发现Annontation的大量使用,很大程度上简化了代码。
2.Annontation原理
Annontation本身不能做任何事情,需要一个类对Annontation进行处理,使用Java的反射机制来获取Annontation中的信息,再执行一些操作。
Annotation是不会影响程序代码的执行,无论annotation怎么变化,代码都始终如一地执行。
3.Annontation基本语法
了解注解的语法之前,先需要了解元注解。元注解就是可以用于注解的注解,定义了注解可修饰的对象、作用的范围等。
可以先看看 @Override注解的代码:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
Java5版本中定义的元注解包括:
@Target、@Retention、@Documented、@Inherited
@Target: 注解可修饰的对象,例如是方法还是变量。@Target的取值包括如下:
ElementType.METHOD: 可用法方法
ElementType.TYPE: 可用于类或者接口
ElementType.CONSTRUCTOR: 可以用于构造方法等
...
@Retention: 注解的作用范围:
RetentionPolicy.RUNTIME: 运行时有效
RetentionPolicy.SOURCE: 在Java类中有效
RetentionPolicy.CLASS: 在class文件中有效
@Documented: 用于使用javadoc生成的文档上将显示注解的信息
eclipse中生成javadoc的方法,选中类或者工程,右键export,选择javadoc,往下继续执行即可。
生成的javadoc中的截图如下:
@Inherited: @Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
了解了元注解之后,就可以试着自己写一个注解了。
注解的格式: public @interface 注解名 {定义体}
@interface用于声明一个注解,定义体中能出现的数据类型包括: 基本类型、String、Class、enum和以上类型的数组。
下面是一个注解和怎样处理注解的例子:
水果名称注解:
package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果名称注解 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitName { String value() default ""; }
水果颜色注解:
package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果颜色注解 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitColor { /** * 颜色枚举 * @author peida * */ public enum Color{ BULE,RED,GREEN}; /** * 颜色属性 * @return */ Color fruitColor() default Color.GREEN; }
水果供应者注解 :
package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 水果供应者注解 * */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitProvider { /** * 供应商编号 * @return */ public int id() default -1; /** * 供应商名称 * @return */ public String name() default ""; /** * 供应商地址 * @return */ public String address() default ""; }
将以上的注解用于Apple类:
public class Apple { @FruitName("Apple") private String appleName; @FruitColor(fruitColor=Color.RED) private String appleColor; @FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦") private String appleProvider; public void setAppleColor(String appleColor) { this.appleColor = appleColor; } public String getAppleColor() { return appleColor; } public void setAppleName(String appleName) { this.appleName = appleName; } public String getAppleName() { return appleName; } public void setAppleProvider(String appleProvider) { this.appleProvider = appleProvider; } public String getAppleProvider() { return appleProvider; } public void displayName(){ System.out.println("水果的名字是:苹果"); } }
写一个处理注解的类,没有该类,只单单的存在注解,是不起任何作用的:
public class FruitInfoUtil { public static void getFruitInfo(Class<?> clazz){ String strFruitName=" 水果名称:"; String strFruitColor=" 水果颜色:"; String strFruitProvicer="供应商信息:"; Field[] fields = clazz.getDeclaredFields(); for(Field field :fields){ if(field.isAnnotationPresent(FruitName.class)){ FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class); strFruitName=strFruitName+fruitName.value(); System.out.println(strFruitName); } else if(field.isAnnotationPresent(FruitColor.class)){ FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class); strFruitColor=strFruitColor+fruitColor.fruitColor().toString(); System.out.println(strFruitColor); } else if(field.isAnnotationPresent(FruitProvider.class)){ FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class); strFruitProvicer=" 供应商编号:"+fruitProvider.id()+" 供应商名称:"+fruitProvider.name()+" 供应商地址:"+fruitProvider.address(); System.out.println(strFruitProvicer); } } }
public static void main(String[] args) {
getFruitInfo(Apple.class);
}
}
输出结果为;
水果名称:Apple
水果颜色:RED
供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦
注解的处理通常有两种方式:在编译时、或在运行时。
Java5使用的apt工具(JDK的bin目录下由apt.exe)和Oracle私有的Mirror API工具已经在Java7中被声明为废弃。
Java6引入了Pluggable Annotaion Processing API,对注解处理机制进行了标准化。
a、分析注解,比如统计一个有多少个类加载了某个注解。(编译时)
b、根据注解,创建和修改源代码(运行时)
c、使用反射处理注解(运行时)
4.和反射、动态代理结合使用
应用场景:把注解、反射和动态代理结合起来。注解用来设置运行时的行为,反射API用来解析注解,动态带来负责应用的具体行为。
例子:模拟权限管理
Role注解:
package annotation.withreflect; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Role{ String[] value(); }
使用JDK的代理,需要一个接口:
package annotation.withreflect; public interface EmployeeInfoManager{ @Role("manager") public void updateSalary(); }
接口的具体实现:
package annotation.withreflect; public class DefaultEmployeeInfoManager implements EmployeeInfoManager{ @Override public void updateSalary() { System.out.println("manager update Salary!"); } }
写一个模拟获取角色的类,将随机获取到manager和employee角色:
package annotation.withreflect; import java.util.Random; public class AccessManager { public static String getCurrentUserRole(){ String role = "manager"; Random rd = new Random(); int i = rd.nextInt(46); System.out.println("random int: " + i); if(i%2==0){ role = "employee"; } return role; } }
使用反射、动态代理来处理注解,只有manager角色可以调用updateSalary方法,:
package annotation.withreflect; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; public class EmployeeInfoManagerProxy implements InvocationHandler { private final Object targetObject; public EmployeeInfoManagerProxy(Object targetObject) { this.targetObject = targetObject; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Role annotation = method.getAnnotation(Role.class); if (annotation != null) { String[] roles = annotation.value(); String currentRole = AccessManager.getCurrentUserRole(); if (!Arrays.asList(roles).contains(currentRole)) { System.out.println("没有调用此方法的权限。"); return null; } } return method.invoke(targetObject, args); } public EmployeeInfoManager getManager() { EmployeeInfoManager instance = new DefaultEmployeeInfoManager(); return (EmployeeInfoManager) Proxy.newProxyInstance(instance.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this); } }
写一个测试类:
package annotation.withreflect; public class ReflectTest { public static void main(String[] args) { EmployeeInfoManagerProxy ef = new EmployeeInfoManagerProxy(new DefaultEmployeeInfoManager()); EmployeeInfoManager em = ef.getManager(); em.updateSalary(); } }
运行结果为:
random int: 4 没有调用此方法的权限。 ///或者如下: random int: 23 manager update Salary!