从源码解析代理模式
Posted 碎格子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从源码解析代理模式相关的知识,希望对你有一定的参考价值。
大纲
代理模式(结构型设计模式)通过代理类去访问实现类中的方法,使用场景比如:已有接口和实现类的情况下,想要在已实现的方法基础上扩展更多的功能的场景。
代理模式里的主要类:
接口
实现类,需实现接口,用来完成真正的业务。(网上资料也叫做委托类、被代理类)
代理类,也需要实现接口,调用实现类的方法,本身不处理业务。
使用代理模式的好处:可以扩展实现类的功能,在实现类基本功能基础上增加一些额外的操作。
静态代理模式
图示:
要想做到在原来实现方法的基础上多增加功能,代理类同样也需要实现接口,并且需要持有一个实现类,在自己实现的方法中扩展功能,并且调用实现类的方法去处理真正的业务:
public class TV
String name;
String model;
public String getModel()
return model;
public String getName()
return name;
public void setModel(String model)
this.model = model;
public void setName(String name)
this.name = name;
public TV(String name,String model)
this.name = name;
this.model = model;
@Override
public String toString()
return "TV" +
"name='" + name + '\\'' +
", model='" + model + '\\'' +
'';
实体类TV.java
public interface ICompany
TV produceTV();
接口ICompany.java
实现类需要实现ICompany接口:
/**
* 实现类
*/
public class CompanyFactory implements ICompany
@Override
public TV produceTV()
System.out.println("生产商生产一台电视");
return new TV("TCL","40寸");
代理类也需要实现ICompany接口:
/**
* 代理类
* 代理类和实现类都要实现接口
*/
public class TVProxy implements ICompany
ICompany company;
@Override
public TV produceTV()
System.out.println("代理收到一笔订单");
if (Objects.isNull(company))
company = new CompanyFactory();
return company.produceTV();
CompanyFactory.java
运行时构建代理类,调用代理类的方法同时也调用了实体类的实现方法:
public class MainClass
public static void main(String[] args)
ICompany company = new TVProxy();
TV tv = company.produceTV();
System.out.println(tv.toString());
总结:
静态代理通过创建代理类去实现接口,在实现方法里添加额外功能,从而实现不改变原有实现方法下做到功能扩展。
动态代理模式
静态代理有什么缺点?(先别看下面内容思考一下)
静态代理的局限在于,如果接口新增了方法,则代理类必须实现该方法,而使用动态JDK代理,代理类不用实现接口。
实体类TV不变,接口增加维修方法,如果是静态代理,接口新增方法,则对应的实现类和代理类都要实现这个方法,而动态代理则在代码编写层面不用实现接口的所有方法:
图示:
接口新增repairTV方法:
public interface ICompany
TV produceTV();
void repairTV(String name); //新增维修电视的方法
实现类需要实现这个方法:
public class CompanyFactory implements ICompany
@Override
public TV produceTV()
System.out.println("生产商生产一台电视");
return new TV("TCL", "40寸");
@Override
public void repairTV(String name)
System.out.println(String.format("生产商维修%s电视",name));
这里是动态代理类,可以看到,这里并没有实现ICompany接口了
public class TVDynamicProxy
Object factory;
public TVDynamicProxy(Object factory)
this.factory = factory;
public <T> T getProxy(Class<T> clazz)
//newProxyInstance实际上是返回了一个实现了接口的代理类实例,这个实例是存在在内存中的,动态生成的:
return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]clazz,
new InvocationHandler()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable //回调
System.out.println("动态代理执行:接收到一笔订单");
Object tv = method.invoke(factory, args); //这里通过反射调用了factory里实现的方法
System.out.println("动态代理结束");
return tv;
);
TVDynamicProxy.java
上面方法中newProxyInstance实际上是返回了一个实现了接口的代理类实例,这个实例是存在在内存中的,动态生成的
public class MainClassB
public static void main(String[] args)
ICompany company = new CompanyFactory();
//将生成的代理类写到本地:
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); //将内存中的$Proxy0.class写到本地
ICompany company1 = new TVDynamicProxy(company).getProxy(ICompany.class);
TV tv = company1.produceTV();
company1.repairTV(tv.name);
代码运行查看结果
运行后断点可以看到,这里实际上返回了$Proxy0
$Proxy0实现了接口,并实现了接口里定义的方法。
所以先前我们说,是在代码编写层面上不用去实现接口,实际是JDK帮我们自动实现了。
public final class $Proxy0 extends Proxy implements ICompany
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws
super(var1);
public final boolean equals(Object var1) throws
try
return (Boolean)super.h.invoke(this, m1, new Object[]var1);
catch (RuntimeException | Error var3)
throw var3;
catch (Throwable var4)
throw new UndeclaredThrowableException(var4);
//自动实现了接口里定义的方法
public final TV produceTV() throws
try
return (TV)super.h.invoke(this, m3, (Object[])null); //这里回调了InvocationHandler的invoke方法
catch (RuntimeException | Error var2)
throw var2;
catch (Throwable var3)
throw new UndeclaredThrowableException(var3);
public final String toString() throws
try
return (String)super.h.invoke(this, m2, (Object[])null);
catch (RuntimeException | Error var2)
throw var2;
catch (Throwable var3)
throw new UndeclaredThrowableException(var3);
public final void repairTV(String var1) throws
try
super.h.invoke(this, m4, new Object[]var1);
catch (RuntimeException | Error var3)
throw var3;
catch (Throwable var4)
throw new UndeclaredThrowableException(var4);
public final int hashCode() throws
try
return (Integer)super.h.invoke(this, m0, (Object[])null);
catch (RuntimeException | Error var2)
throw var2;
catch (Throwable var3)
throw new UndeclaredThrowableException(var3);
...
$Proxy0.class
这个$Proxy0生成到了我们的根目录下面
总结:
动态代理在运行后会在内存中创建一个实现了接口的代理类,通过调用代理类的实现方法,回调到InvocationHandler接口的invoke方法,通过反射拿到实现类的方法,并进行调用。
思考:
如果我新建一个工厂类,不实现ICompany接口,那是否可以代理成功?
public class FactoryB
public TV produceTVB()
System.out.println("B工厂生产电视");
return new TV("华为电视机", "南京");
上面问题可以使用Cglib代理解决。有兴趣的同学可以看给出的参考链接自行拓展。
参考资料:
https://segmentfault.com/a/1190000040407024
以上是关于从源码解析代理模式的主要内容,如果未能解决你的问题,请参考以下文章
从Mapper到JavaBean源码层面解析ResultMap是怎么映射的
深入浅出SpringCloud原理及实战「Netflix系列之Fegin」从源码层面让你认识Feign工作流程和运作机制