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的主要内容,如果未能解决你的问题,请参考以下文章