zoukankan      html  css  js  c++  java
  • 学习笔记_反射&&注解

    反射&注解

    反射

    Java程序的三个阶段,source、class、runTime

    对应三种获取核心类class的方法

    1. 通过.class属性
    2. 通过对象引用的getClass()方法
    3. 通过Class.forName(String className)方法

    获取Constructor对象

    package com.liuxingwu.codes.constructor;
    
    import java.util.Date;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 16:34
     */
    public class Person {
        public static final int ID = 1;     // 静态变量
        static {
            System.out.println("Person prepare!");      // 静态方法
        }
        public Person() {};
        public Person(String name) {};
        public Person(String name, int age) {};
        protected Person(boolean sex) {};
        private Person(Date birthday) {};
    }
    
    
    package com.liuxingwu.codes.constructor;
    
    import java.lang.reflect.Constructor;
    
    /**
     * 当访问常量静态变量属性时,JVM加载类的过程不会进行类的初始化工;
     * 虽然构造方法没有被显示地声明为静态方法,但它仍作为类的静态成员特例,因此,当使用new关键词来构造一个对象时
     * 也会被当做静态类成员的引用,同样会触发JVM来加载这个类。
     * @author LiuXingWu
     * @create 2021-02-10 16:35
     */
    public class BootStrap {
        public static void main(String[] args) throws NoSuchMethodException {
    //        System.out.println("Use static field!");
    //        System.out.println(Person.ID);
    //        System.out.println("new a instance!");
    //        new Person();
    
    //        获取类的构造方法
            Class clazz = Person.class;
    //        getConstructor(Class parameterType···)获取指定参数类型的Constructor对象
    //        Constructor constructor = clazz.getConstructor();
    //        System.out.println(constructor.toString());
    //
    //        Constructor constructor1 = clazz.getConstructor(String.class);
    //        System.out.println(constructor1.toString());
    //
    //        Constructor constructor2 = clazz.getConstructor(String.class, int.class);
    //        System.out.println(constructor2.toString());
    
    //        getConstructors()获取指定类的公有构造函数描述对象Constructor列表,如果指定类没有公有的构造函数,
    //        则返回一个长度为0的Constructor数组
    //        Constructor[] constructors = clazz.getConstructors();
    //        for (Constructor c : constructors) {
    //            System.out.println(c.toString());
    //        }
    
    //        getDeclaredConstructor(Class···parameterTypes, 获取指定参数类型的构造函数描述性对象,包括公有、受保护、私有的构造函数
    //        Constructor declaredConstructor = clazz.getDeclaredConstructor();
    //        System.out.println(declaredConstructor.toString());
    //
    //        Constructor declaredConstructor1 = clazz.getDeclaredConstructor(String.class);
    //        System.out.println(declaredConstructor1.toString());
    //
    //        Constructor declaredConstructor2 = clazz.getDeclaredConstructor(String.class, int.class);
    //        System.out.println(declaredConstructor2.toString());
    //
    //        Constructor declaredConstructor3 = clazz.getDeclaredConstructor(boolean.class);
    //        System.out.println(declaredConstructor3.toString());
    //
    //        Constructor declaredConstructor4 = clazz.getDeclaredConstructor(Date.class);
    //        System.out.println(declaredConstructor4.toString());
    
            //        getDeclaredConstructors()获取指定类的公有构造函数描述对象Constructor列表,如果指定类没有公有的构造函数,
    //        则返回一个长度为0的Constructor数组
            Constructor[] constructors = clazz.getDeclaredConstructors();
            for (Constructor c : constructors) {
                System.out.println(c.toString());
            }
        }
    }
    
    

    获取Method对象

    package com.liuxingwu.codes.methods;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 17:01
     */
    public interface Speakable {
        public void speak();
        public void speak(String message);
    }
    
    package com.liuxingwu.codes.methods;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 17:01
     */
    public abstract class Person implements Speakable {
        protected void useTool() {};
        private void useTool(String toolName) {};
        public void eat(String food) {};
        public static void listen() {};
        protected abstract void listen(String destination);
    //    final 方法:将方法声明为final有两个原因,第一是说明你已经知道这个方法提供的功能已经满足要求,不需要再进行拓展,
    //    并且也不允许任何从此类继承的类来腹泻这个方法,但仍然可以继承这个方法,也就是说可以直接只用。
    //    第二是允许编译器将所有对此方法的调用转化为inline(行内)调用机制,它会使你在调用final方法时,直接将方法主体插入到调用处
    //    ,而不是进行例行的方法调用,例如保存端点、压栈等,这样可能会使程序效率有所提高,然而当方法主体非常庞大时,或在多处调用此
    //    方法,那么调用主题代码便会迅速膨胀,反而会影响效率,所以要慎用final进行方法定义
        public final void fly() {};
    
    //    简单地讲,一个Native方法就是一个Java调用非Java代码的接口。该方法的实现有非Java语言实现,例如C。这个特征并非Java所特有,
    //    很多其他编程语言也有这一机制。在定义一个Native方法是,并不提供实现体(类似定义一个Java interface),因为其实现体是由非
    //    非Java语言在外面实现的。
        public native void think();
    }
    
    package com.liuxingwu.codes.methods;
    
    import java.lang.reflect.Method;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 17:14
     */
    public class BootStrap {
        public static void main(String[] args) throws NoSuchMethodException {
            Class<Person> Clazz = Person.class;
    
    //        getMethos(String name, Class···parameterType)此方法用于获取指定名称和参数类型的公有方法描述对象。
    //        除了获取本身定义的方法外,还包含继承于父类的方法
    //        Method speak = Clazz.getMethod("speak");
    //        System.out.println(speak.toString());
    //
    //        Method eat = Clazz.getMethod("eat", String.class);
    //        System.out.println(eat.toString());
    //
    //        Method listen = Clazz.getMethod("listen");
    //        System.out.println(listen.toString());
    //
    //        Method fly = Clazz.getMethod("fly");
    //        System.out.println(fly.toString());
    //
    //        Method think = Clazz.getMethod("think");
    //        System.out.println(think.toString());
    //
    //        System.out.println("------------------------------------------------------");
    //
    //        Method[] methods = Clazz.getMethods();
    //        for (Method m : methods) {
    //            System.out.println(m.toString());
    //        }
    
            Method useTool = Clazz.getDeclaredMethod("useTool");
            System.out.println(useTool.toString());
            Method useTool1 = Clazz.getDeclaredMethod("useTool", String.class);
            System.out.println(useTool1.toString());
    
            System.out.println("------------------------------------------------------");
    
            Method[] methods = Clazz.getDeclaredMethods();
            for (Method m : methods) {
                System.out.println(m.toString());
            }
    
        }
    }
    
    
    

    获取Field对象

    package com.liuxingwu.codes.field;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 17:43
     */
    public class Person {
        public String name;
        protected boolean sex;
        private int age;
    }
    package com.liuxingwu.codes.field;
    
    import java.lang.reflect.Field;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 17:43
     */
    public class BootStrap {
        public static void main(String[] args) throws NoSuchFieldException {
    //        getField(String name) 此方法获取指定名称的Field对象,此属性必须在类内部已经定义而且必须是公有的
            Class clazz = Person.class;
            Field name = clazz.getField("name");
            System.out.println(name.toString());
    
            System.out.println("---------------------------------------------------");
    
            Field[] fields = clazz.getFields();
            for (Field f : fields) {
                System.out.println(f.toString());
            }
    
            System.out.println("---------------------------------------------------");
            Field name1 = clazz.getDeclaredField("name");
            System.out.println(name1.toString());
            Field sex = clazz.getDeclaredField("sex");
            System.out.println(sex.toString());
            Field age = clazz.getDeclaredField("age");
            System.out.println(age.toString());
    
            System.out.println("---------------------------------------------------");
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field f : declaredFields) {
                System.out.println(f.toString());
            }
    
        }
    }
    
    

    反射示例

    package com.liuxingwu.codes.reflection;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 17:52
     */
    public abstract class Person {
        public abstract String toString();
    }
    package com.liuxingwu.codes.reflection;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 17:53
     */
    public class Teacher extends Person {
        public String position;
        private  int salary;
        public void speak(String message) {
            System.out.println("Speak: " + message);
        }
        @Override
        public String toString() {
            return "[Position: " + position + ", salary: " + salary + "]";
        }
    
        private int getSalary() {
            return this.salary;
        }
    }
    package com.liuxingwu.codes.reflection;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 17:56
     */
    public class BootStrap {
        public static String className = "com.liuxingwu.codes.reflection.Teacher";
        public static void main(String[] args) {
            try {
                // 1. 显示加载指定类
                System.out.println("开始加载类!");
                Class clazz = Class.forName(className);
                System.out.println(clazz.toString());
                System.out.println("类加载完毕!");
    
                // 2. 通过反射实例化类
                System.out.println("通过反射实例化类");
                Person person = (Person) clazz.newInstance();
                System.out.println(person.toString());
    
                Constructor constructor = clazz.getConstructor();
                Person teacher = (Person) constructor.newInstance();
                System.out.println(teacher.toString());
    
                System.out.println("通过反射执行方法");
                Method speak = clazz.getMethod("speak", String.class);      // 获取指定类指定方法描述对象
                speak.invoke(teacher, "Lession one!");      // 通过invoke放来来执行对象teacher的speak方法
                // invoke传入一个实例对象和一个字符串作为参数,
    
                // 4. 通过反射修改属性
                System.out.println("通过反射修改属性");
                Field field = clazz.getField("position");
                System.out.println(teacher.toString());
                field.set(teacher, "Master");
                System.out.println(teacher.toString());
    
                // 5. 修改访问权限
                System.out.println("修改访问权限");
                Field declaredField = clazz.getDeclaredField("salary");
                declaredField.setAccessible(true);
                declaredField.set(teacher, 5000);
                System.out.println(teacher.toString());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    }
    
    

    IOC示例

    image-20210118130602158

    package com.liuxingwu.codes.ioc;
    
    /**
     * IOC容器的顶层接口
     * @author LiuXingWu
     * @create 2021-02-10 19:10
     */
    public interface BeanFactory {
        /**
         * 根据对象的名称标识来获取对象实例
         * @param name 对象名称,及对象描述信息中的对象标识
         * @return 指定名称的对象实例
         */
        public Object getBean(String name);
    }
    
    
    package com.liuxingwu.codes.ioc;
    
    import java.util.Map;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 19:16
     */
    public interface SourceReader {
        /**
         * 读取用户注册的对象信息
         * @param filePath 读取路径
         * @return 注册对象信息Map
         */
        Map<String, BeanInfo> loadBeans(String filePath);
    }
    
    
    package com.liuxingwu.codes.ioc;
    
    import sun.plugin.com.BeanCustomizer;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * XML注册读取器
     * 该类继承了注册读取器接口,并模拟实现了读取注册对象信息的方法
     * @author LiuXingWu
     * @create 2021-02-10 19:44
     */
    public class XMLSourceReader implements SourceReader {
        @Override
        public Map<String, BeanInfo> loadBeans(String filePath) {
            // 初始化一个对象信息
            BeanInfo beanInfo = new BeanInfo();
            beanInfo.setId("Person");
            beanInfo.setType("com.liuxingwu.codes.ioc.Person");
            beanInfo.addPropeity("name", "Tim");
            Map<String, BeanInfo> beans = new HashMap<String, BeanInfo>();      // 初始化一个对象信息Map
            beans.put("Person", beanInfo);      // 将对象信息注册到对象信息Map中
            return beans;
        }
    }
    
    
    package com.liuxingwu.codes.ioc;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 该类用于描述注册在容器中的对象
     * @author LiuXingWu
     * @create 2021-02-10 19:05
     */
    public class BeanInfo {
        private String id;      // 对象标识
        private String type;        // 对象的类型,即全类名
        private Map<String, Object> properties = new HashMap<String, Object>();     // 对象的属性及值的集合
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public Map<String, Object> getProperties() {
            return properties;
        }
    
        public void setProperties(Map<String, Object> properties) {
            this.properties = properties;
        }
    
        public void addPropeity(String name, Object value) {
            this.properties.put(name, value);
        }
    }
    
    
    package com.liuxingwu.codes.ioc;
    
    import java.lang.reflect.Method;
    import java.util.Map;
    
    /**
     * 最顶层的IOC实现
     * 该类负责从注册器中去除注册对象
     * 实现从对象描述信息转换对象实例的过程
     * 实现根据名称获取对象的方法
     * @author LiuXingWu
     * @create 2021-02-10 19:12
     */
    public abstract class AbstractBeanFactory implements BeanFactory {
        private String filePath;        // 注册文件路径
        private Map<String, BeanInfo> container;        // 注册对象信息Map
        protected SourceReader reader;        // 注册对象读取器
    
        public AbstractBeanFactory(String filePath) {
            this.filePath = filePath;
        }
    
        /**
         * 该方法为抽象方法,需由子类实现,用于指定使用什么样的注册读取器
         * @param reader
         */
        protected abstract void setSourceReader(SourceReader reader);
    
        /**
         * 从注册读取器中读取注册对象的信息Map
         */
        public void registerBeans() {
            this.container = this.reader.loadBeans(filePath);
        }
    
        // 实现BeanFactory定义的根据名称获取指定对象的方法
        @Override
        public Object getBean(String name) {
            // 根据对象名称获取该对象的描述信息
            BeanInfo beanInfo = this.container.get(name);
            if (beanInfo == null) {
                // 如果容器中不存在该对象的描述信息,则返回null,此处可以抛开一个异常
                return null;
            } else {
                // 根据对象的信息,解析并生成指定对象实例,返回给用户
                return this.parseBean(beanInfo);
            }
        }
    
        /**
         * 解析并生成对象实例
         * 该方法主要通过反射完成,步骤如下:
         * 1. 根据类名,加载指定类,并获取该类的Class对象clazz
         * 2. 使用Class对象clazz实例化该类,获取一个对象,注意,这里实例化对象是,采用的是
         * 无参构造方法,因此要求注册的对象必须含有无参构造方法
         * 3. 逐个设置对象字段的值,这里采用setter Method方式,而不是直接使用Field对象的
         * 原因是:用户有可能在setter对象中,对注入的值进行额外处理,如格式化等
         * 4. 返回对象实例
         * @param beanInfo 指定对象的描述信息
         * @return
         */
        protected Object parseBean(BeanInfo beanInfo) {
            Class clazz;
            try {
                clazz = Class.forName(beanInfo.getType());      // 根据对象的全类名,指定类
                Object bean = clazz.newInstance();      // 使用注册对象的无参构造函数,实例化对象实例
                Method[] methods = clazz.getMethods();      // 获取该类声明的所有公共方法,气质Spring获取的是所有方法,包括非公有的
                for (String property : beanInfo.getProperties().keySet()) {
                    // 遍历对象的所有属性,进行赋值
                    String setter = "set" + firstCharToUp(property);     // 获取属性的setter方法名称
    
    //                if (setter.length() != 0) {
    //                    System.out.println(setter);
    //                }
    
                    for (Method m : methods) {
                        // 遍历该类的所有公有方法
                        String methodName = m.getName();        // 获取方法名称
                        if (methodName.equals(setter)) {
                            // 比较方法名与属性的setter方法名是否相同,如果相同则进行赋值
                            Object value = beanInfo.getProperties().get(property);      // 从对象描述信息中获取该属性的值
                            m.invoke(bean, value);      // 通过反射对属性进行赋值
                            continue;       // 对下一个属性赋值
                        }
                    }
                }
                return bean;
            } catch (Exception e) {
                    e.printStackTrace();
            }
            return null;
        }
    
        private String firstCharToUp(String property) {
            String temp = property.substring(0, 1);
            return property.replaceFirst(temp, temp.toUpperCase());
        }
    
    }
    
    
    package com.liuxingwu.codes.ioc;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 19:45
     */
    public class XMLContext extends AbstractBeanFactory{
        /**
         * 上下文的构造方法
         * 该方法中指明注册读取器
         * 并在构造该方法时一次性加载注册的对象
         * @param filePath
         */
        public XMLContext(String filePath) {
            super(filePath);
            this.setSourceReader(new XMLSourceReader());        // 添加注册读取器
            this.registerBeans();       // 加载注册对象信息
        }
        // 设置注册读取器
        @Override
        protected void setSourceReader(SourceReader reader) {
            this.reader = reader;
        }
    }
    
    

    测试

    package com.liuxingwu.codes.ioc;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 19:56
     */
    public interface Speakable {
        public void speak(String message);
    }
    
    package com.liuxingwu.codes.ioc;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 19:56
     */
    public class Person implements Speakable {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public void speak(String message) {
            System.out.println(this.name + " say: " + message);
        }
    }
    
    package com.liuxingwu.codes.ioc;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 19:58
     */
    public class BootStrap {
        public static void main(String[] args) {
            BeanFactory factory = new XMLContext("beans.xml");
            Speakable s = (Speakable)factory.getBean("Person");
            s.speak("Lession one!");
        }
    }
    
    

    注解

    Java常用注解

    1. @Override

      表示当前定义的方法将覆盖在父类的同名、同参数方法,如果定义的方法名在父类中找不到,编译器将会提示must override or implement a supertype method错误。

    2. @SuperessWarnings

      关闭无需关心的警告信息,该注解可用于这个类上,也可以用于方法上。该注解只在JDK5之后的版本中才起作用,之前的版本也可以使用该注解,但是不起作用。

    3. @Deprecated

      使用该注解声明方法或类已过时,不鼓励使用这样的方法或类,因为这可能存在风险或有更好的选择。

    package com.liuxingwu.codes.example2;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 21:43
     */
    //    @Override 表示当前定义的方法将覆盖父类的同名】同参数的方法,如果定义的方法
    //    在父类中找不到,编译器将会提示 must override or implement a supertype method错误
    
    //    @SuppressWarnings 关闭无需关心的警告信息,该注解可用于整个类上,也可以用于方法上。该注解
    //    只在JDK5之后的版本中才起作用,之前的版本也可以使用该注解,但是不起作用
    
    //    @Deprecated 使用该注解用于说明方法或类已经过时,不鼓励使用这样的方法或类,
    //    因为这可能存在风险或者有更好的选择。
    //作用在类上的注解一般用于声明和描述类的性质,将作用域整个类
    @Deprecated
    public abstract class Person {
    //    在属性上注解
        @Deprecated     // 说明该属性已经过时,如果使用了该属性,编译器将会抛出警告信息。
    //    注解中设置参数,用于表示某些特定的操作
        @SuppressWarnings(value = "unused")     // 说明了使用参数value, 并为其复制"unused",表明这个属性无需爆出未使用警告
        private String name;
    
    //  作用在方法上的注解,只对目标方法起作用
    
    
        @Deprecated
        public void speak(String message) {
            @SuppressWarnings({"unchecked", "unused"})      // 表明局部变量无需进行抛出检测警告和未使用警告,其实这里的注解也设置了参数
    //                但是没有显示地指明为哪个变量设定参数。
            List list = new ArrayList();
            System.out.println("Speak: " + message);
        }
    
    
        @Override
        public String toString() {
            return "This is a person!";
        }
    }
    
    

    自定义注解

    元注解

    1. @Target:表明了自定义注解的作用域。可能的作用域被定义在一个枚举类型中:ElementType。具体如下
      1. ElementType.ANNOTATION_TYPE:作用在注解类型上的注解
      2. EementType.CONSTRUCTOR:作用在构造方法上的注解
      3. EementType.FIELD:作用在属性上的注解
      4. EementType.LOCAL_VARIABLE:作用在本地变量上的注解
      5. EementType.METHOD:作用在方法上的注解
      6. EementType.PACKAGE:作用在包上的注解
      7. EementType.PRARMETER:作用在参数上的注解
      8. EementType.Type:作用在类、接口或枚举上的注解
    2. @Retention:用于声明注解信息的保留策略,可选的级别被存放在枚举RetentionPolicy中。具体如下
      1. RetentionPolicy.SOURCE:注解信息仅保留在源文件中,编译时将丢弃注解信息
      2. RetentionPolicy.CLASS:注解信息将被编译进Class文件中,但这些注解信息在运行时将丢弃
      3. RetentionPolicy.RUNTIME:注解信息将被保留到运行时,可以通过反射来读取这些注解信息
    3. @Documented:表明制作Javadoc时,是否将注解信息加入文档。如果注解在声明时使用的该注解声明,则在制作Javadoc时注解信息会加入文档中
    4. @Inherited:表明注解是否会被子类继承,默认情况是不继承的。当注解在声明时,使用了该注解声明,则该注解会被使用了该注解的类的子类所继承

    定义注解

    package com.liuxingwu.codes.example5;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-10 22:08
     */
    @Target(ElementType.TYPE)       // 表明作用在类、接口或枚举上
    @Retention(RetentionPolicy.RUNTIME)     // 该保留策略表明注解@Entity的信息将保留到运行时
    public @interface Entity {
    //    注解中定义了两个参数
        public int type() default - 1;      // 默认值是-1
        public String name();       // 没有指明默认值和默认参数,在使用时必须指明具体的值
    }
    
    package com.liuxingwu.codes.example5;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 元注解的使用
     * @author LiuXingWu
     * @create 2021-02-10 22:03
     */
    @Target(ElementType.METHOD)     // 作用在方法上的注解
    @Retention(RetentionPolicy.RUNTIME)     // 注解信息将保被保留到运行时,可以通过反射来读取这些注解信息
    public @interface Test {
    //    @Test 类似一个没有方法的接口
    }
    
    package com.liuxingwu.codes.example5;
    
    /**
     * 自定义注解的使用
     * @author LiuXingWu
     * @create 2021-02-10 22:23
     */
    @Entity(name = "Person")
    public class Person {
        @Test
        public void speak(String message) {}
    }
    
    

    注解参数说明

    注解参数类型:所有的基本类型(int, float, boolean), String, Class, enum, Annotation,以及以上类型的数组。

    注解可嵌套:

    package com.liuxingwu.codes.example8;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 注解的嵌套
     * @author LiuXingWu
     * @create 2021-02-10 22:27
     */
    @Target(ElementType.FIELD)      //
    @Retention(RetentionPolicy.RUNTIME)     //
    public @interface ID {
        public String value() default "id";
    }
    
    package com.liuxingwu.codes.example8;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 注解的嵌套
     * @author LiuXingWu
     * @create 2021-02-10 22:08
     */
    @Target(ElementType.TYPE)       // 表明作用在类、接口或枚举上
    @Retention(RetentionPolicy.RUNTIME)     // 该保留策略表明注解@Entity的信息将保留到运行时
    public @interface Entity {
    //    注解中定义了两个参数
        public String name() default "";
        public ID id() default @ID("id");
    }
    
    

    注解处理器

    package com.liuxingwu.codes.example10;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-11 9:20
     */
    @Target(ElementType.TYPE)       // 注解是作用在类上的,所以该参数值是ElementType.TYPE
    @Retention(RetentionPolicy.RUNTIME)     // 因为需要使用反射方法读取其信息,所以保留策略为运行时
    public @interface ExtractInterface {
        public String value();
    }
    
    
    package com.liuxingwu.codes.example10;
    
    /**
     * 注解处理器接口,只提供一个方法给外部使用
     * @author LiuXingWu
     * @create 2021-02-11 9:23
     */
    public interface AnnotationProcessor {
        public boolean process(Class <?> clazz) throws Exception;
    }
    
    
    

    方法实现逻辑如下:

    package com.liuxingwu.codes.example10;
    
    import java.io.*;
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-11 9:26
     */
    public class ExtractProcessor implements AnnotationProcessor {
        /**
         * 该方法是接口中process方法的具体实现,是暴露在外部的接口,共外部调用。
         * 它的功能是使用其他的工具方法分析并处理注解,完成指定的工作
         * 处理流程如下:
         * 1. 获取@ExtractInterface注解
         * 2. 如果注解该类拥有指定注解,则创建一个StringBuilder用于临时存放生成代码
         * 3. 在StringBuilder中添加包信息
         * 4. 在StringBuilder中添加接口信息
         * 5. 遍历指定类声明的方法集合,并在StringBuilder中加入公共方法信息
         * 6. 生成接口文件
         * @param clazz
         * @return
         * @throws Exception
         */
        public boolean process(Class<?>clazz) throws Exception {
            ExtractInterface ann = this.getExtractInterface(clazz);     // 获取指定的注解接口
    
            if (ann != null) {
                StringBuilder sb = new StringBuilder();
                this.addPackage(sb, clazz);
                this.addInterface(sb, ann);
                Method[] methods = clazz.getMethods();
                // 遍历指定类声明的方法
                for (Method method : methods) {
                    // 如果获取的方法是公共方法
                    if (method.getModifiers() == Modifier.PUBLIC) {
                        sb = this.addMethod(sb, method);        // 添加方法信息
                    }
                }
                sb.append("}");
                this.createFile(sb, clazz, ann);        // 创建接口文件
                return true;
            }
            return false;
        }
    
        /**
         * 该方法获取指定类上的@ExtractInterface,如果指定类上声明了该注解,则返回该注解实例,否则返回null。
         * 具体实现如下:
         * 1. 遍历该类上的所有注解
         * 2. 如果注解的类型为@ExtractInterface,则返回该注解,否则返回null
         * @param clazz 指定类的class对象
         * @return 类上声明的@ExtractInterface注解
         */
        private ExtractInterface getExtractInterface(Class<?>clazz) {
            Annotation[] annotations = clazz.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation.annotationType() == ExtractInterface.class) {
                    return (ExtractInterface)annotation;
                }
            }
            return null;
        }
    
        /**
         * 该方法用于在生成代码临时存储器中添加包信息,包的信息包括关键字package和包的名称,还需要一个";"结尾
         * @param sb
         * @param clazz
         * @return
         */
        private StringBuilder addPackage(StringBuilder sb, Class<?>clazz) {
            sb.append("package ");
            sb.append(clazz.getPackage().getName());
            sb.append(";");
            sb.append("
    ");        // 换行
            return sb;
        }
    
        /**
         * 该方法用于在代码临时存储器中添加接口信息,包括接口的修饰符,接口的关键字interface以及接口的名称,接口的名称由用户指定,存储在注解中
         * @param sb 临时代码存储器
         * @param anno 注解实例
         * @return
         */
        private StringBuilder addInterface(StringBuilder sb, ExtractInterface anno) {
            sb.append("public interface ");     // 本例中的接口修饰符全为public
            sb.append(anno.value());            // 根据@ExtractInterface的value参数来确定接口的名称
            sb.append("{");
            sb.append("
    ");
            return sb;
        }
    
        /**
         * 该方法的功能是向临时代码存储器中添加抽象方法。方法的信息一般包括方法修饰符、返回值、方法名、参数类型、参数等。
         * 具体实现如下:
         * 1. 添加修饰符
         * 2. 添加返回值类型
         * 3. 添加方法名
         * 4. 遍历参数类型,并向StringBuilder中添加参数
         * @param sb 临时代码存储器
         * @param method 方法对象
         * @return
         */
        private StringBuilder addMethod(StringBuilder sb, Method method) {
            sb.append("	 public ");
            sb.append(method.getReturnType().getCanonicalName() + " ");     // 添加返回值类型
            sb.append(method.getName() + " ");      // 添加方法名
            sb.append("(");
            Class<?>[] paras = method.getParameterTypes();      // 获取参数类型集合
            String arg = "arg";     // 参数名的前半部分
            Integer argIndex = 0;       // 参数索引
            // 遍历方法的参数类型
            for (Class<?>para : paras) {
                sb.append(para.getCanonicalName() + " ");       // 添加参数类型
                sb.append(arg + argIndex);      // 添加参数名称,由arg + 索引组成
                sb.append(", ");
                argIndex++;
            }
            if (argIndex > 0) {
                sb = new StringBuilder(sb.substring(0, sb.length() - 2));
            }
            sb.append(")");
            sb.append(";");
            sb.append("
    ");
            return sb;
        }
    
        /**
         * 创建接口文件,并向接口文件填充接口信息
         * @param sb
         * @param clazz
         * @param ext
         * @return
         */
        private void createFile(StringBuilder sb, Class<?>clazz, ExtractInterface ext) {
            String path = clazz.getPackage().getName();
            path = path.replace(".", "\");
            // 文件的路径可自定义,这里指定和类文件相同目录
            String url = System.getProperty("user.dir")     // 程序所在的根路径
                                 + "\src\main\Java\"        // 在程序内的相对路径,可能因为不同的项目结构这部分的内容会不同
                                 + path         // 包路径(包名)
                                 + "\" + ext.value() + ".java";        // 生成的接口名
            FileOutputStream fileWriter = null;
            try {
                File folder = new File(url.substring(0, url.lastIndexOf("\")));        // 先创建目录,如果直接创建文件的话,会抛出找不到指定路径的错误
                if (!folder.exists()) {
                    folder.mkdirs();
                }
                File file = new File(url);      // 创建文件
                if (!file.exists()) {
                    file.createNewFile();
                }
                fileWriter = new FileOutputStream(file.getAbsoluteFile());
                fileWriter.write(sb.toString().getBytes("UTF-8"));
                fileWriter.flush();
                System.out.println(url);        // 打印绝对路径
                System.out.println(sb.toString());      // 打印结果
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fileWriter != null) {
                        fileWriter.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    测试

    package com.liuxingwu.codes.example10;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-16 18:28
     */
    @ExtractInterface("IPerson")
    public class Person {
        public void speak(String message) {
            System.out.println(message);
        }
        public void useTool(String toolName) {
            System.out.println(toolName);
        }
    }
    
    package com.liuxingwu.codes.example10;
    
    /**
     * @author LiuXingWu
     * @create 2021-02-16 18:32
     */
    public class AnnotationTest {
        public static void main(String[] args) throws Exception {
            AnnotationProcessor processor = new ExtractProcessor();
            processor.process(Person.class);
        }
    }
    
    

    结果生成如下的接口:

    package com.liuxingwu.codes.example10;
    public interface IPerson{
    	 public void useTool (java.lang.String arg0);
    	 public void speak (java.lang.String arg0);
    	 public boolean equals (java.lang.Object arg0);
    	 public java.lang.String toString ();
    }
    
    向大神看齐
  • 相关阅读:
    Opencv在mac系统的安装与试用
    VINS 估计器之检查视差
    C语言——第零次作业
    C语言博客05指针
    循环结构
    C语言博客作业数组
    函数3
    C博客作业01分支、顺序结构
    group by的查询
    layui多张图片上传最多9张(新增和修改时的显示问题)
  • 原文地址:https://www.cnblogs.com/Liu-xing-wu/p/14531505.html
Copyright © 2011-2022 走看看