Java反射可以自由的获取类的对象属性、方法。本文主要从以下两个方面进行介绍:
- 理论
- 实践
- 反射需要注意的问题
- AOP专题
理论
C l a s s . f o r N a m e 和 C l a s s L o a d e r 的 区 别 \color{7f1A8A}Class.forName和ClassLoader的区别 Class.forName和ClassLoader的区别
-
说明
- Class.forName和ClassLoader都是用于加载类,Class.forName最终也会调用到ClassLoader
- ClassLoader是遵循双亲委派模型最终调用启动类加载器的类加载器
- ClassLoader不会对类进行初始化,Class.forName在调用时ClassLoader如果不指定初始化,两者几乎没有区别,但是Class.forName一般会初始化。
S P I 机 制 \color{7f1A8A}SPI机制 SPI机制
- 什么是SPI:一般类的加载都是遵从双亲委派模型,从顶层父类逐级往下查找可以加载这个类的加载器,后面出现了一些同名的类,但是需要厂商特定的加载器来加载,这样就不可以使用双亲委派模型了,必须出现一种机制,来使用我需要的加载器来加载使用的类。
- 如何实现SPI:
- 大家约定一个地方,只要我从这个地方读取类,那就不需要双亲委派模型来加载,而是从我自定义的这个地方开始加载,这个地方是:ClassPath路径下的META-INF/services文件夹。
- 其实最后还是在找到特定ClassLoader:cl的基础上调用来Class.forName来实现的。只不过加载器换了。
- 参考博客:深入理解SPI机制
实践
获 取 对 象 中 的 常 量 \color{7f1A8A}获取对象中的常量 获取对象中的常量
- 说明
- 假设存在类A,是一个测试用例类,类中存在大量的以array开头的非静态数组
- 现在有个类B,想对类A中的这些用例做同样的操作,因此需要把这些用例放置在一个容器中,通过循环去做操作,获取用例数组的过程需要反射。
- 注意
- 因为是非静态,所以m.invoke(gg),为不是m.invoke( c ),c是类,gg是对象,类取出来都是空的
- 每次属性取出来需要做判断来获取想要的值:o instanceof int[]
- 其实本质上调用了属性的get方法,因此A类必须有get()方法,或者在A类上注解了@Data(使用lombok)
- 主要源码
// 循环的写法
Class c = Class.forName("UnitTest.DatastructureTest.stringANDlineTest.listTest.ListljTest");
ListljTest gg = (ListljTest) c.newInstance();
Field[] f = c.getDeclaredFields();
for (Field f1 : f) {
if (f1.getName().contains("array0")) {
Method m = c.getMethod("get" + getMethodName(f1.getName()));
Object o = m.invoke(gg);
if (o != null && o instanceof int[]) {
int[] d = (int[]) m.invoke(gg);
System.out.println(“操作”);
}
}
}
// stream 写法
Class c = Class.forName("UnitTest.DatastructureTest.stringANDlineTest.listTest.ListljTest");
ListljTest gg = (ListljTest) c.newInstance();
Field[] f = c.getDeclaredFields();
Map<String, Object> map = Maps.newHashMap();
map = Arrays.stream(f)
.filter(x -> x.getName().contains("array0"))
.collect(Collectors.toMap(Field::getName, field -> {
Object resultObj = null;
field.setAccessible(true);
try {
resultObj = field.get(gg);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return Optional.ofNullable(resultObj).orElse(0);
}, (k1, k2) -> k2));
反射需要注意的问题
N o S u c h M e t h o d E x c e p t i o n \color{7f1A8A}NoSuchMethodException NoSuchMethodException
- 问题1:Exception in thread “main” java.lang.NoSuchMethodException: com.bjsxt. why.Dog.(java.lang.String, java.lang.String)。
- 原因:getConstructor只能获取public方法,无法获取其他修饰符修饰的方法。
- 解决:调用 getDeclaredConstructor() 解决,可获取非public修饰的构造方法
I l l e g a l A c c e s s E x c e p t i o n \color{7f1A8A}IllegalAccessException IllegalAccessException
- 问题2:Exception in thread “main” java.lang.IllegalAccessException: Class com. bjsxt. TestConstructor3 can not access a member of class com.bjsxt.Dog with modifiers "
- 原因:可以获取非public修饰的构造方法,不等于可以运行非public修饰的构造方法,受到了封装性的限制
- 解决:调用 con.setAccessible(true) 方法,可以突破封装性的限制。
AOP专题
j d k 动 态 代 理 和 c g l i b \color{7f1A8A}jdk动态代理和cglib jdk动态代理和cglib
- 两者的作用:都是通过代理的方式来解决通用代码新增的问题
- 两者的区别:jdk自带的代理使用上面有个限制,只能为接口创建代理类,如果需要给具体的类创建代理类,需要用后面要说的cglib
- jdk中最主要的类
- java.lang.reflect.Proxy
- java.lang.reflect.InvocationHandler