反射与注解
一、反射
- 使用反射机制可以动态的获取当前class的信息,比如方法的信息、注解信息、方法的参数、属性
- 反射目的:方便开发者对框架的拓展,缺点:消耗一定资源、破坏封装性
二、字节码对象创建方式
Class class1 = 类名.class
Class class2 = Class.forName(包路径) //使用最多
Class class3 = 对象.getClass()
// class1 == class2 == class3
三、反射常用api
1. getDeclaredMethod(s):返回自身类的所有公用(public)方法包括私有(private)方法,
这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,
但不包括继承的方法。
2. 返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法,
这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承
的那些的类或接口)的公共 member 方法。
// getDeclaredMethods的关键词是:自身,所有方法,不继承
// getMethods的关键词是public+继承
3. getDeclaredField(s)和getField(s)同上。
4. getDeclaredAnnotation(s):返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,
该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)
该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。
5. getAnnotation(s):返回此元素上存在的所有注释。(如果此元素没有注释,则返回长度为零的数组。)
该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。
// getDeclaredAnnotations得到的是当前成员所有的注释,不包括继承的。
// getAnnotations得到的是包括继承的所有注释。
四、反射越过泛型检查
// 反射越过泛型检查
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("1");
Class<? extends ArrayList> aClass = arrayList.getClass();
Method add = aClass.getMethod("add", Object.class);
add.invoke(arrayList, 23); System.out.println(arrayList); // [1, 23],数据已经插入进去
// java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
arrayList.forEach(System.out::println); // 注意,如果使用循环遍历时容易导致类型转换异常
五、注解
- 元注解用来在声明新注解时指定新注解的一些特性
- 开发人员自定义注解
常用注解
@Target(ElementType.METHOD) //指定注解在方法上
@Retention(RetentionPolicy.RUNTIME) //指定新注解保留到程序运行时期
@Inherited // 指定新注解标注在父类上时可以被子类继承
六、获取方法、字段上面的注解
Class<?> aClass = Class.forName("com.lzs.annotationdemo.day01.User");
Method sum = aClass.getMethod("test");
Field userName = aClass.getDeclaredField("userName");
// 反射获取方法上的注解
LzsName method = sum.getDeclaredAnnotation(LzsName.class);
// 反射获取字段上的注解
LzsName filed = userName.getDeclaredAnnotation(LzsName.class);
// 如果没有方法上没有 @LzsName该注解则为null
System.out.println(method); //@com.lzs.annotationdemo.annotation.LzsName()
// 如果没有字段上没有 @LzsName该注解则为null
System.out.println(filed); //@com.lzs.annotationdemo.annotation.LzsName()
七、使用aop环绕通知拦截目标方法(aop+自定义注解实现限流)
封装限流注解思路
- 需要自定义注解
- 限流注解如何生效
- 如何判断方法上是否加上了限流注解
解决思路
- 使用反射机制判断该方法上是否加上限流注解
- 如果该方法上有加注解,则调用限流api框架
八、为什么要使用环绕通知(aop)
- 环绕通知非常的灵活控制目标方法是否执行
- 环绕通知
- 前置通知-----如何触发:
如果在环绕通知中调用了目标方法,则会先执行前置通知,再执行目标方法
- 目标方法
- 后置通知
- 异常通知
环绕通知中执行目标方法时执行顺序: 环绕通知开始执行 前置通知 目标方法开始执行 环绕通知结束执行 后置通知 环绕通知中不执行目标方法时执行顺序:(前置通知不执行) 环绕通知开始执行 环绕通知结束执行 后置通知
九、总结
-
熟悉反射3种获取字节码对象的方法
Class class2 = Class.forName(包路径) //使用最多
-
会自定义注解,如果不知道如何定义注解,可以随便找一个注解点进去看看
-
熟悉反射常用api,如获取构造器、方法、属性等(获取方法运用最多,通过反射调用方法)
-
熟悉aop+反射+自定义注解这一套