java中的代理(静态代理和动态代理)

之前本人在设计模式中有写过静态代理和动态代理的相关代码测试,可以看下。
今天我们主要学一下理论相关知识。

AOP的原理就是动态代理机制。RPC框架也是实现了AOP机制。

静态代理:由程序员创建或工具生成代理类的源码,再编译代理类,即代理类和委托类的关系再程序运行前就已经存在。
动态代理:在运行期间使用动态生成字节码形式,动态创建代理类。使用的工具有 jdkproxy、cglibproxy 等。

java中的代理(静态代理和动态代理)_第1张图片

静态代理

静态代理的好处

  1. 可以隐藏委托类的具体实现;
  2. 可以在不改变委托类的情况下增加额外的操作。

静态代理:在代码编译时就确定了被代理的类是哪一个。

这个静态代理比较简单,代理类和被代理类实现了同一接口,在代理类的构造函数中定义一个被代理类的对象即可。
Student类:实现Person接口(接口中有sayHello()方法)。
被代理类和Person类就比较简单,这里就不附上源码了。

代理类:StudentProxy类 实现Person接口

/**
 * 静态代理,这个代理类也必须要实现和被代理类相同的Person接口
 *
 */
public class ProxyTest implements Person{	
	private Person o;	
	public ProxyTest(Person o){
		this.o = o;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//s为被代理的对象,某些情况下 我们不希望修改已有的代码,我们采用代理来间接访问
		Student s = new Student();
		//创建代理类对象
		ProxyTest proxy = new ProxyTest(s);
		//调用代理类对象的方法
		proxy.sayHello("welcome to java", 20);
	}
 
	@Override
	public void sayHello(String content, int age) {
		// TODO Auto-generated method stub
		System.out.println("ProxyTest sayHello begin");
		//在代理类的方法中 间接访问被代理对象的方法
		o.sayHello(content, age);
		System.out.println("ProxyTest sayHello end");
	}
}

动态代理

动态代理:java的动态代理机制中,有两个重要的类或者接口,一个是InvocationHandler(Interface),还有一个是Proxy(是一个class)。

其中InvocationHandler是每一个动态代理类都必须要实现的接口,我们通过代理对象调用一个方法的时候,该方法就会被转发由InvocationHandler这个接口的invoke方法来调用。
Proxy类,动态的创建一个代理对象的类,它提供了许多方法,我们用的最多的是newProxyInstance方法,该方法的作用就是得到一个动态的代理对象。

动态代理模式主要由四个元素共同组成:
1.接口:定义具体实现的方法
2.被代理类:实现上述接口,执行接口中的方法
3.代理类:实现InvocationHandler,帮助被代理类实现方法。

代理类中要实现的内容有:

  1. 因为动态代理不知道被代理的类是哪一个,所以在实现了InvocationHandler的代理类中定义了一个Object类,在代理类的构函数中作为参数传递进来。
  2. 实现InvocationHandler中的invoke方法。
  3. 写main方法,并在main方法中根据定义的被代理类实现代理类的生成。

动态代理的步骤:

1. 首先获得一个被代理对象的引用,

2. 获得该引用的接口

3. 生成一个类,这个类实现了我们给的代理对象所实现的接口

4. 上述类编译生成了.class字节码供JVM使用

5. 调用上述生成的class

/**
 * 动态代理类
 * @author Martina
 *
 */
public class MyInvocationHandler implements InvocationHandler{
	
	private Object object;
	
	public MyInvocationHandler(Object object){
		this.object = object;
	}
 
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("MyInvocationHandler invoke begin");
		System.out.println("proxy: "+ proxy.getClass().getName());
		System.out.println("method: "+ method.getName());
		for(Object o : args){
			System.out.println("arg: "+ o);
		}
		//通过反射调用 被代理类的方法
		method.invoke(object, args);
		System.out.println("MyInvocationHandler invoke end");
		return null;
	}
	
	public static void main(String [] args){
		//创建需要被代理的类
		Student s = new Student();
		//这一句是生成代理类的class文件,前提是你需要在工程根目录下创建com/sun/proxy目录,不然会报找不到路径的io异常
		System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
		//获得加载被代理类的 类加载器
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		//指明被代理类实现的接口
		Class[] interfaces = s.getClass().getInterfaces();
		// 创建被代理类的委托类,之后想要调用被代理类的方法时,都会委托给这个类的invoke(Object proxy, Method method, Object[] args)方法
		MyInvocationHandler h = new MyInvocationHandler(s);
		//生成代理类
		//注意newProxyInstance的三个参数所代表的含义
		Person proxy = (Person)Proxy.newProxyInstance(loader, interfaces, h);
		//通过代理类调用 被代理类的方法
		proxy.sayHello("yujie.wang", 20);
		proxy.sayGoodBye(true, 100);
		System.out.println("end");
	}
 
}

上述代码中的Proxy.newProxyInstance所创建的代理类的位置和怎么输出其名字:
(6)反编译Proxy.newProxyInstance所创建的代理类

//这一句是生成代理类的class文件,前提是你需要在工程根目录下创建com/sun/proxy目录,不然会报找不到路径的io异常
System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,“true”);

我们在代码中加入上述代码,代码就会保存生成的代理类,名称为$Proxy0.class

java中的代理(静态代理和动态代理)_第2张图片
 对于生成的 P r o x y 0. c l a s s 文 件 , 可 以 用 j a v a 反 编 译 工 具 j d − g u i − 0.3.3. w i n d o w s 查 看   要 记 住 , 通 过 P r o x y . n e w P r o x y I n s t a n c e 创 建 的 代 理 对 象 是 在 j v m 运 行 时 动 态 生 成 的 一 个 对 象 , 它 并 不 是 我 们 的 I n v o c a t i o n H a n d l e r 类 型 , 也 不 是 我 们 定 义 的 那 组 接 口 的 类 型 , 而 是 在 运 行 是 动 态 生 成 的 一 个 对 象 , 并 且 命 名 方 式 都 是 这 样 的 形 式 , 以 Proxy0.class文件,可以用java反编译工具jd-gui-0.3.3.windows查看   要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以 Proxy0.classjavajdgui0.3.3.windows Proxy.newProxyInstancejvmInvocationHandler开头,proxy为中,最后一个数字表示对象的标号。

代理:https://www.cnblogs.com/xiaoluo501395377/p/3383130.html
https://blog.csdn.net/u011784767/article/details/78281384
https://www.cnblogs.com/lfdingye/p/7717063.html

你可能感兴趣的:(设计模式)