Java并发系列:反射的使用和AOP

Java反射可以自由的获取类的对象属性、方法。本文主要从以下两个方面进行介绍:

理论


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.forNameClassLoader

  • 说明

    • Class.forName和ClassLoader都是用于加载类,Class.forName最终也会调用到ClassLoader
    • ClassLoader是遵循双亲委派模型最终调用启动类加载器的类加载器
    • ClassLoader不会对类进行初始化,Class.forName在调用时ClassLoader如果不指定初始化,两者几乎没有区别,但是Class.forName一般会初始化。
  • 参考博客:在Java的反射中,Class.forName和ClassLoader的区别

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 jdkcglib

  • 两者的作用:都是通过代理的方式来解决通用代码新增的问题
  • 两者的区别:jdk自带的代理使用上面有个限制,只能为接口创建代理类,如果需要给具体的类创建代理类,需要用后面要说的cglib
  • jdk中最主要的类
    • java.lang.reflect.Proxy
    • java.lang.reflect.InvocationHandler
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值