学习笔记_反射&&注解

反射&注解

反射

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("\n");        // 换行
        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("\n");
        return sb;
    }

    /**
     * 该方法的功能是向临时代码存储器中添加抽象方法。方法的信息一般包括方法修饰符、返回值、方法名、参数类型、参数等。
     * 具体实现如下:
     * 1. 添加修饰符
     * 2. 添加返回值类型
     * 3. 添加方法名
     * 4. 遍历参数类型,并向StringBuilder中添加参数
     * @param sb 临时代码存储器
     * @param method 方法对象
     * @return
     */
    private StringBuilder addMethod(StringBuilder sb, Method method) {
        sb.append("\t 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("\n");
        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 ();
}
posted @ 2021-03-14 09:33  技术扫地生—楼上老刘  阅读(114)  评论(0编辑  收藏  举报