目录

    • 一、何时使用代理?
    • 二、创建代理对象
    • 三、代理类的特性
    • 四、代理模式
    • 五、组成
    • 六、优点
      • 1、职责清晰
      • 2、保护对象
      • 3、高扩展性
    • 七、模式结构
    • 八、静态代理
    • 九、动态代理
      • 1、动态代理流程图
      • 2、动态代理代码实现

大家好,我是哪吒。

一、何时使用代理?

利用代理可以在运行时创建实现了一组给定接口的新类,只有在编译时无法确定需要实现哪个接口时菜需要使用代理。

假设要构造一个类的对象,这个类实现了多个接口,但是在编译时并不知道这些接口到底是做什么的。

要想构造一个具体的类,使用newInstance方法找出构造器,但是,不能实例化接口,需要在运行的程序中定义一个新类。

为了解决这个问题,有些程序会生成代码,将这些代码放在一个文件中,调用编译器,然后再加载得到文件。但是,这么操作因为涉及到磁盘IO,会比较慢。

此时,可以通过代理类实现,代理类可以在运行时创建全新的类。

代理类包含以下方法:

  1. 指定接口所需要的全部方法;
  2. Object类中定义的全部方法;

不过,不能在运行时为这些方法定义新代码。必须提供一个调用处理器invocation handler,InvocationHandler只有一个方法invoke。

无论何时调用代理对象的方法,都会调用这个invoke方法,并提供Method对象和原调用的参数。

二、创建代理对象

通过Proxy类的newProxyInstance方法,创建代理对象。

newProxyInstance()包含三个参数:

  1. 类加载器;
  2. Class对象数组,每个元素对应需要实现的各个接口;
  3. 调用处理器;

三、代理类的特性

1、所有的代理类都扩展Proxy类,一个代理类只有一个实例对象(调用处理器),它在Proxy超类中定义,完成代理对象任务所需要的任何额外数据都必须存储在调用处理器中。

2、所有的代理类都覆盖Object类中的toString、equals、hashCode方法。和所有代理方法一样,这些方法只是在调用处理器上调用invoke。Object类的其它方法没有重新定义。

3、对于特定的类加载器和一组接口,只能有一个代理类。如果使用类加载器和接口数组调用多次newProxyInstance方法,将得到同一个类的多个对象,也可以通过getProxyClass方法获得这个类。

4、代理类总是public和final。

5、可以通过调用Proxy类的isProxyClass方法检测一个特定的Class对象是否表示一个代理类。

四、代理模式

代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

著名的代理模式例子为引用计数(英语:reference counting)指针对象。

当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。

五、组成

抽象角色:通过接口或抽象类声明真实角色实现的业务方法。

代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

六、优点

1、职责清晰

真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。

2、保护对象

代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。

3、高扩展性

七、模式结构

一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理对象实现同一个接口,先访问代理类再访问真正要访问的对象。

代理模式分为静态代理、动态代理。

静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

八、静态代理

创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。

使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。

九、动态代理

1、动态代理流程图

2、动态代理代码实现

(1)代理类

利用反射机制在运行时创建代理类。
接口、被代理类不变,我们构建一个ProxyInvocationHandler类来实现InvocationHandler接口。

package com.guor.aop.dynamicproxy;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
public class ProxyInvocationHandler implements InvocationHandler {
	private Object target;
	
	public Object getTarget() {
		return target;
	}
 
	public void setTarget(Object target) {
		this.target = target;
	}
 
	//生成得到代理类
	public Object getProxy() {
		return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
	}
	
	//处理代理实例,并返回结果
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		log(method.getName());
		//动态代理的本质就是使用反射机制来实现
		Object result = method.invoke(target, args);
		return result;
	}
	
	public void log(String msg) {
		System.out.println("执行了"+msg+"方法");
	}
}

通过Proxy类的静态方法newProxyInstance返回一个接口的代理实例。针对不同的代理类,传入相应的代理程序控制器InvocationHandler。

(2)被代理类UserService

package com.guor.aop;
 
public interface UserService {
	public void add();
	public void delete();
	public void update();
	public void query();
}
package com.guor.aop;
 
public class UserServiceImpl implements UserService {
 
	public void add() {
		System.out.println("add");
	}
 
	public void delete() {
		System.out.println("delete");
	}
 
	public void update() {
		System.out.println("update");
	}
 
	public void query() {
		System.out.println("query");
	}
 
}

(3)执行动态代理

package com.guor.aop.dynamicproxy;
 
import com.guor.aop.UserService;
import com.guor.aop.UserServiceImpl;
 
public class Client {
 
	public static void main(String[] args) {
		//真实角色
		UserService userService = new UserServiceImpl();
		
		//代理角色
		ProxyInvocationHandler pih = new ProxyInvocationHandler();
		//通过调用程序处理角色来处理我们要调用的接口对象
		pih.setTarget(userService);
		
		UserService proxy = (UserService) pih.getProxy();
		proxy.update();
	}
 
}

(4)控制台输出


🏆本文收录于,Java基础教程系列。

目前已经700+订阅,CSDN最强Java专栏,包含全部Java基础知识点、Java8新特性、Java集合、Java多线程、Java代码实例,理论结合实战,实现Java的轻松学习。

🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师。

更多推荐

49天精通Java,第18天,Java代理类详解