JDK动态代理两种方式
-
JDK动态代理(基于接口的动态代理)
-
优点:
-
解耦合(Decoupling):动态代理允许你将代理逻辑和实际业务逻辑分开,从而实现代码的解耦合。这可以使代码更容易维护和扩展。
-
代码重用:通过使用动态代理,你可以在不修改原始类的情况下,为多个类添加相同的代理行为。这种代码重用可以减少冗余代码的编写。
-
横切关注点(Cross-Cutting Concerns):动态代理可以用于实现横切关注点,如日志记录、事务管理、权限控制等。这样可以将这些关注点从业务逻辑中分离出来,提高了代码的可维护性。
-
灵活性:动态代理在运行时生成代理类,这使得你可以在不同的情况下使用不同的代理逻辑。你可以在运行时决定要应用的代理行为,而无需修改原始类。
-
减少重复代码:如果多个类需要相同的代理逻辑,动态代理可以避免在每个类中都编写相同的代码,从而减少了重复劳动。
-
-
缺点:
-
性能损失:动态代理通常会引入额外的运行时开销,包括代理类的生成和方法调用的拦截。虽然现代的动态代理机制尽可能优化了性能,但在某些高性能场景下,性能损失可能会有所影响。
-
只能代理接口方法:Java的动态代理只能代理接口中的方法,无法代理类的非接口方法。这意味着如果目标类没有实现接口,或者你想代理非接口方法,就需要考虑其他方法,比如CGLIB代理。
-
限制于public方法:动态代理只能代理公共(public)方法,无法代理私有(private)方法。这可能会限制你在一些特定情况下的代理能力。
-
复杂性增加:在一些复杂的场景下,动态代理可能会增加代码的复杂性。代理逻辑、代理接口、实际业务逻辑等之间的交互可能会导致代码难以理解。
-
-
案例:
- 要求:被代理类至少实现一个接口
-
接口标准
-
/* 技能类要实现的接口,即标准 * */ public interface ISkills { void shanxian(String user); void zhansha(String user); }
-
-
接口实现类
-
/* * 实现英雄技能类 * */ public class Skills implements ISkills { @Override public void shanxian(String user) { System.out.println(user + " 使用闪现追击"); } @Override public void zhansha(String user) { System.out.println(user + " 斩杀了英雄"); } }
-
-
主方法
-
public class Users { public static void main(String[] args) { # 实例化接口类 final Skills skills = new Skills(); /* * 涉及的类:Proxy * 创建代理对象的方法:newProxyInstance() * 该方法的参数: * ClassLoader:;类加载器。和被代理对象使用相同的类加载器 * Class[]:字节码数组。被代理类实现的接口,要求代理对象和被代理对象具有相同的行为 * InvocationHandler:用于我们提供增强代码的接口。一般会写一个该接口的实现类。 * 实现类可以是匿名内部类。它的含义就是如何代理。这里的代码只能是谁用谁提供 */ ISkills proxySkills = (ISkills) Proxy.newProxyInstance(skills.getClass().getClassLoader(), skills.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = null; String user = (String) args[0]; if ("shanxian".equals(method.getName( ) ) ){ name = (String) method.invoke(skills, user); } if ("zhansha".equals(method.getName( ) ) ){ name = (String) method.invoke(skills, user); } return name; } }); proxySkills.shanxian("鲁班"); proxySkills.zhansha("亚索"); }
-
-
-
简单CGLIB动态代理
-
CGLib是一个强大、高性能的字节码生成库, 使用CGLib实现动态代理, 完全不受代理类必须实现接口的限制。CGLib底层采用的是ASM字节码生成框架。使用字节码技术生成代理类,比java的反射效率还要高。CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类
-
CGLib包结构:
- net.sf.cglib.core 底层字节码处理类。
- net.sf.cglib.transform 该包中的类用于class文件运行时转换或编译时转换。
- net.sf.cglib.proxy 该包中的类用于创建代理和方法拦截。
- net.sf.cglib.reflect 该包中的类用于快速反射,并提供了C#风格的委托。
- net.sf.cglib.util 集合排序工具类。
- net.sf.cglib.beans JavaBean工具类。
-
CGLib动态代理相关的基础类:
- net.sf.cglib.proxy.Enhancer 主要的增强类。
- net.sf.cglib.proxy.MethodInterceptor 主要的方法拦截类,它是Callback接口的子接口,需要用户实现。
- net.sf.cglib.proxy.MethodProxy JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用。
-
案例:
-
被代理的类
// 创建一个普通类做为代理类 public class Person { // 代理类中由普通方法 public void eat() { System.out.println("我要开始吃饭咯..."); } public void play() { System.out.println("我要出去玩耍了,,,"); } }
-
拦截器
public class MyInterceptor implements MethodInterceptor { @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("吃饭前我会先洗手"); // 此处可以做一些操作 // Object result=proxy.invokeSuper(o,args); 表示调用原始类的被拦截到的方法。这个方法的前后添加需要的过程。 // 在这个方法中,我们可以在调用原方法之前或之后注入自己的代码 Object result = methodProxy.invokeSuper(object, args); System.out.println("吃完饭我会先休息会儿" ); // 方法调用之后也可以进行一些操作 return result; } }
-
主方法
/* * void setSuperclass(java.lang.Class superclass) 设置产生的代理对象的父类。 * void setCallback(Callback callback) 设置CallBack接口的实例。 * void setCallbacks(Callback[] callbacks) 设置多个CallBack接口的实例。 * void setCallbackFilter(CallbackFilter filter) 设置方法回调过滤器。 * Object create() 使用默认无参数的构造函数创建目标对象。 * Object create(Class[], Object[]) 使用有参数的构造函数创建目标对象。 * 参数Class[] 定义了参数的类型,第二个Object[]是参数的值。 * * */ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Person.class); enhancer.setCallback(new MyInterceptor()); Person person = (Person) enhancer.create(); person.eat();
-
-
-
复杂的CGLib动态代理
-
此时我想要在吃饭和玩耍前后都要进行一些操作,我们先看完整代码
-
完整代码:
-
package org.example; import net.sf.cglib.proxy.*; import java.lang.reflect.Method; public class test1 { public static void main(String[] args) { // 定义一个回调接口的数组 Callback[] callbacks = new Callback[] { new MyApiInterceptor(), new MyApiInterceptorForPlay() }; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Person.class); // 设置要代理的父类 enhancer.setCallbacks(callbacks); // 设置回调的拦截器数组 enhancer.setCallbackFilter(new CallbackFilterImpl()); // 设置回调选择器 Person person = (Person) enhancer.create(); // 创建代理对象 person.eat(); System.out.println("--------------------"); person.play(); } } class MyApiInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("吃饭前我会先洗手"); // 此处可以做一些操作 Object result = proxy.invokeSuper(obj, args); System.out.println("吃完饭我会先休息会儿" ); // 方法调用之后也可以进行一些操作 return result; } } class MyApiInterceptorForPlay implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("出去玩我会先带好玩具"); // 此处可以做一些操作 Object result = proxy.invokeSuper(obj, args); System.out.println("玩一个小时我就回家了" ); // 方法调用之后也可以进行一些操作 return result; } } class CallbackFilterImpl implements CallbackFilter { @Override public int accept(Method method) { if (method.getName().equals("play")) return 1; else return 0; } } // 创建一个普通类做为代理类 class Person { // 代理类中由普通方法 public void eat() { System.out.println("我要开始吃饭咯..."); } public void play() { System.out.println("我要出去玩耍了,,,"); } }
-
-
CallbackFilter可以实现不同的方法使用不同的回调方法。CallbackFilter中的accept方法, 根据不同的method返回不同的值i, 这个值是在callbacks中的顺序, 就是调用了callbacks[i]
-
增加定义有一个回调接口的数组
// 定义一个回调接口的数组 Callback[] callbacks = new Callback[] { new MyApiInterceptor(), new MyApiInterceptorForPlay() };
-
实现多个拦截
class MyApiInterceptorForPlay implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("出去玩我会先带好玩具"); // 此处可以做一些操作 Object result = proxy.invokeSuper(obj, args); System.out.println("玩一个小时我就回家了" ); // 方法调用之后也可以进行一些操作 return result; } }
参考文献:动态代理之 cglib 实现_温柔狠角色的博客-CSDN博客
-