星空体育入口:招聘面试官问:动态代理是什么?
代售点的出现,可以说,它明显地提升了老板的用户购票体验。从软件设计的观点来看,实际效果是一样的。使用代理模式编程能明显增强原有功能并简化方法调用方式。在讲解动态代理之前,让我们先来谈一谈静态代理。
静态代理接着,我们以两数相加为例来展示实现过程星空体育APP。
接口类
public interface Calculator { /** * 求两个数的和 * @param num1 * @param num2 * @return */ Integer add(Integer num1, num2); }
目标物件
public class CalculatorImpl implements Calculator { @Override public Integer add(Integer num1,整数num2) {\n Integer result = num1 + num2;\n return result;\ \n代理对象\npublic class CalculatorProxyImpl implements Calculator {\n private Calculator calculator;\n @Override\n public Integer add(Integer num1, 整数 num2) { // 在调用方法前,可以添加其他功能... 整数 结果 = 计算器.相加(num1, num2); // 方法调用后,可以为CalculatorProxyImpl添加其他功能。在CalculatorProxyClient测试类中,通过创建CalculatorImpl对象作为目标对象,再创建CalculatorProxyImpl对象作为代理对象,可以实现代理的功能。例如,代理对象可以调用目标对象的add方法,进行一些额外的操作,并返回所需的结果。2)System.out.println("累加结果:" + result);\n \n输出结果\n累加结果:3\n通过这种代理方式,最大的好处是可以在不改变目标对象的情况下,扩展目标对象的功能。虽然代理模式有其优点,但也存在一些缺点:代理对象和目标对象需实现相同的接口。因此,当目标对象扩展新功能时,代理对象也需要相应扩展,因而难以维护。动态代理的优点在于可以解决目标对象扩展新功能时代理对象也需要跟着扩展的问题。然而,它也有缺点,需要代理对象和目标对象实现相同的接口,因此,当目标对象扩展新的功能时,代理对象也需要一起扩展,维护起来不容易。那么它是如何被解决的呢?以JDK为例,当需要为特定目标对象添加代理处理时,JDK会动态在内存中构建代理对象,以实现对目标对象的代理功能。在下面,我们仍然以两数相加为例,详细介绍具体的方法!3.1、JDK 中生成代理对象的方式是通过创建接口。创建接口时需要使用public关键字, 并且在接口中定义方法。例如:\n```java\npublic interface JdkCalculator{\n /**\n * 计算两个数之和\n * @param num1\n * @param num2\n * @return\n */\n Integer add(Integer num1,\n```实现整数目标接口的JdkCalculatorImpl类,实现JdkCalculator接口。 覆盖公共Integer add(Integer num1)方法。 整数 num2) { 整数 result = num1 + num2; 返回 result; } }
动态代理对象星空体育入口
public class JdkProxyFactory { /** * 维护一个目标对象 */ 私有的 Object target; 公共的 JdkProxyFactory(Object target) { this.target = target; } 公共的 Object getProxyInstance(){ Object proxyClassObj = Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces() 新的 InvocationHandler(){ @Override public Object invoke(Object proxy, Method method, Object[] args) 抛出 Throwable 异常 { System.out.println("方法调用前,可以增加其他功能..."); // 执行目标对象方法 Object 返回值 = method.invoke(target,System.out.println("在方法调用后, args); ...");可以增加其他功能..."); 返回 returnValue; } }); 返回 proxyClassObj; } }测试类
公共 类
TestJdkProxy { 公共 静态 空 main(字符串public static void main(String[] args) {\n // 目标对象\n JdkCalculator target = new JdkCalculatorImpl();\n System.out.println(target.getClass());\n \n // 代理对象\n JdkCalculator proxyClassObj = (JdkCalculator) new JdkProxyFactory(target).getProxyInstance();\n System.out.println(proxyClassObj.getClass());\n \n // 执行代理方法\n Integer result = proxyClassObj.add(1, 2);\n System.out.println("结果为:" + result);\ 2) {{\nSystem.out.println("Sum result: " + result);\ }}\n输出如下:\nclass com.example.java.proxy.jdk1.JdkCalculatorImpl\nclass com.sun.proxy.$Proxy0\n在调用方法前,额外功能可以被添加...\n在调用方法后,可以增加其他功能...
相加结果为:3
2.通过Proxy.newProxyInstance()创建interface实例,其需要3个参数: (1)使用的ClassLoader,通常为接口类的ClassLoader (2)需要实现的接口数组至少需要提供一个接口作为参数。 (3)用于处理接口方法的调用的 InvocationHandler 实例。3. 对于返回的 Object,需要将其强制转换为接口类型。动态代理实际上是 JVM 在运行时动态创建类的过程,并没有什么神秘的黑魔法技术。将上述动态代理的代码改写为一个静态实现类大致如下示例:
目标 测试类 在这里,拦截思路与 JDK 类似,都是通过一个接口方法进行拦截处理!在前面的内容中,我们提到了,cglib 不仅可以代理接口,还可以代理类。接下来我们来尝试代理一个类。public class CglibCalculatorClass {\n /**\n * 计算两个数的和\n * @param num1\n * @param num2\n * @return\n */\n public Integer add(Integer num1,public int add(int num1, int num2) {\n int result = num1 + num2;\n return result;\
\npublic class TestCglibProxyClass {\n public static void main(String[] args) {\n //目标对象\n CglibCalculatorClass target = new CglibCalculatorClass();\n System.out.println(target.getClass());\n //代理对象\n CglibCalculatorClass proxyClassObj = (CglibCalculatorClass) new CglibProxyFactory(target).getProxyInstance();\n System.out.println(proxyClassObj.getClass());\n //执行代理方法\n int result = proxyClassObj.add(1, 2);\n System.out.println("result: " + result);\n }\
2);\nSystem.out.println("结果相加:" + result);\
}}\n输出结果\n类 com.example.java.proxy.cglib1.CglibCalculatorClass\n类 com.example.java.proxy.cglib1.CglibCalculatorClass$$EnhancerByCGLIB$$e68ff36c\n方法调用前,可以增加其他功能... 方法调用之后,能够附加其他功能... 两数相加的结果为3 在前述内容中,我们介绍的代理方式是通过在代码运行时动态生成class文件来实现动态代理的目的。动态代理的技术目的在于解决静态代理模式中当目标接口发生扩展时,代理类也需要相应变动的问题。这样可以避免工作变得繁琐和复杂。 基本上,动态代理的目的是回到问题的本质。遵循问题的核心,动态代理技术的主要目的实际上是为了解决静态代理模式中的一个问题,即当目标接口发生扩展时,代理类也需要相应地进行更改,以避免带来繁琐和复杂的工作负担。在Java生态系统中,还存在着一个非常知名的第三方代理框架,即AspectJ。AspectJ可以通过特定的编译器,在将目标类编译成class字节码时,同时在方法周围添加业务逻辑,从而实现静态代理的效果。 使用AspectJ进行方法插入时,一般有四种方式: 操作起来也很简单,首先需要在项目中添加AspectJ编译器插件。<插件>\n 由于是静态织入的,性能相对较好!由于是静态织入的,因此性能相对较好。小结如下:根据上述静态织入方案的介绍,与我们目前使用的Spring AOP的方法非常相似。可能有的同学会有疑问,我们目前使用的Spring AOP是动态代理,是动态生成的还是静态织入的呢星空体育官方?实际上,Spring AOP代理是对JDK代理和CGLIB代理进行了封装,同时引入了一些AspectJ中的注解,比如@pointCut、@after、@before等等,本质上使用的是动态代理技术。总结来看有三个要点:针对接口目标,一般采用JDK的动态代理技术;对于类目标,则使用cglib的动态代理技术;在引用AspectJ的时候会用到一些注解,如@pointCut、@after和@before,目的是为了简化使用方式,与AspectJ关系不是很大;那么为什么Spring AOP不采用AspectJ这种静态织入方案呢?虽然AspectJ编译器功能强大,性能优异,但每当目标类发生更改时都需要重新编译,可能主要是因为AspectJ编译器过于复杂,使用动态代理更加方便省事! 1、Java中的三种代理模式包括静态代理、动态代理和cglib代理 星空体育平台 星空体育官网 星空体育APP<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.5</version> </dependency>
接下来,我们将继续以两数相加为例,详细介绍具体的步骤!在这个地方呢,我们会只写中文而不是其他语言。 num2) }
public class CglibCalculatorImpl implements CglibCalculator { @Override public Integer add(Integer num1,public int add(int num1, int num2) {\n int result = num1 + num2;\n return result;\
\npublic class CglibProxyFactory implements MethodInterceptor {\n /** 维护一个目标对象 **/\n private Object target;\n \n public CglibProxyFactory(Object target) {\n this.target = target;\n }\n \n /** 为目标对象生成代理对象 **/\n public Object getProxyInstance() {\n //工具类\n Enhancer en = new Enhancer();\n //设置父类\n en.setSuperclass(target.getClass());\n //设置回调函数\n en.setCallback(this);\n //创建子类对象代理\n return en.create();\n }\n \n @Override\n public Object intercept(Object o, Method method, Object[] objects, \n MethodProxy methodProxy) throws Throwable {\n // TODO Auto-generated method stub\n return method.invoke(target, objects);\n }\
方法method(Object[] args, MethodProxy methodProxy) throws Throwable {\nSystem.out.println("在调用方法之前,可以添加其他功能...");\n// 执行目标对象方法\nObject returnValue = method.invoke(target, args);\
args); System.out.println("在方法调用之后,可以增加其他功能..."); return returnValue; } }
public class TestCglibProxy { public static void main(String[] args) { //目标对象 CglibCalculator target = new CglibCalculatorImpl(); System.out.println(target.getClass()); 使用代理对象CglibCalculator来代理目标对象,我们可以获取代理的实例对象。我们可以通过CglibProxyFactory类的getProxyInstance方法来创建代理实例。下面的代码展示了如何获取代理对象的类并执行代理方法。\n```java\nCglibCalculator proxyClassObj = (CglibCalculator) new CglibProxyFactory(target).getProxyInstance();\nSystem.out.println(proxyClassObj.getClass());\n// 执行代理方法\nInteger result = proxyClassObj.add(1, 2);\n```
2);\nSystem.out.println("The result of addition:" + result);\n输出结果:\nclass com.example.java.proxy.cglib1.CglibCalculatorImpl\nclass com.example.java.proxy.cglib1.CglibCalculatorImpl$$EnhancerByCGLIB$$3ceadfe4\n方法调用前,可以添加其他功能...\n方法调用后,可以增加其他的功能... 结果相加为:3 假设要把 cglib 生成的代理类改成静态实现类,大概如下:\npublic class CglibCalculatorImplByCGLIB extends CglibCalculatorImpl implements Factory {\n private static final MethodInterceptor methodInterceptor;\n private static final Method method;\n public final Integer add(Integer var1, 在 变量2) { 返回 methodInterceptor.intercept(this, method, new Object[]{var1, var2}, methodProxy); } //... }
2、Java动态代理的用途是什么?
代表未知数,
表示一个数, 代表正数。
星空体育入口 星空体育官方 星空体育下载