zoukankan      html  css  js  c++  java
  • Annotation基础知识

     1.Annotation介绍  

      AnnontationJava5开始引入的新特征。中文名称一般叫注解。

      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!
  • 相关阅读:
    SQL 三种Join关联示例
    EntityFramework 更新 整个记录全部字段不确定字段
    C# 生成Excel 报错COMException(0x800A03EC)
    EntityFramework4 几种报错解决方法
    Machine learning lecture2 note
    谷歌360度街景拍摄车对日本核电站污染隔离区进行拍摄
    android电池电量状态代码
    唠骚 2013年终奖
    互联网手机的围城:小米顶多再风光一年(转载)
    AIX下c3p0连接池问题
  • 原文地址:https://www.cnblogs.com/lnlvinso/p/4521170.html
Copyright © 2011-2022 走看看