静态代理和动态代理
1. 概述
代理对象可以在调用者和目标对象之间起中介作用,代理对象可以对目标对象的功能进行改写或增强。在java代码中可以体现为不修改源代码而对源代码进行改写。以达到代理作用。
1.1 分类
- 静态代理
- 动态代理
1.2 四要素
- 调用者: 你。
- 代理对象: 联想电脑代理商/黄牛。
- 目标对象: 电脑工厂/12306。
- 抽象对象: 代理对象和目标对象都共有的接口,保证代理对象和真实对象都有相应的方法,如电脑代理商和电脑工厂都需要有卖电脑的功能。
2. 静态代理
由程序员创建代理类,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
案例:卖电脑
- 抽象对象:Providable接口
/**
* 提供商品的接口
*/
public interface Providable{
// 卖电脑
void sellComputer(double price);
// 维修电脑
void repairComputer(double price);
}
- 真实对象:ComputerFactory类
public class ComputerFactory implements Providable {
@Override
public void sellComputer(double price) {
System.out.println("电脑工厂卖出一台电脑,价格: " + price);
}
@Override
public void repairComputer(double price) {
System.out.println("电脑工厂修好一台电脑,价格: " + price);
}
}
- 静态代理对象:ComputerSeller类
public class ComputerSeller implements Providable {
private Providable provider;
ComputerSeller(Providable provider){
this.provider = provider;
}
@Override
public void sellComputer(double price) {
double realPrice = price * 0.5;
System.out.println("推销员获得一台电脑,进价: " + realPrice);
System.out.println("推销员卖出一台电脑,价格: " + price);
}
@Override
public void repairComputer(double price) {
double realPrice = price * 2;
System.out.println("推销员拿电脑给电脑工厂维修,收取顾客费用: " + realPrice);
System.out.println("电脑工厂修好一台电脑,价格: " + price);
}
}
- 调用者:Customer类
public class Customer {
public static void main(String[] args) {
//创建真实对象
Providable provider = new ComputerFactory();
//创建代理对象:注入真实对象
Providable proxy = new ComputerSeller(provider);
//调用方法
proxy.sellComputer(2000);
proxy.repairComputer(1200);
}
}
缺点
- 一个真实对象必须对应一个代理对象,如果大量使用会导致类的急剧膨胀。
- 如果抽象对象中方法很多,则代理对象也要编写大量的代码。
3. 动态代理
在运行时动态地创建代理对象。不必自己创建类对象,代码量较少,可以弥补静态代理对象的不足。
3.1 相关api
Proxy类中的方法
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
作用:动态生成代理对象
ClassLoader loader 与真实对象相同的类加载器
Class[] interfaces 代理类或真实类所有实现的接口
InvocationHandler h 调用代理对象的接口,需要重写接口中的方法,实现真实对象中每个方法的调用。
返回Object 也就是生成的代理对象
参数 | 说明 |
---|---|
loader | 与真实对象相同的类加载器 |
interfaces | 代理类或真实类所有实现的接口。 |
h | 调用代理对象的接口,需要重写接口中的方法,实现真实对象中每个方法的调用。 |
返回Object | 也就是生成的代理对象 |
InvocationHandler接口的invoke方法
Object invoke(Object proxy, Method method, Object[] args)
作用:接口中这个方法会调用多次,真实对象中的每个代理方法都会调用一次
参数 | 说明 |
---|---|
proxy | 动态生成的代理对象,不要在方法中直接调用,不然会出现递归死循环的调用。 |
method | 真实对象的方法。 |
args | 代理对象调用方法时传递的参数。 |
返回Object | 方法的返回值。 |
3.2 开发步骤
- 首先需要存在抽象对象,定义所有的功能
- 真实对象实现抽象对象所有的功能
- 通过Proxy类,创建代理对象,调用代理方法
- 在InvocationHandler的invoke对代理的方法有选择的修改或不修改真实对象原有的方法
3.3 改写卖电脑案例
- 抽象对象:Providable接口
package com.zmysna.proxy;
public interface Providable{
// 卖电脑
void sellComputer(double price);
// 维修电脑
void repairComputer(double price);
}
- 真实对象:ComputerFactory类
package com.zmysna.proxy;
public class ComputerFactory implements Providable {
@Override
public void sellComputer(double price) {
System.out.println("电脑工厂卖出一台电脑,价格: " + price);
}
@Override
public void repairComputer(double price) {
System.out.println("电脑工厂修好一台电脑,价格: " + price);
}
}
- 调用者:Customer类 (调用者中动态创建对象)
package com.zmysna.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Objects;
public class Customer {
public static void main(String[] args) {
//创建真实对象
Providable provider = new ComputerFactory();
//创建动态代理对象,对sellComputer方法进行重写。
Providable proxy = (Providable) Proxy.newProxyInstance(provider.getClass().getClassLoader(), new Class[]{Providable.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//重写sellComputer方法
if(Objects.equals(method.getName(),"sellComputer")){
double price = (double)args[0];
double realPrice = price * 0.5;
System.out.println("推销员获得一台电脑,进价: " + realPrice);
System.out.println("推销员卖出一台电脑,价格: " + price);
return null;
}else return method.invoke(provider,args); //其他方法正常执行
}
});
proxy.sellComputer(100);
proxy.repairComputer(50);
}
}
运行结果
推销员获得一台电脑,进价: 50.0
推销员卖出一台电脑,价格: 100.0
电脑工厂修好一台电脑,价格: 50.0