美文网首页
Java随笔(2) 代理模式

Java随笔(2) 代理模式

作者: sunyelw | 来源:发表于2020-03-21 19:38 被阅读0次

在看Mybatis发现其SQL执行使用了动态代理,这里简单做个记录


代理模式应该是最常见的设计模式之一,它很好。

代理模式
  • 客户调用的是接口,无所谓你什么实现,完成我的功能就行
  • 实际调用的是代理对象,在代理对象内部调用了实际对象的接口实现方法
  • 代理是一种增强
  • 代理分为动态代理和静态代理两种,区别就是代理的生成时机
  • 静态代理在程序运行之前就存在,动态代理则在运行时通过反射等技术动态生成代理对象
  • 动态代理分为JDK动态代理和CGLIB动态代理
  • 开闭原则

其实跟代理模式很容易混淆的就是适配器模式,只需要记住代理模式是一种增强,而适配器模式是转换,而且还需要新增对外的适配接口并且所有目标接口都要继承它。

我觉得代理模式就做了两件事

  • 生成代理对象
  • 在代理对象中调用目标方法同时做一些增强

Base 对象

接口IService.java

public interface IService {
    void doSomeThing();
}

实现IServiceImpl.java

public class IServiceImpl implements IService {
    @Override
    public void doSomeThing() {
        System.out.println(" Hello world ");
    }
}

静态代理

代理StaticProxy.java,代理的就是这个实现类IServiceImpl 了

public class StaticProxy implements IService {

    public IService iService;
    public StaticProxy(IService iService) {
        this.iService = iService;
    }

    @Override
    public void doSomeThing() {
        beforeDo();
        iService.doSomeThing();
        afterDo();
    }
    private void beforeDo() {
        System.out.println("before do this");
    }
    private void afterDo() {
        System.out.println("after do this");
    }
}

调用示例

public class StaticDemo {
    public static void main(String[] args) {
        // init
        StaticProxy sdp = new StaticProxy(new IServiceImpl());
        // invoke
        sdp.doSomeThing();
    }
}

静态代理的两件事:

  • 以实际对象为参数构造代理对象StaticProxy
  • 代理对象的方法调用时调用实际对象

关于动态代理与静态代理的区别,也就是为什么动态代理横行而静态代理门可罗雀,我讲不好。

但我想试试看。

  • 动态代理可以代理一系列的接口,静态代理也可以实现一系列接口,但实际方法调用时就要一个一个去调用实际对象的同名方法
  • 都说了我不会。。

动态代理 - JDK

  • 需要实现指定接口InvocationHandler
  • 代理对象通过反射类Proxy生成

JDKDynamicProxy.java

public class JDKDynamicProxy implements InvocationHandler {
    private Object obj;
    public JDKDynamicProxy(Object obj) {
        this.obj = obj;
    }
    /**
     * 实现方法
     *
     * @param proxy 代理对象
     * @param method 代理方法
     * @param args 代理方法参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy name: " + proxy.getClass().getName());
        System.out.println("method name: " + method.getName());
        beforeDo();
        // obj.method(args)
        Object response = method.invoke(obj, args);
        afterDo();
        return response;
    }

    private void beforeDo() {
        System.out.println("JDK PROXY BEFORE");
    }
    private void afterDo() {
        System.out.println("JDK PROXY AFTER");
    }
}

再看下如何生成
JDKDynamicFactory.java

public class JDKDynamicFactory {

    public static IService newInstance() {
        // 实际对象
        Object delegate = new IServiceImpl();
        // 调用处理器
        InvocationHandler handler = new JDKDynamicProxy(delegate);
        return newInstance(delegate, handler);
    }

    private static IService newInstance(Object obj, InvocationHandler invocationHandler) {

        /**
         * 类加载器,可传null
         *
         * 代理接口,可直接写 Class<?>{Xxxx.class}
         *
         * 当前代理对象, InvocationHandler
         */
        return (IService) Proxy.newProxyInstance(
                obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),
                invocationHandler);
    }
}

仔细分析下最后一步的方法 Proxy.newProxyInstance

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) throws IllegalArgumentException {
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
    // 1.为接口生成代理对象
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        // 2.使用反射拿到代理对象的构造器
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }

        // 3.构造实例返回
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

其实 做了三件事

1.为接口生成代理对象
Class<?> cl = getProxyClass0(loader, intfs);

2.使用反射拿到代理对象的构造器,以 InvocationHandler 为参数
private static final Class<?>[] constructorParams = {InvocationHandler.class};
final Constructor<?> cons = cl.getConstructor(constructorParams);

3.使用调用处理器实例构造接口实例返回
InvocationHandler h;
return cons.newInstance(new Object[]{h});

那么整个工厂额外做的一件事就是构造调用处理器实例 h 了

0.构造调用处理器
Object delegate = new IServiceImpl();
InvocationHandler handler = new JDKDynamicProxy(delegate);

JDKDynamicProxy 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用

再看下调用JDKDynamicDemo.java

public class JDKDynamicDemo {
    public static void main(String[] args) {
        IService proxy = JDKDynamicFactory.newInstance();
        proxy.doSomeThing();
    }
}

调用结果

proxy name: com.sun.proxy.$Proxy0
method name: doSomeThing
JDK PROXY BEFORE
 Hello world 
JDK PROXY AFTER

JDK动态代理的两件事

  • JDKDynamicFactory.newInstance 构造代理对象
  • JDKDynamicProxy.invoke 调用实际对象

动态代理 - CGLIB

相比之前的代理模式实现,CGLIB最牛逼的点应该就是可以不用实现接口,他可以直接代理类

不过同样,代理类需要实现指定接口MethodInterceptor

代理类CglibDynamicProxy

public class CglibDynamicProxy implements MethodInterceptor {

    public CglibDynamicProxy() {}

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object ot = null;
        beforeDo();
        ot = methodProxy.invokeSuper(o, objects);
        afterDo();
        return ot;
    }

    private void beforeDo() {
        System.out.println("CGLIB PROXY BEFORE");
    }
    private void afterDo() {
        System.out.println("CGLIB PROXY AFTER");
    }
}

被代理类CglibService.java

public class CglibService {
    public void a() {
        System.out.println("aaaaaaaaaaaaaaa");
    }
}

调用CglibDynamicDemo.java

public class CglibDynamicDemo {
    public static void main(String[] args){
        Enhancer enhancer = new Enhancer();
        0. 设置代理目标
        enhancer.setSuperclass(CglibService.class);
        1. 设置CGLIB代理处理
        enhancer.setCallback(new CglibDynamicProxy());
        2. 创建代理对象
        CglibService iService = (CglibService) enhancer.create();
        iService.a();
    }
}

执行结果

CGLIB PROXY BEFORE
aaaaaaaaaaaaaaa
CGLIB PROXY AFTER

CGLIB的实现包有两种,其中还有一段很乱的历史。

代理模式的经典场景

  • spring aop底层实现就是动态代理,我隐约记得是如果有接口就用JDK没有就用CGLIB
  • mybatis的SQL执行

......

参考链接
动态代理与静态代理区别


今天一天都在为mybatis的SQL执行流程源码做准备,醉了~

相关文章

  • Java随笔(2) 代理模式

    在看Mybatis发现其SQL执行使用了动态代理,这里简单做个记录 代理模式应该是最常见的设计模式之一,它很好。 ...

  • 代理模式

    JAVA的动态代理模式:A接口,A1子类实现A接口,A2子类实现A接口。那么JAVA的动态代理模式会A1、A2.....

  • java动态代理(JDK和cglib)(转载自http://ww

    java动态代理(JDK和cglib) JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是...

  • 学习笔记2020-05-20

    1、JAVA内存模型 2、结构型模式概述,代理模式 1、JAVA内存模型 Java 虚拟机 我们都知道 Java ...

  • java 动态代理

    1、代理模式 2、java 动态代理2.1 InvocationHandler 实现类告诉程序运行动态生成的代理...

  • Java代理模式之JDK动态代理

    了解什么是动态代理模式,可参考Java设计模式之代理模式 简介 JDK动态代理是java.lang.reflect...

  • Android插件化架构浅析实现方法

    Java动态代理 1,什么是代理模式?代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。 2,代理模...

  • java代理模式的那些事

    java代理模式-登场 什么是代理模式? 代理模式是java中的一种设计模式,它其实就是设置一个中间环节来代理你要...

  • java建造者模式

    其他设计模式java单例模式java建造者模式java策略模式java代理模式java观察者模式java适配器模式...

  • java单例模式

    其他设计模式java单例模式java建造者模式java策略模式java代理模式java观察者模式java适配器模式...

网友评论

      本文标题:Java随笔(2) 代理模式

      本文链接:https://www.haomeiwen.com/subject/msryyhtx.html