代理模式给某对象提供一个代理对象,由代理对象来控制对原对象的引用。该模式经常出现在系统框架或相关组件中,如Spring框架如何解决循环依赖,在Mybatis 定义 Dao 层相关接口 不写实现 如何通过注解或者xml映射到对应到sql语句。下面介绍 静态代理和动态代理(JDK ,cglib代理)
设计模式,一定要敲代码理解
静态代理
被代理实体抽象
public interface Isend {void toSend();
}
被代理实体实现
public class Send implements Isend {@Overridepublic void toSend() {System.out.println("送快递中");}
}
静态代理类
/*** @author ggbond* @date 2024年04月09日 15:24* 静态代理 类*/
public class staticProxy implements Isend{private Isend send;public staticProxy(Isend send) {this.send = send;}//方法增强@Overridepublic void toSend() {before();send.toSend();after();}public void before(){System.out.println("代理人员即将进行配送");}public void after(){System.out.println("代理人完成配送");}
}
测试与结果
public class Main {public static void main(String[] args) {Isend s1=new Send();s1.toSend();System.out.println("----------");Isend proxy=new staticProxy(s1);proxy.toSend();}
}
送快递中
----------
代理人员即将进行配送
送快递中
代理人完成配送
动态代理
JDK 代理
核心 实现 InvocationHandler 接口 ,通过反射实现代理
/*** @author ggbond* @date 2024年04月09日 15:35* JDK代理*/
public class myIvocationhandler implements InvocationHandler {private Object target;public myIvocationhandler(Object target) {this.target = target;}public void before(){System.out.println("JDK代理人员即将进行配送");}public void after(){System.out.println("JDK代理人完成配送");}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object object;if ("toSend".equals(method.getName())){ //反射拦截注入before();object = method.invoke(target, args);after();}else {object = method.invoke(target, args);}return object;}
}
cjlib 代理
cglib 代理 ,需导入 cglib.jar, ASM.jar包
核心 实现MethodInterceptor 接口,与上述写法类似。
public class myInterceptor implements MethodInterceptor {private Object target;public myInterceptor(Object target) {this.target = target;}public void before(){System.out.println("cglib代理人员即将进行配送");}public void after(){System.out.println("cglib代理人完成配送");}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {Object object;if ("toSend".equals(method.getName())){ //反射拦截注入before();object = method.invoke(target, objects);after();}else {object = method.invoke(target, objects);}return object;}
}
测试与结果
/*** @author ggbond* @date 2024年04月09日 16:00*/
public class Main {public static void main(String[] args) {Isend s1= new Send();s1.toSend();System.out.println("-----------");//JDK proxy 基于接口 代理Isend JDKproxy=(Isend) Proxy.newProxyInstance(s1.getClass().getClassLoader(),s1.getClass().getInterfaces(),new myIvocationhandler(s1));JDKproxy.toSend();System.out.println("-----------");//cjlib proxy 继承代理Send s2 = new Send();Enhancer enhancer=new Enhancer();enhancer.setCallback(new myInterceptor(s2));enhancer.setSuperclass(s2.getClass());Send cglibproxy =(Send) enhancer.create();cglibproxy.toSend();}
}
送快递中
-----------
JDK代理人员即将进行配送
送快递中
JDK代理人完成配送
-----------
cglib代理人员即将进行配送
送快递中
cglib代理人完成配送
总结
静态代理 需对代理目标进行”量身定制“,当目标新加方法时,代理类也要对应加实现。不利于扩展。
动态代理 :这里介绍 基于接口实现的JDK代理与cglib代理(继承代理)
- 代理对象的生成方式:JDK代理是基于接口的代理,要求目标对象必须实现一个接口,代理类会实现同样的接口,并在其中调用目标对象的方法。而CGLIB代理则能够代理没有实现接口的类,通过继承目标类生成子类的方式来创建代理对象。
- 底层实现技术:JDK代理主要基于反射机制,通过InvocationHandler接口来定义代理类的行为。在代理对象的方法被调用时,会触发InvocationHandler接口的invoke()方法。而CGLIB代理则使用底层的字节码技术,通过Enhancer类和MethodInterceptor接口来创建代理对象,工作通过字节码增强技术完成。
- 性能差异:JDK代理由于基于反射机制,因此在调用代理方法时性能上可能不如CGLIB代理。而CGLIB代理通常被认为性能更好,因为它通过直接操作字节码生成新的类,避免了使用反射的开销。
使用场景:JDK代理适用于接口驱动的代理场景,当不涉及具体类,只关心接口定义时非常适用。而CGLIB代理则在需要代理没有实现接口的类,或者需要通过继承来提供增强功能的场景更适用。 - 依赖问题:JDK代理不需要添加任何额外依赖,因为它是基于JDK自带的API。而CGLIB代理则需要添加CGLIB库的依赖。
代码下载
代码下载