此内容根据文章生成,仅用于文章内容的解释与总结
投诉动态代理
什么是动态代理
为了避免代码侵入式的修改,在程序运行时,目标对象不变,对目标对象生成代理对象,代理对象中的方法是目标对象方法的增强方法,最终达到目标对象增强的效果。
Java中动态代理的实现
第一种:基于接口实现动态代理
基于接口的动态代理,用到的类是Proxy的newProxyInstance静态方法创建,要求被代理对象至少实现一个接口,如果没有,则不能创建代理对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface MyInterface { void doSomething(); } public class DynamicProxyDemo { public static void main(String[] args) {
MyInterface proxyInstance = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(), MyInterface.getClass().getInterface(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before method: " + method.getName()); Object result = method.invoke(new MyInterfaceTarget(), args); System.out.println("After method: " + method.getName()); return result; } } ); proxyInstance.doSomething(); } static class MyInterfaceTarget implements MyInterface { @Override public void doSomething() { System.out.println("Doing something..."); } } }
|
可以看到,参数三InvocationHandler是一个函数式接口,这里采用匿名内部类的形式重写方法,这个方法中的这行代码:
1
| Object result = method.invoke(new MyInterfaceTarget(), args);
|
就是利用反射,拿到目标对象的方法,而上下两行的打印语句就是被增强的地方。
方法返回原始方法method。
最终返回结果是原始方法,因为增强的功能已经执行了。
第二种:基于第三方库CGLib
要导入cglib第三方库,使用的类是Enhancer的create静态方法创建,要求被代理类不能是最终类,即不能用final修饰,如String类。
CGLib 实现步骤
- 创建一个实现接口 MethodInterceptor 的代理类,重写 intercept 方法;
- 创建获取被代理类的方法 getInstance(Object target);
- 获取代理类,通过代理调用方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public static Object getObjectByCGLib(final Object obj){
Object proxyObj = Enhancer.create(obj.getClass(), new MethodInterceptor() { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { long startTime = System.currentTimeMillis();
Object invokeObject = method.invoke(obj, objects);
long endTime = System.currentTimeMillis(); SimpleDateFormat sdf = new SimpleDateFormat(); System.out.printf(String.format("%s方法执行结束时间:%%s ;方法执行耗时:%%d%%n" , method.getName()), sdf.format(endTime), endTime - startTime); return invokeObject; } }); return proxyObj; }
|
两者区别
JDK Proxy 和 CGLib 的区别主要体现在以下方面:
- JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现;
- Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新,Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;
- JDK Proxy 是通过拦截器加反射的方式实现的;
- JDK Proxy 只能代理实现接口的类;
- JDK Proxy 实现和调用起来比较简单;
- CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高;
- CGLib 无需通过接口来实现,它是针对类实现代理,主要是对指定的类生成一个子类,它是通过实现子类的方式来完成调用的。
ASM
ASM是一个Java字节码操作和分析框架,它提供了一套直接操作Java字节码的API。与Java反射机制相比,ASM通过直接操作字节码来避免了许多反射带来的性能开销,因此能够显著提高应用程序的性能。ASM的API设计得非常灵活且强大,它允许开发者在类被加载到Java虚拟机之前动态地修改类的行为,包括添加、删除或修改方法、字段等。
具体来说,当CGLib需要创建一个代理对象时,它会利用ASM来动态生成这个代理类的字节码。这个过程中,CGLib会根据被代理类的结构、需要增强的方法等信息,生成相应的字节码,并加载到Java虚拟机中。这样,CGLib就能够在运行时动态地创建代理类,而无需编写大量的模板代码。
反射
Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性、方法、构造函数、访问修饰符等信息。例如,使用它能获得 Java 类中各成员的名称并显示出来。在运行时动态加载类等。第三方主流的框架如:spring, springMVC, mybatis 等内部都大量的使用反射技术。
面试官问你什么是反射
只需要答出来,反射机制指的是在程序运行时(JVM时期)可以获取自身的信息。在Java中只需要给定类名,就可以通过反射机制获取该类所有的属性和方法。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java反射机制主要提供了以下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理。
- 反射
java.lang.Class
实例:表示正在运行的类的信息
- 通过
java.lang.Class
反射Constructor
(构造函数)实例
- 通过
java.lang.Class
反射Field
(字段、属性)实例
- 通过
java.lang.Class
反射Method
(方法)实例
Java代码实现反射
反射 Class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class ReflectionConstructor { public static void main(String[] args) { try { java.lang.Class clazz = Class.forName("com.lanqiao.reflect.Employee");
java.lang.Class<?> clazz2 = Employee.class; Employee e = new Employee(); Class<? extends ?> aClass = e.getClass(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
|
反射构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class ReflectionConstructor { public static void main(String[] args) { try { Class clazz = Class.forName("com.lanqiao.reflect.Employee"); Constructor[] constructors = clazz.getDeclaredConstructors(); constructors = clazz.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); }
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class); System.out.println(constructor);
constructor.setAccessible(true);
Object emp = constructor.newInstance("james", 11); System.out.println(emp);
constructor = clazz.getDeclaredConstructor(String.class, Double.class, int.class); emp = constructor.newInstance("mike", 999.9, 11); System.out.println(emp);
} catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
|
反射成员属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class ReflectionField { public static void main(String[] args) { try { Class clazz = Class.forName("com.lanqiao.reflect.Employee"); Object instance = clazz.newInstance();
Field[] fields = clazz.getFields(); fields = clazz.getDeclaredFields(); for (Field field : fields) { System.out.println(field); }
Field field = clazz.getDeclaredField("ename"); field.setAccessible(true); field.set(instance, "jerry");
field = clazz.getDeclaredField("sal"); field.setAccessible(true); field.set(instance, 999.88);
System.out.println(instance); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } }
|
反射成员方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class ReflectionMethod { public static void main(String[] args) { try { Class clazz = Class.forName("com.lanqiao.reflect.Employee"); Object instance = clazz.newInstance();
Method[] methods = clazz.getMethods(); methods = clazz.getDeclaredMethods(); for (Method method : methods) { System.out.println(method); }
Method method = clazz.getDeclaredMethod("setEname", String.class); Object returnValue = method.invoke(instance,"jerry");
System.out.println("调用 setEname 方法的返回值:" + returnValue); System.out.println(instance);
method = clazz.getDeclaredMethod("test", String.class, int.class); method.setAccessible(true); returnValue = method.invoke(instance, "hello ", 22); System.out.println("调用 test 方法的返回值:" + returnValue); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
|
反射为什么慢
Java虚拟机限制
在JVM中,将字节码翻译成机械码的方式有:Java自带的解释器和JIT即时编译器。
由于反射涉及到动态解析的类型,对此不能执行java虚拟机的某些优化机制, 比如JIT优化。
这两种方式的区别在于,前者启动速度快但运行速度慢,后者启动速度慢但运行速度快,原因是因为解释器不需要像JIT编译器一样,将所有字节码都转化为机器码,自然就少去了优化时间。
一文看懂为什么java反射性能慢、效率低_反射为什么消耗性能大-CSDN博客
Java的反射机制? 为什么反射慢_java反射性能低下的原因-CSDN博客
需要反复装箱拆箱
在使用反射时, 参数需要包装成Object类型, 但是真正执行方法的时候, 又需要拆包成真正的类型
触发GC次数多
需要额外的内存分配,反射操作可能涉及到临时的对象创建,比如method,field等对象,导致垃圾回收的成本变高,造成额外的开销