Mockito 动态代理 & ByteBuddy

Posted master-dragon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mockito 动态代理 & ByteBuddy相关的知识,希望对你有一定的参考价值。

现在说动态代理:总是想到Spring AOP的JDK动态代理(接口,基于反射) & Cglib代理(当然这两种都是新增一个新的class)完成动态代理;其实各种mock也有不少使用的动态代理技术的,比如Mockito


  • mock例子
public interface HelloService 
    String say(String what);

import com.example.service.HelloService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class HelloServiceMockTest 

   @Test
   public void testHelloSay()
   	HelloService helloService = Mockito.mock(HelloService.class);
   	String str = "abc";
   	Mockito.when(helloService.say(Mockito.anyString())).thenReturn("xx:" + str);
   	String what = helloService.say("xxx");
   	Assert.assertEquals(what, "xx:" + str);
   


  • 源码追踪

mock对象debug到如下

org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker#createMock

 public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) 
      Class<? extends T> mockedProxyType = this.createMockType(settings);
       Instantiator instantiator = Plugins.getInstantiatorProvider().getInstantiator(settings);
       Object mockInstance = null;

       try 
           mockInstance = instantiator.newInstance(mockedProxyType);
           MockAccess mockAccess = (MockAccess)mockInstance;
           mockAccess.setMockitoInterceptor(new MockMethodInterceptor(handler, settings));
           return ensureMockIsAssignableToMockedType(settings, mockInstance);
        catch (ClassCastException var7) 
           throw new MockitoException(StringUtil.join(new Object[]"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.", ""), var7);
        catch (InstantiationException var8) 
           throw new MockitoException("Unable to create mock instance of type '" + mockedProxyType.getSuperclass().getSimpleName() + "'", var8);
       
   

其中关于Class对象(见org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator#mockClass)可以猜想就是通过mock信息生产字节码构造出来的(产生一个子类)

进一步org.mockito.internal.creation.bytebuddy.SubclassBytecodeGenerator#mockClass

 public <T> Class<? extends T> mockClass(MockFeatures<T> features) 
        String name = this.nameFor(features.mockedType);
        Builder<T> builder = this.byteBuddy.subclass(features.mockedType).name(name).ignoreAlso(isGroovyMethod()).annotateType(features.stripAnnotations ? new Annotation[0] : features.mockedType.getAnnotations()).implement(new ArrayList(features.interfaces)).method(this.matcher).intercept(this.dispatcher).transform(ForMethod.withModifiers(new net.bytebuddy.description.modifier.ModifierContributor.ForMethod[]SynchronizationState.PLAIN)).attribute((Factory)(features.stripAnnotations ? NoOp.INSTANCE : ForInstrumentedMethod.INCLUDING_RECEIVER)).method(ElementMatchers.isHashCode()).intercept(this.hashCode).method(ElementMatchers.isEquals()).intercept(this.equals).serialVersionUid(42L).defineField("mockitoInterceptor", MockMethodInterceptor.class, new ForField[]Visibility.PRIVATE).implement(new Type[]MockAccess.class).intercept(FieldAccessor.ofBeanProperty());
        if (features.serializableMode == SerializableMode.ACROSS_CLASSLOADERS) 
            builder = ((Builder)builder).implement(new Type[]CrossClassLoaderSerializableMock.class).intercept(this.writeReplace);
        

        if (this.readReplace != null) 
            builder = ((Builder)builder).defineMethod("readObject", Void.TYPE, new net.bytebuddy.description.modifier.ModifierContributor.ForMethod[]Visibility.PRIVATE).withParameters(new Type[]ObjectInputStream.class).throwing(new Type[]ClassNotFoundException.class, IOException.class).intercept(this.readReplace);
        

        ClassLoader classLoader = (new net.bytebuddy.dynamic.loading.MultipleParentClassLoader.Builder()).append(new Class[]features.mockedType).append(features.interfaces).append(new ClassLoader[]Thread.currentThread().getContextClassLoader()).append(new Class[]MockAccess.class, DispatcherDefaultingToRealMethod.class).append(new Class[]MockMethodInterceptor.class, ForHashCode.class, ForEquals.class).build(MockMethodInterceptor.class.getClassLoader());
        if (classLoader != features.mockedType.getClassLoader()) 
            assertVisibility(features.mockedType);
            Iterator var5 = features.interfaces.iterator();

            while(var5.hasNext()) 
                Class<?> iFace = (Class)var5.next();
                assertVisibility(iFace);
            

            builder = ((Builder)builder).ignoreAlso(ElementMatchers.isPackagePrivate().or(ElementMatchers.returns(ElementMatchers.isPackagePrivate())).or(ElementMatchers.hasParameters(ElementMatchers.whereAny(ElementMatchers.hasType(ElementMatchers.isPackagePrivate())))));
        

        return ((Builder)builder).make().load(classLoader, this.loader.resolveStrategy(features.mockedType, classLoader, name.startsWith("org.mockito.codegen."))).getLoaded();
    

使用到了ByteBuddy技术, demo例子

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.matcher.ElementMatchers;


interface HelloService 
    String say(String what);


public class Client 

    public static void main(String[] args) throws Exception 
        testProxy();
        testProxy2();
    

    private static void testProxy() throws Exception
        HelloService proxy = createByteBuddyDynamicProxy();
        String s = proxy.say("xxx");
        System.out.println(s);
    

    private static void testProxy2() throws Exception
        HelloService proxy = createByteBuddyDynamicProxy2();
        String s = proxy.say("xxx");
        System.out.println(s);
    

    public static HelloService createByteBuddyDynamicProxy() throws Exception 
        return new ByteBuddy().subclass(HelloService.class)
                .method(
                        ElementMatchers.named("say")
                                .and(ElementMatchers.returns(String.class))
                )
                .intercept(FixedValue.value("hello"))
                .make()
                .load(Client.class.getClassLoader())
                .getLoaded()
                .getDeclaredConstructor()
                .newInstance();
    

    public static HelloService createByteBuddyDynamicProxy2() throws Exception 
        return new ByteBuddy().subclass(HelloService.class)
                .method(
                        ElementMatchers.named("say")
                                .and(ElementMatchers.isDeclaredBy(HelloService.class))
                                .and(ElementMatchers.returns(String.class))
                )

                .intercept(MethodDelegation.to(Interceptor.class))
                .make()
                .load(Client.class.getClassLoader())
                .getLoaded()
                .getDeclaredConstructor()
                .newInstance();
    

    public static class Interceptor
        @RuntimeType
        public static String say(@Origin String method,
                                 @AllArguments Object[] args) 
            System.out.println(method);
            return "hello:" + args[0];
        
    


其中

new ByteBuddy().subclass(HelloService.class)
                .method(
                        ElementMatchers.named("say")
                                .and(ElementMatchers.returns(String.class))
                )
                .intercept(FixedValue.value("hello"))
                .make()
                .saveIn(new File("Temp.class"));

生成如下类

package buddy;

public class HelloService$ByteBuddy$HLrzXYPl implements HelloService 
    public String say(String var1) 
        return "hello";
    

    public HelloService$ByteBuddy$HLrzXYPl() 
    

而另外一个拦截生成如下(调用Interceptor的say方法)

package buddy;

import buddy.Client.Interceptor;

public class HelloService$ByteBuddy$U6Z1os3k implements HelloService 
    public String say(String var1) 
        return Interceptor.say("public abstract java.lang.String buddy.HelloService.say(java.lang.String)", new Object[]var1);
    

    public HelloService$ByteBuddy$U6Z1os3k() 
    

以上是关于Mockito 动态代理 & ByteBuddy的主要内容,如果未能解决你的问题,请参考以下文章

JAVA静态&动态代理

深圳某小公司:Java反射 && 动态代理

mockito when() 调用如何工作?

二十二 动态代理&解决网站的字符集编码问题

Mockito:使用泛型参数进行验证

ORM简介 && MyBatis和Hibernate的不同 && 动态代理简单实现Mybatis基本使用