反射&注解
反射
Java程序的三个阶段,source、class、runTime
对应三种获取核心类class的方法
- 通过.class属性
- 通过对象引用的getClass()方法
- 通过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](https://i-blog.csdnimg.cn/blog_migrate/afc500c2aa0c1f92023cd57d3267ca27.png)
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常用注解
-
@Override
表示当前定义的方法将覆盖在父类的同名、同参数方法,如果定义的方法名在父类中找不到,编译器将会提示must override or implement a supertype method错误。
-
@SuperessWarnings
关闭无需关心的警告信息,该注解可用于这个类上,也可以用于方法上。该注解只在JDK5之后的版本中才起作用,之前的版本也可以使用该注解,但是不起作用。
-
@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!";
}
}
自定义注解
元注解
- @Target:表明了自定义注解的作用域。可能的作用域被定义在一个枚举类型中:ElementType。具体如下
- ElementType.ANNOTATION_TYPE:作用在注解类型上的注解
- EementType.CONSTRUCTOR:作用在构造方法上的注解
- EementType.FIELD:作用在属性上的注解
- EementType.LOCAL_VARIABLE:作用在本地变量上的注解
- EementType.METHOD:作用在方法上的注解
- EementType.PACKAGE:作用在包上的注解
- EementType.PRARMETER:作用在参数上的注解
- EementType.Type:作用在类、接口或枚举上的注解
- @Retention:用于声明注解信息的保留策略,可选的级别被存放在枚举RetentionPolicy中。具体如下
- RetentionPolicy.SOURCE:注解信息仅保留在源文件中,编译时将丢弃注解信息
- RetentionPolicy.CLASS:注解信息将被编译进Class文件中,但这些注解信息在运行时将丢弃
- RetentionPolicy.RUNTIME:注解信息将被保留到运行时,可以通过反射来读取这些注解信息
- @Documented:表明制作Javadoc时,是否将注解信息加入文档。如果注解在声明时使用的该注解声明,则在制作Javadoc时注解信息会加入文档中
- @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;
}
方法实现逻辑如下:
![](https://i-blog.csdnimg.cn/blog_migrate/d848f704a00b68c17fa849c9f3c1e9af.png)
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 ();
}