在学习反射的过程中,有遇到关于动态代理的东西,但是不太了解。经过谷歌、百度爸爸的教导。初入jdk动态代理的大门。发现这其实是个很系统的知识框架,需要了解很多的东西~也意识自己的知识实在实在是太薄弱...时间都被吃鸡给剥夺了,shame。
一、静态代理和动态代理的区别
首先,我们要先知道什么是静态加载和动态加载。
静态加载:静态加载就是程序员写的代码编译后,生成的class文件被JVM加载,该class文件是程序编译后产生,运行之前就已经存在了,是写死的。
动态代理:动态加载就是程序自己可以在运行期间动态的生成的class文件,被JVM加载。不需要程序员去重新编译,是由程序根据代码指示动态生成。
首先我们来谈一下什么是代理?
现在我们是一个游戏供应商(实现类),我们不想自己去卖游戏,所以找到了Steam平台(代理类)发行游戏售卖。这就是代理,那什么是静态代理?什么是动态代理吗?我们就从代码实现中去感受把~
静态代理:
我们是一家游戏公司(GameFirm),实现于公司接口(FirmInterface),拥有卖游戏的功能。
FirmInterface.java(公司接口)
package dynamicproxy; /** * 测试接口,代理类和委托类都必须遵循的接口 * @author SnailMann * */ public interface FirmInterface { public void sellGame(); }GameFirm.java(游戏公司类)
package dynamicproxy; public class GameFirm implements FirmInterface{ /** * 委托类,相当于供应商 */ @Override public void sellGame() { System.out.println("Sell GTA5"); } }StaticProxySteam.java(steam平台)
package dynamicproxy; /** * 静态代理类,静态代理MTest类,相当于代理商。 * @author SnailMann * */ public class StaticProxySteam implements FirmInterface { GameFirm firm=new GameFirm(); @Override public void sellGame() { firm.sellGame(); } }
test.java(主函数)
package dynamicproxy; import java.lang.reflect.Proxy; public class test { public static void main(String[] args) { //静态代理 System.out.println("------------静态代理-------------"); StaticProxySteam spTest=new StaticProxySteam(); spTest.sellGame(); } }
StaticProxySteam.java(steam平台,打广告)
package dynamicproxy; /** * 静态代理类,相当于代理商。 * @author SnailMann * */ public class StaticProxySteam implements FirmInterface { GameFirm firm=new GameFirm(); @Override public void sellGame() { System.out.println("StaticProxy - Advertise for Game "); firm.sellGame(); } }
我们就达到效果了,在卖游戏之前,进行了一波广告宣传
动态代理:
那么问题来了,目前我这个公司只是卖游戏而已。也就是接口里只有一个卖游戏的方法,如果它还卖软件、工具等等,有一百甚至几百个方法的时候。倘若公司卖的其他东西也想让代理商先宣传一波再卖。那就要在所有的方法里都加上宣传的方法。这样代码就很冗余了,多了太多简单重复的代码。所以,动态代理就可以完美的解决这个问题。我们可以把这些重复的代码抽出来。放在一个中间扩展类中。这一点就非常类似Spring的aop思想了。然后经过内部函数的处理,动态生成一个包含中间类方法的接口新实例...然后动态的class文件..jvm加载
为了表示区分,所以我们新建一个公司接口,拥有卖游戏、卖软件、卖工具的功能,当然你也可以更多(jdk动态代理只能实现对接口的操作,所以委托类必须实现接口)
FirmInterface2.java
package dynamicproxy; public interface FirmInterface2 { public void sellGame(); public void sellSoftware(); public void sellTools(); }GameFirm2.java
package dynamicproxy; public class GameFirm2 implements FirmInterface2{ /** * 委托类,被代理类 */ @Override public void sellGame() { System.out.println("Sell GTA5 "); } @Override public void sellSoftware() { System.out.println("Sell Software"); } @Override public void sellTools() { System.out.println("Sell Tool"); } }DynamicProxy.java
package dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class DynamicProxy implements InvocationHandler{ //object存放的是你需要的委托类(被代理类)的实例 private Object object; public DynamicProxy(){} public DynamicProxy(Object object) { this.object=object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /* * proxy传进来的参数,应该是生成的代理类的实例,作用不明,疑似没卵用 * refer to https://www.zhihu.com/question/52551525/answer/132978844 Accelerator */ System.out.println("DynamicProxy - Advertise"); //返回的是该方法的返回值,如果该方法有返回值,就返回返回值,如果是void,则返回null Object result=method.invoke(object, args); return result; } }
这里类就是个重点了,它是实现动态代理的第一步。首先我们定义中间类,必须实现InvocationHandler接口,重写invoke方法。它的第一个参数作用不明,可以百度一下。第二个参数则是根据方法名取得的方法,第三个参数该方法的参数。我们要在GameFirm的所有方法里面加入广告宣传,所以我们在method.invoke(object,args)前加入广告方法就行了,如代码所示。具体它是怎么被调用的,那就要去debug jdk了。它就是一个调用处理器,当代理类的sellGame、sellTools、sellSoftware方法被调用的时候,它会把他们拦截下来,加入需要加入的方法,再放行。
test.java(主函数)
package dynamicproxy; import java.lang.reflect.Proxy; public class test { public static void main(String[] args) { /* * 从里面的思想,我们就能感受到Spring动态代理思想AOP的存在。 * DynamicProxy就是一个中间扩展类的存在,类似于aop思想是想在各个类抽象出的切面上加上想要执行的扩展类。作用就是减少代码冗余,将重复的代码从每个方法中抽出,变成一个具体的中间扩展层 * 再将这个扩展层在各方法的纵向切进去执行 * 应用就比如说,我们要每个方法执行期间都将运行状态加进log,记录方法的开始和结束。或是权限判断,事务管理等等 */ System.out.println("------------动态代理-------------"); DynamicProxy dpTest2=new DynamicProxy(new GameFirm2()); FirmInterface2 firmProxy2=(FirmInterface2)(Proxy.newProxyInstance(FirmInterface2.class.getClassLoader(),GameFirm2.class.getInterfaces(),dpTest2)); firmProxy2.sellGame(); firmProxy2.sellSoftware(); firmProxy2.sellTools(); } }
主函数的重点就是Proxy.newProxyInstance()方法,作用就是由程序自己根据代码提示动态生成一个类,也就是加了中间方法之后的类。第一个参数是委托类所实现的接口的ClassLoader. 第二个参数就是委托类所实现的接口列表,第三个参数就是我们定义的中间类实例。返回的就是被处理好的代理类的实例,就相当于程序已经动态帮你生成好了代理商的具体实例。这一点就很类似于Spring容器对对象的管理。由程序来实例化,而不是程序员自己。
其中我发现又很多不懂地方,比如说ClassLoader是什么,这就看后面的更新后续了~
大家可以参考这里:从代理模式再出发!ClassLoader初探
区别:
动态代理就是不需要像静态代理一样,写死一个代理类,而是将重复代码写入中间类,抽象出来,再通过反射的方法动态生成我们想要的代理类实例。
参考网站:
总结就到这里啦~~~