【Java反射学习笔记系列之jdk动态代理】静态代理和动态代理的区别以及动态代理的作用和实现

在学习反射的过程中,有遇到关于动态代理的东西,但是不太了解。经过谷歌、百度爸爸的教导。初入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();		
	}

}



这就是一个静态代理所用到的几个类,接口、委托类、代理类。在这里,游戏公司是通过steam平台来售卖GTA5游戏。如果是我想要在卖游戏之前进行一波游戏的宣传,炒作炒作,提高游戏知名度,怎么办?那我就需要让代理商Steam平台把GTA5的游戏推上平台首页,达到推广宣传的效果。所以我们需要修改StaticProxySteam.java这个类,不需要修改GameFirm.java来达到不修改委托类就可以完成一些功能,达到一定的解耦作用,这就是代理的作用。

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初探


区别:

动态代理就是不需要像静态代理一样,写死一个代理类,而是将重复代码写入中间类,抽象出来,再通过反射的方法动态生成我们想要的代理类实例。


参考网站:

Java帝国之动态代理

十分钟理解Java中的动态代理

总结就到这里啦~~~


猜你喜欢

转载自blog.csdn.net/snailmann/article/details/79187635