1.静态代理
- 定义真实对象和代理对象的公共接口
- 代对象内部保存对真实目标对象的引用
- 访问者仅能通过代理对象访问真实目标对象,不可直接访问目标对象
1.1代理模式的定义
给目标对象提供一个代理对象,代理对象包含该目标对象,并控制对该目标对象的访问。
1.2代理模式的目的:
- 通过代理对象的隔离,可以在对目标对象访问前后增加额外的业务逻辑,实现功能增强。
- 通过代理对象访问目标对象,可以防止系统大量地直接对目标对象进行不正确地访问,出现不可预测的后果
1.3静态代理的缺点
- 静态代理违反了开闭原则,面对新的需求时,需要修改代理类,增加实现新的接口和方法,导致代理类越来越庞大,变得难以维护。
2.动态代理
动态代理解决的问题是面对新的需求时,不需要修改代理对象的代码,只需要新增接口和真实对象,在客户端调用即可完成新的代理。
2.1JDK Proxy实现动态代理
JDK Proxy 是 JDK 提供的一个动态代理机制,它涉及到两个核心类,分别是
Proxy
和InvocationHandler
动态代理和静态代理区别不大,唯一的变动是代理对象,由代理工厂生产。
- 如何实现一个代理工厂
- 如何通过代理工厂动态生成代理对象
//实现一个代理工厂
// 自定义 InvocationHandler (也可以看做是代理)
public class SellProxyFactory implements InvocationHandler {
/** 代理的真实对象 */
private Object realObject;
public SellProxyFactory(Object realObject) {
this.realObject = realObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doSomethingBefore();
Object obj = method.invoke(realObject, args);
doSomethingAfter();
return obj;
} private void doSomethingAfter() {
System.out.println("执行代理后的额外操作...");
} private void doSomethingBefore() {
System.out.println("执行代理前的额外操作...");
}
}
- Object proxy:代理对象
- Method method:真正执行的方法
- Object[] agrs:调用第二个参数 method 时传入的参数列表值
如何通过代理工厂动态生成代理对象。
- 生成代理对象需要用到Proxy类,它可以帮助我们生成任意一个代理对象,里面提供一个静态方法newProxyInstance。
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
实例化代理对象时,需要传入3个参数:
//通过代理工厂动态生成代理对象
public class XiaoMing {
public static void main(String[] args) {
//真实对象和代理对象的公共接口
ChanelFactory chanelFactory = new ChanelFactory();
//代理类的调用处理程序,即调用接口中的方法时,会找到该代理工厂h,执行invoke()方法
SellProxyFactory sellProxyFactory = new SellProxyFactory(chanelFactory);
//代理工厂获取代理对象,这个对象是动态生成的,没有java源文件
SellPerfume sellPerfume = (SellPerfume) Proxy.newProxyInstance(
chanelFactory.getClass().getClassLoader(),
chanelFactory.getClass().getInterfaces(),
sellProxyFactory);
sellPerfume.sellPerfume(1999.99);
}
}
- ClassLoader loader:加载动态代理类的类加载器
- Class<?>[] interfaces:代理类实现的接口,可以传入多个接口
- InvocationHandler h:指定代理类的调用处理程序,即调用接口中的方法时,会找到该代理工厂h,执行invoke()方法。
JDK 动态代理的使用方法
- 代理工厂需要实现 InvocationHandler接口,调用代理方法时会转向执行invoke()方法
- 生成代理对象需要使用Proxy对象中的newProxyInstance()方法,返回对象可强转成传入的其中一个接口,然后调用接口方法即可实现代理
JDK 动态代理的特点
- 目标对象强制需要实现一个接口,否则无法使用 JDK 动态代理