文章目录
一、静态代理
1.1 角色分析
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户(Client):访问代理对象的人!
1.2 代理模式的优点和缺点
代理模式的优点:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共业务就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
代理模式的缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会变低
1.3 代码实例 一(客户、中介、房东)
接下来我们就用实例来讲解下具体的用法
以 客户(Client)、中介(Proxy)、房东(Host)为例详细说明
- 首先定义一个 Rent 接口
public interface Rent {
// 定义租房接口
public void rent();
}
- 定义房东 Host 类
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子!!!");
}
}
- 定义中介 Proxy 类
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
seeHouse();
host.rent();
hetong();
fare();
}
//看房
public void seeHouse(){
System.out.println("中介带你看房");
}
//签合同
public void hetong(){
System.out.println("签租赁合同");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
- 实现客户 Client 类
public class Client {
public static void main(String[] args) {
//房东要租房子
Host host=new Host();
//代理,中介帮房东租房子,但是呢?代理角色一般会有一些附属操作!
Proxy proxy = new Proxy(host);
//你不用面动房东,直接找中介租房即可!
proxy.rent();
}
}
- 查看结果
二、动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口—JDK 动态代理【我们在这里使用】
- 基于类:cglib
- java字节码实现:javasist
需要了解两个类:Proxy(代理),InvocationHandler(调用处理程序)
2.1 InvocationHandler(接口)和Proxy(类)[重要!]
1. InvocationHandler 接口是 proxy 代理实例的调用处理程序实现的一个接口,每一个 proxy 代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的 invoke 方法:
/**
* proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* method:我们所要调用某个对象真实的方法的Method对象
* args:指代代理对象方法传递的参数
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
2. Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
这个方法的作用就是创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:
- loader:一个 classloader 对象,定义了由哪个 classloader 对象对生成的代理类进行加载
- interfaces:一个 interface 对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
- h:一个 InvocationHandler 对象,表示的是当动态代理对象调用方法的时候会关联到哪一个 InvocationHandler 对象上,并最终由其调用。
直接看,会有点难理解,下面通过两个实例具体了解一下动态代理
2.2 代码实例 一(客户、中介、房东)
- 定义 Rent 接口
public interface Rent {
public void rent();
}
- 定义房东 Host 对象
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子!!!");
}
}
- 定义中介 ProxyInvocationHandler 对象
// 自动实现代理类
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口
private Object target;
public void setRent(Rent rent) {
this.target = rent;
}
// 生成得到的代理类
// Proxy类就是用来创建一个代理对象的类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this );
}
// 处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 动态代理的本质:就是使用反射机制实现
Object result = method.invoke(target, args);
return result;
}
}
- 实现客户 Client 对象
public class Client {
public static void main(String[] args) {
// 真实角色
Host host = new Host();
// 代理角色:现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
// 通过调用程序处理角色 -- 向代理角色传入真实角色
pih.setRent(host);
// 创建代理对象
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
2.3 代码实例 二(People、Teacher、WorkHandler)
- 定义 People 接口
public interface People {
public void work();
}
- 定义 Teacher 对象
public class Teacher implements People{
@Override
public void work() {
System.out.println("老师教书育人..");
}
}
- 定义 WorkHandler 对象
public class WorkHandler implements InvocationHandler {
//代理类中的真实对象
private Object obj;
public WorkHandler() {
}
//构造函数,给我们的真实对象赋值
public WorkHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在真实的对象执行之前我们可以添加自己的操作
System.out.println("before invoke。。。");
Object invoke = method.invoke(obj, args);
//在真实的对象执行之后我们可以添加自己的操作
System.out.println("after invoke。。。");
return invoke;
}
}
- 实现 Test 对象
public class Test {
public static void main(String[] args) {
//要代理的真实对象
People people = new Teacher();
//代理对象的调用处理程序,我们将要代理的真实对象传入代理对象的调用处理的构造函数中,最终代理对象的调用处理程序会调用真实对象的方法
InvocationHandler handler = new WorkHandler(people);
/**
* 通过Proxy类的newProxyInstance方法创建代理对象,我们来看下方法中的参数
* 第一个参数:handler.getClass().getClassLoader(),使用handler对象的classloader对象来加载我们的代理对象
* 第二个参数:people.getClass().getInterfaces(),这里为代理类提供的接口是真实对象实现的接口,这样代理对象就能像真实对象一样调用接口中的所有方法
* 第三个参数:handler,我们将代理对象关联到上面的InvocationHandler对象上
*/
People proxy = (People) Proxy.newProxyInstance(handler.getClass().getClassLoader(), people.getClass().getInterfaces(), handler);
//System.out.println(proxy.toString());
proxy.work();
}
}
2.4 动态代理的优点
动态代理的优点:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共业务就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就算对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!
2.5 补充说明
通过上面的讲解和示例动态代理的原理及使用方法,在 Spring 中的两大核心 IOC 和 AOP 中的 AOP (面向切面编程)的思想就是动态代理,在代理类的前面和后面加上不同的切面组成面向切面编程。
上面我们只讲解了 Proxy 中的 newProxyInstance(生成代理类的方法),但是它还有其它的几个方法,我们下面就介绍一下:
- getInvocationHandler:返回指定代理实例的调用处理程序
- getProxyClass:给定类加载器和接口数组的代理类的 java.lang.Class 对象。
- isProxyClass:当且仅当使用 getProxyClass 方法或 newProxyInstance 方法将指定的类动态生成为代理类时,才返回true。
- newProxyInstance:返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。