代理模式给某对象提供一个代理对象,由代理对象来控制对原对象的引用。该模式经常出现在系统框架或相关组件中,如Spring框架如何解决循环依赖,在Mybatis 定义 Dao 层相关接口 不写实现 如何通过注解或者xml映射到对应到sql语句。下面介绍 静态代理和动态代理(JDK ,cglib代理)
设计模式,一定要敲代码理解
静态代理
被代理实体抽象
public interface Isend {
void toSend();
}
被代理实体实现
public class Send implements Isend {
@Override
public 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;
}
//方法增强
@Override
public 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代理人完成配送");
}
@Override
public 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代理人完成配送");
}
@Override
public 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库的依赖。