文章目录
- 反射和内省(introspector)的区别
- InvocationHandler调用处理接口
- 动态代理示例
- 动态代理报错 object is not an instance of declaring class
- InvocationHandler的invoke的proxy参数
- invoke方法自动调用 重
- proxy的newProxyInstance()方法的参数有几个,分别是什么?
- 反射获取方法
- 反射获取类
- 反射获取某些属性值
- 面试题
- 说下对反射的理解
- 动态代理有几种方式?
- spring中的动态代理是怎样的?
反射早就知道,今天翻博客,发现居然没有反射的笔记,那弄一篇吧。
反射和内省(introspector)的区别
反射:反射是指在运行时获取一个类的所有信息,包括成员变量、成员方法、构造器等,并可以操纵这些部分。反射机制将类的各种成分映射成Java对象,以便在运行时进行操作。
内省:内省是指程序在运行时检查对象的能力,而反射则是指程序在运行时修改对象的能力
。内省主要用于获取对象的属性和方法信息,而反射则更侧重于动态操作对象。
| — | 反射 | 内省 |
|---|---|---|
| 定义 | 反射是指在运行时获取一个类的所有信息,包括成员变量、成员方法、构造器等,并可以操纵这些部分。反射机制将类的各种成分映射成Java对象,以便在运行时进行操作。 | 略 |
| 区别 | 反射则是指程序在运行时修改对象的能力。反射则更侧重于动态操作对象。 | 内省是指程序在运行时检查对象的能力。内省主要用于获取对象的属性和方法信息。 |
InvocationHandler调用处理接口
代码:
publicinterfaceInvocationHandler{publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable;}InvocationHandler是反射的重要组成部分。
动态代理示例
步骤:
1、定义一个接口。# 这是你的业务接口
2、实现InvocationHandler接口。# 这个接口用于处理代理实例上的方法调用
3.创建代理对象,然后代理对象调用业务方法(这步调用方法不说也一样)。 # 使用Proxy.newProxyInstance()方法创建一个代理对象
举个例子就比较好理解了。
例如交通工具,可以跑,现在想要记录下跑一定距离花费的时间。
交通工具接口类。
// 交通工具publicinterfaceVehicle{// 跑publicvoidrun(Stringdistance);}汽车是交通工具的一个实现类。
@Slf4jpublicclassCarimplementsVehicle{@Overridepublicvoidrun(Stringdistance){log.info("run方法开始,distance={}",distance);}publicstaticvoidmain(String[]args){Carcar=newCar();TimeInvocationHandlertimeInvocationHandler=newTimeInvocationHandler(car);VehicleproxyInstance=(Vehicle)Proxy.newProxyInstance(Car.class.getClassLoader(),newClass[]{Vehicle.class},timeInvocationHandler);proxyInstance.run("100米");}}时间动态代理。
@Slf4jpublicclassTimeInvocationHandlerimplementsInvocationHandler{privatefinalObjectvehicle;publicTimeInvocationHandler(Objectvehicle){this.vehicle=vehicle;}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{longs=System.currentTimeMillis();log.info("invoke方法开始,method={},开始时间={}",method.getName(),System.currentTimeMillis());Objectinvoke=method.invoke(vehicle,args);log.info("invoke方法完成,method={},结束时间={},耗时={}",method.getName(),System.currentTimeMillis(),System.currentTimeMillis()-s);returninvoke;}}动态代理报错 object is not an instance of declaring class
当然不只是一种情况,个人遇到的是invoke方法的对象错写为this了,实际应该是构造器传入的对象。
InvocationHandler的invoke的proxy参数
动态代理的proxy代码中并没有用到,是可有可无的吗?
关于这个参数,说法太多了。有说它就是代理对象的,也有说可以递归调用的。
invoke方法自动调用 重
说实话,这个问题困扰我半天,一直没想明白返回的明明是业务接口,调用方法是如何和invoke产生关联的。
后来终于找到了,网上确实有高人。 详见另外一篇博客。
proxy的newProxyInstance()方法的参数有几个,分别是什么?
千万别小看这个问题,对着代码什么都明白,背过代码啥都记不起来,脑子是白长的吗?
publicstaticObjectnewProxyInstance(ClassLoaderloader,Class<?>[]interfaces,InvocationHandlerh)有3个参数,分别是:
loader # 类加载器
interfaces # 业务接口数组
InvocationHandler # 调用处理接口
反射获取方法
反射获取类
反射获取某些属性值
例如:获取attr1-20属性的值。
publicstaticMap<Long,Long>buildDimMapFromAttrs(Objectdetail){Map<Long,Long>dimMap=newHashMap<>();if(detail==null){returndimMap;}Class<?>clazz=detail.getClass();for(inti=1;i<=50;i++){try{Fieldfield=clazz.getDeclaredField("attr"+i);// 属性为attr1、attr2等field.setAccessible(true);Longvalue=(Long)field.get(detail);if(value!=null){dimMap.put((long)i,value);}}catch(NoSuchFieldException|IllegalAccessExceptione){// ignore}}returndimMap;}注:这里的detail以前是具体的类型,实际可以用Object,适配性更强。
面试题
说下对反射的理解
基于reflect包,在运行时,查看、创建、修改对象的能力。
动态代理有几种方式?
1、jdk动态代理
2、cglib动态代理
| — | jdk动态代理 | cglib动态代理 |
|---|---|---|
| 底层原理 | 实现接口的实现类,基于jdk内置字节码生成器生成代理类(接口实现类)字节码 | 继承父类,实现子类,基于asm字节码工具生成子类 |
| 依赖 | 基于jdk内部字节码生成器 | 基于asm字节码工具 |
| 反射 | 全程依赖反射,主要是Method.invoke() | 创建对象用到反射,方法调用依靠FastClass绕过反射,性能更高 |
| 限制 | 必须有接口, | 类、方法不能用final修饰,否则无法生成,类(无法被继承),方法(无法被重写) |
spring中的动态代理是怎样的?
是不是会这么答?
有接口 用jdk动态代理 # 默认
无接口 用cglib动态代理
所以spring自动做了兼容,无论是否有接口都可以实现。
对是对,还可以答的更好。
springboot2.0之后,默认是cglib动态代理。
如果需要关闭,可以通过设置参数proxy-target-class=false,即:
1、全局都用jdk动态代理。
2、有接口没问题、无接口会报错。
注:2.0为了统一化,设置为false后不再自动兼容。