zoukankan      html  css  js  c++  java
  • java 注解详解

    先引用一下百度百科的名词解析:

    定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
    作用分类:
    ①编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
    ② 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
    ③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】

    下面开始正文:

    概述:

    就如上面所说,注解有几种用途,第一种是编写文档,这种注解会在javadoc中存在;

    第二种是代码分析,这需要使用 反射 的api去解析注解;

    第三种类是提供编译时的检查,如 deprecated 这些,编译的时候会产生警告信息。

    举几个例子:

    1、下面的例子,注解使用了@Documented注解,这意味着,我们的注解会在 javadoc 生成的文档中出现,另外一个@Retention的用途下面会说到

    package com.ruby;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Documented
    @Retention(RetentionPolicy.SOURCE)
    public @interface ClassPreamble {
        String author();
    }
    

      使用:

    @ClassPreamble(
            author = "ruby"
    )
    public class Main ...
    

      

    2、代码分析(我们拙劣地模拟一下Spring Boot的路由注解)

    代码目录结构:

    RequestMapping注解定义:

    package com.ruby.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface RequestMapping {
        String value() default "/";
    }
    

      

    Home控制器定义:

    package com.ruby.controller;
    
    import com.ruby.annotation.RequestMapping;
    
    public class Home {
        @RequestMapping("test")
        public void HelloWorld() {
            System.out.println("HelloWorld !");
        }
    }
    

    使用方法(命名不是太规范,test一般用于测试文件命名):关键是main方法里面通过反射去解析注解的代码,其他两个方法只是工具方法(用以获取包下面所有类)

    package com.ruby.test;
    
    import com.ruby.annotation.RequestMapping;
    
    import java.io.File;
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.Enumeration;
    import java.util.List;
    
    public class TestHome {
        public static void main(String[] args) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException, InvocationTargetException{
            String route = "test";
    
           Class[] classes = getClasses("com.ruby.controller");
           for (Class cls : classes) {
               Method[] methods = cls.getMethods();
               for (Method method : methods) {
                   RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                   if (requestMapping != null) {
                       String mapping = requestMapping.value();
                       if (mapping.equals(route)) {
                           method.invoke(cls.newInstance());
                       }
                   }
               }
           }
        }
    
        /**
         * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
         *
         * @param packageName The base package
         * @return The classes
         * @throws ClassNotFoundException
         * @throws IOException
         */
        private static Class[] getClasses(String packageName)
                throws ClassNotFoundException, IOException {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            assert classLoader != null;
            String path = packageName.replace('.', '/');
            Enumeration<URL> resources = classLoader.getResources(path);
            List<File> dirs = new ArrayList<File>();
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                dirs.add(new File(resource.getFile()));
            }
            ArrayList<Class> classes = new ArrayList<Class>();
            for (File directory : dirs) {
                classes.addAll(findClasses(directory, packageName));
            }
            return classes.toArray(new Class[classes.size()]);
        }
    
        /**
         * Recursive method used to find all classes in a given directory and subdirs.
         *
         * @param directory   The base directory
         * @param packageName The package name for classes found inside the base directory
         * @return The classes
         * @throws ClassNotFoundException
         */
        private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
            List<Class> classes = new ArrayList<Class>();
            if (!directory.exists()) {
                return classes;
            }
            File[] files = directory.listFiles();
            for (File file : files) {
                if (file.isDirectory()) {
                    assert !file.getName().contains(".");
                    classes.addAll(findClasses(file, packageName + "." + file.getName()));
                } else if (file.getName().endsWith(".class")) {
                    classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
                }
            }
            return classes;
        }
    }
    

      

    3、编译时检查:如@SuppressWarnings,如有些废弃的方法调用之后会有警告信息,你可以使用@SuppressWarnings注解:@SuppressWarnings("deprecation"),这样编译的时候就不会产生警告信息了。

    注解定义

    注解定义的方法类似接口定义(上面有例子了),只是在interface关键字前面多了个@符号,注解定义上面也还可以有其他注解(下面详说),注解体里面的元素基本格式都一致,类型名称 + 变量名 + 括号 + default xxx; 默认值的提供是可选的。类型可以是普通类型,也可以是类啊、异常啊这些。

    接下来要说的发图吧,比较清晰:

    注解分类:

    1、普通注解,如:@Deprecated、@Override、@SuppressWarnings、@SafeVarargs等

    2、注解其他注解的注解(比较拗口):记重点了,比较关键的是@Retention、@Target

    别的不说,先发图,看图比较清晰:

    先说@Retention,这个很重要,如果我们要在运行时使用定义的注解,一定不要写错了,默认是不会到运行时还存在的

    使用方法:在注解定义处使用:

    @Retention(RetentionPolicy.SOURCE)
    public @interface ClassPreamble {
        String author();
    }

    有三个可选的选项:(默认是RetentionPolicy.CLASS)

    1、RetentionPolicy.SOURCE,如果使用该选项,我们的注解只会在源代码存在,编译之后就没有了,我们去看编译之后的 class 文件,会发现注解已经不存在了。但是如果我们不写@Retention 的话,编译后的 class 文件还会有该注解,因为默认是RetentionPolicy.CLASS。

    2、RetentionPolicy.CLASS(默认),使用该选项,我们的注解会在源代码和class文件中存在

    3、RetentionPolicy.RUNTIME,使用该选项,我们的注解会一直存在,我们可以在运行的时候去获取注解信息,然后做相应处理。

    @Document 这个注解会在 javadoc 生成的文档中出现

    @Target,这也算是一个比较重要的注解,指明了该注解可以用在什么地方,好比如,你指定了一个注解的Target为ElementType.METHOD,那么这个注解只能用在方法上,而不能用在field或者type上,当然你也可以同时指定多个,如:

    @Target({ElementType.FIELD, ElementType.METHOD})
    

     可用类型如上图。

    @Repeatable 指明该注解是否可以多次使用

    好了,就这么多了,其他还不是太了解,上面只是看文档总结的东西,

    其实最重要的还是@Retention,如果要在运行时也用到注解信息,那么必须指定  Retention.RUNTIME 参数,否则通过反射去获取注解是获取不到的。

    与此相关的另外的知识就是反射了,有空再补充。

  • 相关阅读:
    今天更新Chrome到1.0版本
    升级安装Windows7后需要处理的兼容性程序(不断更新中)
    解决IE8下无法播放rm的视频网站内容
    升级到NVelocity1.1版本
    安装VS2010中文版初体验
    【iPhone开发】说说Xcode4中xib绑定的原理
    第一个iPhone版本应用发布
    xcode4.2开发最新的ios5应用,分享snow版本下载地址
    xCode 4.X 免证书真机发布及调试
    [转]IT程序员之间薪水差距之大及对职业培训的看法
  • 原文地址:https://www.cnblogs.com/eleven24/p/8278441.html
Copyright © 2011-2022 走看看