注解与反射
注解加反射,框架构建基础
注解(Annotation)
annotation,对程序作出解释,被程序理解
附加在方法,属性上,给他们添加额外的信息,或者进行什么操作
@Override Java内置对象
元注解
@Target 用于描述注解的使用范围
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
value属性返回值ElementType 是enum类型,比如上面的ElementType.ANNOTATION_TYPE
查阅JDKAPI:
public enum ElementType {
TYPE, //类,接口(包括注释类型)或枚举声明
FIELD,//字段声明(包括枚举常数)
METHOD,//方法声明
PARAMETER,//正式参数声明
CONSTRUCTOR,//构造函数声明
LOCAL_VARIABLE,//局部变量声明
ANNOTATION_TYPE,//注解类型声明
PACKAGE,//包装声明
TYPE_PARAMETER,//键入参数声明
TYPE_USE//使用类型
}
Retention:用于解释注解的生命周期(source-源码, class-编译 ,runtime-运行)
@Retention(RetentionPolicy.RUNTIME)
public @interface Retention {
RetentionPolicy value();
}
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
Document 说明该注解将被包含在Javadoc中,即其注释将成为注释元素的公共API的一部分
Inherited 说明子类可以继承父类中的该注解
自定义注解
@interface自动继承了Java.lang.annotation.Annotation接口
声明注解@interface
要有元注解中的Target与Retention
属性就是一个配置参数
属性类型(可以是数组)就是在注解输入的参数类型
只有一个名为value的属性,可以不用在注解引用时写
其他的要写
有多个引用方法时全都要写,除非配置了default
注解元素必须要有值,在定义注解元素时,经常使用空字符串和0作为默认值
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@TableStudent(value = "学生表注释",name = {"姓名","性别"},age = 18)
@interface TableStudent{
String value() default "";
String[] name()default {};
int age() default 0;
}
反射(Reflection)
程序在运行时,对未知的类进行探索的过程。
获取一个类有哪些属性、方法、构造器。Java因为反射获得了动态语言的特性,即在运行时可改变其结构
1、获取类的Class对象
Class to1=TestObject.class;
Class to2=Class.forName("com.hs.TestObject");
TestObject oo=new TestObject(10);
Class to3 = oo.getClass();
2、一个实体类只有一个Class对象,如下图两个对象的内存地址相同
3、一个类被加载后,整个类的结构被封装到Class对象中
即一个加载的类在JVM中只有一个Class实例
在类加载器加载类的时候会生成类的Class对象,然后在类初始化时通过
4、Class对象只能由系统建立对象,只能获得这个对象
类什么时候初始化:
类主动引用发送类的初始化:
JVM启动,先初始化主程序入口所在类,即main方法所在类
new一个类的对象时,下次再new类的初始化已完成
调用类的静态成员(除了final修饰的变量)和静态方法
初始化一个类,发现父类未初始化,则会先初始化它的父类,父类在类加载的链接阶段
类的被动引用:
通过数组定义类引用,不会触发次类的初始化
引用常量不会触发类的初始化,因为常量在类加载的链接阶段就存入到调用类的常量池中了
获取类运行时的完整结构
实体类
public class Category implements Serializable {
private static final long serialVersionUID = -95736938451252186L;
private Integer cateid;
public Integer getCateid() {
return cateid;
}
public void setCateid(Integer cateid) {
this.cateid = cateid;
}
public Category() {
}
public Category(Integer cateid) {
this.cateid = cateid;
}
}
获得指定的方法,因为重载,所以通过方法名与参数识别指定方法,比如使用有参构造器,传入参数String.Class
Class<Category> c1 = Category.class;
//通过获得无参构造器实例化
Constructor<Category> constructor = c1.getConstructor();
Category category1 = constructor.newInstance();
//通过class对象得到该对象的一个实例
Category category = c1.newInstance();
//通过有参构造器实例化
Constructor<Category> constructor1 = c1.getConstructor(String.class);
Category category2 = constructor1.newInstance();
//只能获得public修饰的属性
Field[] fields = c1.getFields();
//循环所有属性
Field[] declaredFields = c1.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
//获取本类中的所有方法,包括私有的(private、protected、默认以及public)的方法。
Method[] declaredMethods = c1.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
//获取本类以及父类或者父接口中所有的公共方法
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
私有的不能直接使用
可以通过access为true授权使用
//得到Class对象
Class<Category> c1 = Category.class;
//通过class对象得到该对象的一个实例
Category category = c1.newInstance();
//得到setName方法,方法没有参数写null
Method method = c1.getDeclaredMethod("setName",String.class);
//通过方法调用invoke给方法赋值,确定是category实例的方法,方法没有参数写null
method.invoke(category,"闪闪");
//输出该实例的Name值
System.out.println(category.getName());
//属性同理
Field field = c1.getDeclaredField("name");
//私有需要授权
field.setAccessible(true);
field.set(category,"我");
System.out.println(category.getName());
想要获得反射效率,可以关闭权限检查
反射获取泛型
public void test1(Map<String,Integer> map, List<Double> list){
System.out.println("test1");
}
public Map<String,Character> test2(){
System.out.println("test2");
return null;
}
@Test
public void test() throws NoSuchMethodException {
Class<GenericTest> genericTestClass = GenericTest.class;
Method test1 = genericTestClass.getMethod("test1", Map.class, List.class);
Type[] genericParameterTypes = test1.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println("参数类型"+genericParameterType);
if (genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
Method test2 = genericTestClass.getMethod("test2", null);
Type genericReturnType = test2.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
反射操作注解
框架原理就是通过反射找到注解中的值,然后将值赋值给对应的属性,所以Springboot可以通过注解实现自动配置
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableStudent{
String value() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FiledStudent{
String value() ;
String name();
int column();
}
@TableStudent("Student_tb")
public class Student {
@FiledStudent(value = "属性",name = "张三",column =1)
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Test
public void test1() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
Class<Student> studentClass = Student.class;
Annotation[] annotations = studentClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
TableStudent annotation = studentClass.getAnnotation(TableStudent.class);
String value = annotation.value();
System.out.println(value);
Field name = studentClass.getDeclaredField("name");
FiledStudent annotation1 = name.getAnnotation(FiledStudent.class);
System.out.println(annotation1.column());
System.out.println(annotation1.name());
System.out.println(annotation1.value());
//通过注解获得值,通过反射给创建实例的属性赋值
Student student = studentClass.newInstance();
name.setAccessible(true);
name.set(student,annotation1.name());
System.out.println(student.getName());
}