从代理模式到mockito原理

代理模式回顾

代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

从代理模式到mockito原理_第1张图片

  • 抽象角色Subject:通过接口或抽象类声明真实角色实现的业务方法。

  • 代理角色Proxy:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

  • 真实角色RealSubject:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用

静态代理是由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

根据上面代理模式的类图,可以很简单的写出静态代理的例子。

代理类在程序运行时创建的代理方式被成为动态代理。动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了。

JDK的动态代理

JDK提供了Proxy类的newProxyInstance方法,可以创建一个动态代理对象

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

        final Class[] intfs = interfaces.clone();
        // Android-changed: sm is always null
        // final SecurityManager sm = System.getSecurityManager();
        // if (sm != null) {
        //     checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        // }

        /*
         * Look up or generate the designated proxy class.
         */
        Class cl = getProxyClass0(loader, intfs);

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

            final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                // Android-changed: Removed AccessController.doPrivileged
                cons.setAccessible(true);
            }
            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);
        }
    }
  private static Class getProxyClass0(ClassLoader var0, Class... var1) {
        if (var1.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        } else {
            return (Class)proxyClassCache.get(var0, var1);
        }
    }

通过 getProxyClass0方法获取到了代理类

private static final WeakCache[], Class> proxyClassCache = new WeakCache(new Proxy.KeyFactory(), new Proxy.ProxyClassFactory());
ProxyClassFactory动态生成了代理类,ProxyClassFactory调用了ProxyGenerator动态生成了代理类的字节码。

JDK动态代理只能对实现了接口的类生成代理,而不能针对类。

如何对类进行代理呢?可以使用CGLIB

CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)

JDK和Cglib对比

  1. JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。

  2. JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。

  3. JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。

mockito最开始使用了Cglib创建动态代理,后来修改为使用bytebuddy。

mockito原理

//Mockito.mock方法
    public static  T mock(Class classToMock, MockSettings mockSettings) {
        return MOCKITO_CORE.mock(classToMock, mockSettings);
    }
//MOCKITO_CORE.mock方法
    public  T mock(Class typeToMock, MockSettings settings) {
        if (!MockSettingsImpl.class.isInstance(settings)) {
            throw new IllegalArgumentException("Unexpected implementation of '" + settings.getClass().getCanonicalName() + "'\n" + "At the moment, you cannot provide your own implementations of that class.");
        }
        MockSettingsImpl impl = MockSettingsImpl.class.cast(settings);
        MockCreationSettings creationSettings = impl.build(typeToMock);
        T mock = createMock(creationSettings);
        mockingProgress().mockingStarted(mock, creationSettings);
        return mock;
    }
//MockUtil createMock方法
  public static  T createMock(MockCreationSettings settings) {
        MockHandler mockHandler =  createMockHandler(settings);

        T mock = mockMaker.createMock(settings, mockHandler);

        Object spiedInstance = settings.getSpiedInstance();
        if (spiedInstance != null) {
            new LenientCopyTool().copyToMock(spiedInstance, mock);
        }

        return mock;
    }
//SubclassByteBuddyMockMaker createMock方法
   public  T createMock(MockCreationSettings settings, MockHandler handler) {
        Class mockedProxyType = createMockType(settings);

        Instantiator instantiator = Plugins.getInstantiatorProvider().getInstantiator(settings);
        T mockInstance = null;
        try {
            mockInstance = instantiator.newInstance(mockedProxyType);
            MockAccess mockAccess = (MockAccess) mockInstance;
            mockAccess.setMockitoInterceptor(new MockMethodInterceptor(handler, settings));

            return ensureMockIsAssignableToMockedType(settings, mockInstance);
        } catch (ClassCastException cce) {
            throw new MockitoException(join(
                    "ClassCastException occurred while creating the mockito mock :",
                    "  class to mock : " + describeClass(settings.getTypeToMock()),
                    "  created class : " + describeClass(mockedProxyType),
                    "  proxy instance class : " + describeClass(mockInstance),
                    "  instance creation by : " + instantiator.getClass().getSimpleName(),
                    "",
                    "You might experience classloading issues, please ask the mockito mailing-list.",
                    ""
            ), cce);
        } catch (org.mockito.creation.instance.InstantiationException e) {
            throw new MockitoException("Unable to create mock instance of type '" + mockedProxyType.getSuperclass().getSimpleName() + "'", e);
        }
    }
//SubclassByteBuddyMockMaker createMockType方法
    public  Class createMockType(MockCreationSettings settings) {
        try {
            return cachingMockBytecodeGenerator.mockClass(MockFeatures.withMockFeatures(
                    settings.getTypeToMock(),
                    settings.getExtraInterfaces(),
                    settings.getSerializableMode(),
                    settings.isStripAnnotations()
            ));
        } catch (Exception bytecodeGenerationFailed) {
            throw prettifyFailure(settings, bytecodeGenerationFailed);
        }
    }
//SubclassBytecodeGenerator mockClass方法
 public  Class mockClass(MockFeatures features) {
        String name = nameFor(features.mockedType);
        DynamicType.Builder builder =
                byteBuddy.subclass(features.mockedType)
                         .name(name)
                         .ignoreAlso(isGroovyMethod())
                         .annotateType(features.stripAnnotations
                             ? new Annotation[0]
                             : features.mockedType.getAnnotations())
                         .implement(new ArrayList(features.interfaces))
                         .method(matcher)
                           .intercept(dispatcher)
                           .transform(withModifiers(SynchronizationState.PLAIN))
                           .attribute(features.stripAnnotations
                               ? MethodAttributeAppender.NoOp.INSTANCE
                               : INCLUDING_RECEIVER)
                         .method(isHashCode())
                           .intercept(hashCode)
                         .method(isEquals())
                           .intercept(equals)
                         .serialVersionUid(42L)
                         .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE)
                         .implement(MockAccess.class)
                           .intercept(FieldAccessor.ofBeanProperty());
        if (features.serializableMode == SerializableMode.ACROSS_CLASSLOADERS) {
            builder = builder.implement(CrossClassLoaderSerializableMock.class)
                             .intercept(writeReplace);
        }
        if (readReplace != null) {
            builder = builder.defineMethod("readObject", void.class, Visibility.PRIVATE)
                    .withParameters(ObjectInputStream.class)
                    .throwing(ClassNotFoundException.class, IOException.class)
                    .intercept(readReplace);
        }
        ClassLoader classLoader = new MultipleParentClassLoader.Builder()
            .append(features.mockedType)
            .append(features.interfaces)
            .append(currentThread().getContextClassLoader())
            .append(MockAccess.class, DispatcherDefaultingToRealMethod.class)
            .append(MockMethodInterceptor.class,
                MockMethodInterceptor.ForHashCode.class,
                MockMethodInterceptor.ForEquals.class).build(MockMethodInterceptor.class.getClassLoader());
        if (classLoader != features.mockedType.getClassLoader()) {
            assertVisibility(features.mockedType);
            for (Class iFace : features.interfaces) {
                assertVisibility(iFace);
            }
            builder = builder.ignoreAlso(isPackagePrivate()
                .or(returns(isPackagePrivate()))
                .or(hasParameters(whereAny(hasType(isPackagePrivate())))));
        }
        return builder.make()
                      .load(classLoader, loader.resolveStrategy(features.mockedType, classLoader, name.startsWith(CODEGEN_PACKAGE)))
                      .getLoaded();
    }

最终mockito 在SubclassBytecodeGenerator类中使用bytebuddy 动态创建了一个代理类,bytebuddy提供了一个API用于动态生成任意的Java类。

动态代理使用了继承,所以不能代理final类。

mockito2.0开始支持代理final类,它是如何实现的呢?

2.0引入新的插件InlineByteBuddyMockMaker,在InlineBytecodeGenerator中进行字节码转换,原理是使用JAVA instrumentation动态修改类的行为(注意这里不是Android的instrumentation)。

Android中不能使用 inline插件,因为JAVA的 java.lang.instrument Android中是不可见的,会抛出NoClassDefFoundError异常

 

你可能感兴趣的:(android,java)