代理模式
代理模式分为静态代理和动态代理,核心的功能就是方法增强。
通过代理可以在不修改原有的代码的情况下拓展该方法。由代理对象去调用目标对象。
1. 静态代理
静态代理角色分析
- 抽象角色: 一般使用接口或者抽象类来实现
- 真实角色: 目标对象,被代理的角色
- 代理角色: 代理真实角色;代理真实角色后,还可以增加一些真实角色的操作
- 客户: 使用代理角色去实现一些操作
代码实现
抽象角色
public interface BuyHouse {
void buyHouse();
}
真实角色
public class BuyHouseImpl implements BuyHouse {
@Override
public void buyHouse() {
System.out.println("我要买房");
}
}
代理角色
public class BuyHouseProxy implements BuyHouse {
//代理需要持有被代理的对象
private BuyHouse buyHouse;
public BuyHouseProxy(BuyHouse buyHouse){
this.buyHouse = buyHouse;
}
@Override
public void buyHouse() {
//在不改变真实对象的前提下增加功能
System.out.println("买房前准备");
//调用真实对象的方法
buyHouse.buyHouse();
System.out.println("买房后装修");
}
}
客户
public class ProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
buyHouse.buyHouse();
System.out.println("============================");
BuyHouseProxy proxy = new BuyHouseProxy(buyHouse);
proxy.buyHouse();
}
}
结果
我要买房
============================
买房前准备
我要买房
买房后装修
静态代理总结
优点:符合开闭原则,不改变真实对象的结构,对目标对象进行了功能的拓展。
缺点:如果需要代理多个真实对象,工作量大,不宜管理。同时如果接口发生了变化,代理类也需要修改。
tips:开闭原则–>软件实现应该对扩展开放,对修改关闭
2.动态代理
在动态代理中我们不需要再手动创建代理类,代理对象的创建由JDK在运行时为我们动态的创建。
2.1 JDK原生的动态代理
JDK的动态代理需要了解两个类
核心:InvocationHandler 和 Proxy
【InvocationHandler:调用处理程序】
源码
public Object invoke(Object proxy, Method method, Object[] args);
//参数
//proxy - 调用该方法的代理实例
//method - 所属方法对应于调用代理实例上的接口方法的实例,方法对象的声明类将是该方法声明的接口
//args - 包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数,
【Proxy: 代理】
接口返回指定接口的代理类实例,该接口将方法调用分派给指定的调用处理程序。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
// ClassLoader : the class loader to define the proxy class 定义代理类的类加载器
// interfaces 代理类要实现的接口列表
// InvocationHandler 指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
代码实现
依然是刚才的例子,改用动态代理。只修改了BuyHouseProxy这个类
代理对象
public class BuyHouseProxy implements InvocationHandler {
//被代理对象
private Object object;
public BuyHouseProxy(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("看房");
System.out.print("执行方法method:");
Object invoke = method.invoke(object,args);
System.out.println("装修");
return invoke;
}
}
客户
public class ProxyTest {
public static void main(String[] args) {
BuyHouse proxyBuy = new BuyHouseImpl();
//新建一个代理实例
BuyHouse buyHouse =(BuyHouse)Proxy.newProxyInstance(proxyBuy.getClass().getClassLoader(),
proxyBuy.getClass().getInterfaces(),new BuyHouseProxy(proxyBuy));
buyHouse.buyHouse();
}
2.2 CGLib实现动态代理
JDK实现动态代理需要真实对象通过接口定义方法,对于没有接口的类,我们可以使用CGLib。其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
实现CGLib动态代理
让代理接口继承MethodInterceptor(方法拦截器)并实现intercept方法:拦截所有目标方法的调用
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
//obj : 目标类的实例
//method : 目标类方法的反射
//args: 方法的动态入参
//proxy: 代理类实例
贴代码!
代理对象
public class BuyHouseProxy implements MethodInterceptor{
private Object object;
public Object getInstance(final Object object){
this.object = object;
//使用Enhancer创建代理类
Enhancer enhancer = new Enhancer();
//继承被代理类
enhancer.setSuperclass(this.object.getClass());
//设置回调
enhancer.setCallback(this);
//生成代理对象并返回
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("看房");
Object result = proxy.invokeSuper(object, args);
System.out.println("装修");
return result;
}
}
BuyHouseProxy类就相当于A类,而Enhancer类则是B类,A类中调用了Enhancer类的setCallback(this)方法,并将回调对象this作为实参传递给了Enhancer类,Enhancer类在后续执行过程中,会调用A类中的intercept()方法。这个intercept()就是回调方法 。
public static void main(String[] args) {
BuyHouseProxy cglibProxy = new BuyHouseProxy();
BuyHouseImpl instance = (BuyHouseImpl) cglibProxy.getInstance(new BuyHouseImpl()); //生成代理对象
instance.buyHouse(); //调用的是intercept()方法
}