目录
1.概述
- 代理模式指: 为一个对象提供一个替身,以控制对这个对象的访问。既通过代理对象访问目标对象,这样做的好处是: 可以在目标对象实现的基础上,增加额外的功能操作,即扩展目标对象的功能。
- 代理模式分为: 静态代理模式和动态代理模式(JDK 和CGLIB)。
2. 静态代理模式
- 概述: 静态代理由开发者对抽象主题编写相关代理类实现,编译之后生成代理类的class.
- 使用: 静态代理在使用时,需要定义接口或者父类, 目标对象与代理对象一起实现接口或者继承共同的父类。
- 实例: 以外卖骑手为例,骑手代理顾客执行购买,等待,取餐的操作
2.1基于接口的静态代理
- Foodie 品尝接口(抽象主题)
/**
* 美食家---> 抽象主题: 静态代理
*/
public interface Foodie {
/**
* 品尝美食
*/
void eat();
}
- RealFoodie 真实品尝类(真实主题)
/**
* 真实美食家---> 真实主题: 静态代理
*/
public class RealFoodie implements Foodie{
@Override
public void eat() {
System.out.println("开始品尝美食....");
}
}
- ProxyFactory 代理工厂
/**
* 重写Foodie接口的方法
*/
@Override
public void eat() {
//增加扩展功能
System.out.println("开始购买...");
System.out.println("开始等待...");
System.out.println("开始配送");
//调用目标对象方法
foodie.eat();
}
- Test 测试类
public class Test {
public static void main(String[] args) {
//1.静态代理---> 基于接口
RealFoodie realFoodie=new RealFoodie();
//填入目标对象
Foodie foodie=new ProxyFactory(realFoodie);
foodie.eat();
}
}
- 输出结果
开始购买....
开始等待....
开始配送....
开始品尝....
总结 : 以上代码可以看出: ProxyFactory代理工厂替目标对象扩展了骑手配送的功能。目标对象没有作任何更改,因此目标对象和扩展功能互不影响,职责分明。
注意 : 目标对象和代理工厂都要实现抽象主题, 这是基于接口的一种方式。
2.2基于继承的静态代理
- 使用场景: 在代理工厂不想实现接口去扩展功能时,就可以使用继承的方式(即继承和真实主题共有的父类或直接继承真实主题)。
- Foodie 抽象主题(和上面一样没做任何改动)
- RealFoodie 真实主题(和上面一样没做任何改动)
- ProxyFactory 代理工厂
/**
* 代理工厂直接继承真实主题--->即目标对象
*/
public class ProxyFactory extends RealFoodie{
/**
* 重写父类的方法
*/
@Override
public void eat() {
//扩展功能
System.out.println("开始购买....");
System.out.println("开始等待....");
System.out.println("开始配送....");
//调用父类(即目标对象)的方法
super.eat();
}
}
2.3优缺点
优点:
- 可以在不修改目标对象的前提下,能通过代理对象对目标功能进行扩展。
- 目标对象和代理的扩展功能职责清晰不会产生耦合。
缺点:
- 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。
- 一旦接口增加方法,目标对象与代理对象都要维护。
3.动态代理模式
- 概述: 动态代理是在运行时动态生成的,通过反射动态生成代理类字节码。
- 分类: 动态代理模式可分为JDK动态代理(基于接口),CGlib(继承方式)。
3.1JDK 动态代理
- 实现:目标对象实现抽象接口,代理方法逻辑类实现InvocationHandler调用处理器接口。
- Foodie 抽象主题
/**
* 美食家--->抽象主题: JDK动态代理
*/
public interface Foodie{
void eat();
}
- RealFoodie 真实主题
/**
* 真实主题---> JDK动态代理
*/
public class RealFoodie implements Foodie{
/**
* 品尝美食
*/
@Override
public void eat() {
System.out.println("开始品尝....");
}
}
- MethodInvocationHandler 方法逻辑调用处理类
该类用于处理代理方法逻辑及或利用反射创建代理对象:
/**
* 实现代理方法逻辑类----> JDK 动态代理
*/
public class MethodInvocationHandler implements InvocationHandler {
//定义未知目标对象
private Object target;
public MethodInvocationHandler(Object target) {
this.target = target;
}
/*
* 创建代理工厂
**/
public Object getInstance()
{
/**
* ClassLoader loader, 目标对象的类加载器
* Class<?>[] interfaces, 目标对象所实现的所有接口
* InvocationHandler h 代理的方法逻辑自动调用重写的invoke
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new MethodInvocationHandler(target));
}
/*
* 处理代理的方法逻辑
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//扩展新功能
System.out.println("开始购买...");
System.out.println("开始等待...");
System.out.println("开始送餐...");
//调用目标对象的方法
Object invoke = method.invoke(target, args);
return invoke;
}
}
- Test测试类
public class Test {
public static void main(String[] args) {
//创建真实主题
RealFoodie realFoodie=new RealFoodie();
//创建代理方法逻辑
MethodInvocationHandler methodInvocationHandler=new MethodInvocationHandler(realFoodie);
//创建代理对象--->填入目标对象
Foodie foodie= (Foodie) new MethodInvocationHandler(realFoodie).getInstance();
foodie.eat();
}
}
3.2 CGlib 动态代理
- 前置操作: 引入CGlib的jar包。
- 使用场景:目标对象不想实现接口时,CGlib使用继承来实现。
- 实现: 代理方法拦截类实现MethodInterceptor 即方法拦截接口。
- MethodInterceptor 代理方法拦截类
该类用于创建代理对象并处理代理方法逻辑(对目标对象方法进行拦截) :
/**
* 代理方法拦截类---> CGlib动态代理
*/
public class MethodInterceptor implements net.sf.cglib.proxy.MethodInterceptor {
//目标对象
private Object target;
public MethodInterceptor(Object target) {
this.target = target;
}
/**
* 创建代理对象
*/
public Object createInstance()
{
//1.设置工具类
Enhancer enhancer=new Enhancer();
//2.设置父类
enhancer.setSuperclass(target.getClass());
//3.设置回调函数
enhancer.setCallback(this);
//4.创建代理对象
Object o = enhancer.create();
return o;
}
/**
* 写入拦截扩展代码
* @param o 目标对象
* @param method 所扩展目标对象的方法
* @param objects 方法参数
* @param methodProxy 代理对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//扩展功能
System.out.println("开始购买...");
System.out.println("开始等待...");
System.out.println("开始送餐...");
//调用父类指定方法(拦截目标对象的方法)
Object invoke =methodProxy.invokeSuper(o,objects);
return invoke;
}
}
- RealFoodie 真实主题类
/**
* 真实主题---->CGlib动态代理
*/
public class RealFoodie {
void eat()
{
System.out.println("开始品尝...");
}
}
- Test测试类
public class Test {
public static void main(String[] args) {
//创建真实主题
RealFoodie realFoodie = new RealFoodie();
//创建代理方法拦截器并创建代理对象
RealFoodie realFoodie1 = (RealFoodie) new MethodInterceptor(realFoodie).createInstance();
realFoodie1.eat();
}
}
3.3优缺点
优点:
- 如扩展功能类似时,代理工厂类对象是未知的,就可以使用一个代理对象完成扩展功能,使用时指定不同目标对象即可。
- 代理工厂无需实现抽象接口的方法,因为是利用反射在运行时创建的代理对象。
缺点:
- CGlib : 通过继承的方式进行代理,无法处理被final修饰类的情况。
- JDK: 只能基于接口处理。