JUnit4 源码阅读(二) - 模型类 解读与分析
如下图是runners部分的结构
可以分为两个部分
1. 模型类: 作用域模型,方法模型,测试类模型
2. 执行类Runner: 如ParentRunner
, BlockJUnit4ClassRunner
, Suite
,理解这三个Runner很重要。
作用域模型是对测试类作用域的封装,方法模型是对测试类方法的封装,测试类模型是对测试类的封装。我们知道测试类的作用域,方法,以及类上都有可能存在注解,所以这三个模型都实现了Annotatable
注解,2个方法一个是获取所有注解,一个是获取特定注解。
import java.lang.annotation.Annotation;
public interface Annotatable {
Annotation[] getAnnotations();
<T extends Annotation> T getAnnotation(Class<T> annotationType);
}
作用域模型和方法模型都继承于抽象类FrameMember,这里用到了泛型,同时要求子类泛型类型必须是FrameMemeber的子类。
关于各个方法,请看注释
public abstract class FrameworkMember<T extends FrameworkMember<T>> implements
Annotatable {
//是否和otherMember相等
abstract boolean isShadowedBy(T otherMember);
//是否已经存在于members集合中
boolean isShadowedBy(List<T> members) {
for (T each : members) {
if (isShadowedBy(each)) {
return true;
}
}
return false;
}
protected abstract int getModifiers();
//修饰符是否是静态
public boolean isStatic() {
return Modifier.isStatic(getModifiers());
}
//修饰符是否是公共的
public boolean isPublic() {
return Modifier.isPublic(getModifiers());
}
//明称,方法名、作用域名
public abstract String getName();
//作用域类型,方法返回值类型
public abstract Class<?> getType();
//获取声明类
public abstract Class<?> getDeclaringClass();
}
作用域模型
FrameworkField封装了一个Field, 对外提供了一些,我们访问field的方法。其中field.get(target)
,表示获取目标对象target在该作用的值。
public class FrameworkField extends FrameworkMember<FrameworkField> {
private final Field field;
FrameworkField(Field field) {
if (field == null) {
throw new NullPointerException(
"FrameworkField cannot be created without an underlying field.");
}
this.field = field;
}
@Override
public String getName() {
return getField().getName();
}
public Annotation[] getAnnotations() {
return field.getAnnotations();
}
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return field.getAnnotation(annotationType);
}
@Override
public boolean isShadowedBy(FrameworkField otherMember) {
return otherMember.getName().equals(getName());
}
@Override
protected int getModifiers() {
return field.getModifiers();
}
public Field getField() {
return field;
}
@Override
public Class<?> getType() {
return field.getType();
}
@Override
public Class<?> getDeclaringClass() {
return field.getDeclaringClass();
}
public Object get(Object target) throws IllegalArgumentException, IllegalAccessException {
return field.get(target);
}
@Override
public String toString() {
return field.toString();
}
}
方法模型
同样FrameworkMethod封装了一个Method对象。
其中invokeExplosively
方法接收目标对象和方法所需参数,通过反射来执行目标对象的方法。关于反射,可以参考这篇博文
Java 反射和注解的一些总结
public class FrameworkMethod extends FrameworkMember<FrameworkMethod> {
private final Method method;
/**
* Returns a new {@code FrameworkMethod} for {@code method}
*/
public FrameworkMethod(Method method) {
if (method == null) {
throw new NullPointerException(
"FrameworkMethod cannot be created without an underlying method.");
}
this.method = method;
}
/**
* Returns the underlying Java method
*/
public Method getMethod() {
return method;
}
/**
* Returns the result of invoking this method on {@code target} with
* parameters {@code params}. {@link InvocationTargetException}s thrown are
* unwrapped, and their causes rethrown.
*/
public Object invokeExplosively(final Object target, final Object... params)
throws Throwable {
return new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return method.invoke(target, params);
}
}.run();
}
/**
* Returns the method's name
*/
@Override
public String getName() {
return method.getName();
}
/**
* Adds to {@code errors} if this method:
* <ul>
* <li>is not public, or
* <li>takes parameters, or
* <li>returns something other than void, or
* <li>is static (given {@code isStatic is false}), or
* <li>is not static (given {@code isStatic is true}).
* </ul>
*/
public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) {
validatePublicVoid(isStatic, errors);
if (method.getParameterTypes().length != 0) {
errors.add(new Exception("Method " + method.getName() + " should have no parameters"));
}
}
/**
* Adds to {@code errors} if this method:
* <ul>
* <li>is not public, or
* <li>returns something other than void, or
* <li>is static (given {@code isStatic is false}), or
* <li>is not static (given {@code isStatic is true}).
* </ul>
*/
public void validatePublicVoid(boolean isStatic, List<Throwable> errors) {
if (isStatic() != isStatic) {
String state = isStatic ? "should" : "should not";
errors.add(new Exception("Method " + method.getName() + "() " + state + " be static"));
}
if (!isPublic()) {
errors.add(new Exception("Method " + method.getName() + "() should be public"));
}
if (method.getReturnType() != Void.TYPE) {
errors.add(new Exception("Method " + method.getName() + "() should be void"));
}
}
@Override
protected int getModifiers() {
return method.getModifiers();
}
/**
* Returns the return type of the method
*/
public Class<?> getReturnType() {
return method.getReturnType();
}
/**
* Returns the return type of the method
*/
@Override
public Class<?> getType() {
return getReturnType();
}
/**
* Returns the class where the method is actually declared
*/
@Override
public Class<?> getDeclaringClass() {
return method.getDeclaringClass();
}
public void validateNoTypeParametersOnArgs(List<Throwable> errors) {
new NoGenericTypeParametersValidator(method).validate(errors);
}
//比较方法名,然后参数个数,最后参数类型
@Override
public boolean isShadowedBy(FrameworkMethod other) {
if (!other.getName().equals(getName())) {
return false;
}
if (other.getParameterTypes().length != getParameterTypes().length) {
return false;
}
for (int i = 0; i < other.getParameterTypes().length; i++) {
if (!other.getParameterTypes()[i].equals(getParameterTypes()[i])) {
return false;
}
}
return true;
}
@Override
public boolean equals(Object obj) {
if (!FrameworkMethod.class.isInstance(obj)) {
return false;
}
return ((FrameworkMethod) obj).method.equals(method);
}
@Override
public int hashCode() {
return method.hashCode();
}
/**
* Returns true if this is a no-arg method that returns a value assignable
* to {@code type}
*
* @deprecated This is used only by the Theories runner, and does not
* use all the generic type info that it ought to. It will be replaced
* with a forthcoming ParameterSignature#canAcceptResultOf(FrameworkMethod)
* once Theories moves to junit-contrib.
*/
@Deprecated
public boolean producesType(Type type) {
return getParameterTypes().length == 0 && type instanceof Class<?>
&& ((Class<?>) type).isAssignableFrom(method.getReturnType());
}
private Class<?>[] getParameterTypes() {
return method.getParameterTypes();
}
/**
* Returns the annotations on this method
*/
public Annotation[] getAnnotations() {
return method.getAnnotations();
}
/**
* Returns the annotation of type {@code annotationType} on this method, if
* one exists.
*/
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return method.getAnnotation(annotationType);
}
@Override
public String toString() {
return method.toString();
}
}
测试类模型 TestClass
这个类里面声明了许多方法,而且大部分都用到了泛型,有点不容易读。
作用域
private final Class<?> clazz; //clazz是测试类对象,(可以为空,这点需要注意,后面有一些为空的情况,到时再说)
//以下List中可能包含超类的作用域或方法,从后面的构造方法可以看出,之后会涉及到
//key为注解,值为带有该注解的所有方法集合
private final Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations;
//key为注解,值为带有该注解的所有作用域集合
private final Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations;
公有方法
类中非公有类型的方法一般都是提供给公有方法使用,私有方法的产生应该就是为了达到公有方法的效果,将公有方法中重复的片段抽取出来,封装成私有方法,供公有方法使用,这样有助于减少代码数量(尽管这样,该类代码还是很长…)
那么我们就先从公有方法说起,从而慢慢渗透。
TestClass(Class<?>) : //构造方法
getAnnotations() : Annotation[] //获取测试类上的所有注解
getAnnotation(Class<T>) : T //获取测试类上特定注解
getAnnotatedMethods() : List<FrameworkMethod> //测试类上有注解的方法集合
getAnnotatedMethods(Class<? extends Annotation>): List<FrameworkMethod> //有特定类型注解的方法集合
getAnnotatedFields() : List<FrameworkField> //测试类上有注解的作用域集合
getAnnotatedFields(Class<? extends Annotation>) : List<FrameworkField> //有特定类型注解的作用域集合
//以下方法主要用在 规则测试中
//获取所有 带有某种注解的作用域的值,并且作用域类型是T类型或其子类
/**eg.
* @Rule
* public TemporaryFolder folder = new TemporaryFolder(new File("/home/live/IdeaProjects/test"));
*/
getAnnotatedFieldValues(Object, Class<? extends Annotation>, Class<T>): List<T> //
//获取目标对象上 所有带有某种注解的方法的返回值,并且方法的返回值类型是T类型或者其子类
/**eg.
* @Rule
* public ExternalResource getTemporarayForlder() {
* System.out.println("方法中");
* return new TemporaryFolder(new File("/home/live/IdeaProjects/test"));
* }
*/
getAnnotatedMethodValues(Object, Class<? extends Annotation>, Class<T>) : List<T> //
getJavaClass() : Class<?> //测试类对象
getName() : String //测试类类名
getOnlyConstructor(): Constructor<?> //唯一的构造函数
isPublic() : boolean
isANonStaticInnerClass() : boolean //是非静态内部类吗?
hashCode() : int
equals(Object): boolean
构造函数
构造方法传入的测试类对象不为空的话,那么构造函数的个数不能大于1。
注解-方法,注解-作用域 这两个map的构造过程是这样的,首先,我们需要从测试类clazz, 获取所有Member(getDeclaredField, getDeclaredMethod
), 每一个成员都会有对应的注解(getAnnotations()), 那么就是Member-Annotation[] 的形式,我们需要反过来,以Annotation - List的形式 存储到Map中,以下就是这个过程的代码实现(这个过程还是很绕的…)。
public TestClass(Class<?> clazz) {
this.clazz = clazz;
if (clazz != null && clazz.getConstructors().length > 1) {
throw new IllegalArgumentException(
"Test class can only have one constructor");
}
Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations =
new LinkedHashMap<Class<? extends Annotation>, List<FrameworkMethod>>();
Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations =
new LinkedHashMap<Class<? extends Annotation>, List<FrameworkField>>();
scanAnnotatedMembers(methodsForAnnotations, fieldsForAnnotations);
this.methodsForAnnotations = makeDeeplyUnmodifiable(methodsForAnnotations);
this.fieldsForAnnotations = makeDeeplyUnmodifiable(fieldsForAnnotations);
}
//浏览测试类及父类的注解成员。
protected void scanAnnotatedMembers(
Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations,
Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations) {
for (Class<?> eachClass : getSuperClasses(clazz)) { //当前类,及所有超类
//(对象声明方法和方法是不一样的,这点注意)
for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) { //对象所有声明方法
addToAnnotationLists(new FrameworkMethod(eachMethod), methodsForAnnotations);
}
for (Field eachField : getSortedDeclaredFields(eachClass)) {//对象所有声明作用域
addToAnnotationLists(new FrameworkField(eachField), fieldsForAnnotations);
}
}
}
//递归获取超类
private static List<Class<?>> getSuperClasses(Class<?> testClass) {
ArrayList<Class<?>> results = new ArrayList<Class<?>>();
Class<?> current = testClass;
while (current != null) {
results.add(current);
current = current.getSuperclass();
}
return results;
}
// Member-Annotation[],根据Annotation找出对应List<Member>,添加到其中
protected static <T extends FrameworkMember<T>> void addToAnnotationLists(T member,
Map<Class<? extends Annotation>, List<T>> map) {
for (Annotation each : member.getAnnotations()) {
Class<? extends Annotation> type = each.annotationType();
List<T> members = getAnnotatedMembers(map, type, true);
if (member.isShadowedBy(members)) {
return;
}
if (runsTopToBottom(type)) {
members.add(0, member);
} else {
members.add(member);
}
}
}
获取成员的值,利用反射从目标对象中获取
public <T> List<T> getAnnotatedFieldValues(Object test,
Class<? extends Annotation> annotationClass, Class<T> valueClass) {
List<T> results = new ArrayList<T>();
for (FrameworkField each : getAnnotatedFields(annotationClass)) {
try {
//获取目标对象在作用域each上的值
Object fieldValue = each.get(test);
//判断是否为我们需要的类型
if (valueClass.isInstance(fieldValue)) {
results.add(valueClass.cast(fieldValue));
}
} catch (IllegalAccessException e) {
throw new RuntimeException(
"How did getFields return a field we couldn't access?", e);
}
}
return results;
}
public <T> List<T> getAnnotatedMethodValues(Object test,
Class<? extends Annotation> annotationClass, Class<T> valueClass) {
List<T> results = new ArrayList<T>();
for (FrameworkMethod each : getAnnotatedMethods(annotationClass)) {
try {
//判断方法的方法的返回类型是否为我们需要的类型 T
if (valueClass.isAssignableFrom(each.getReturnType())) {
//是的话,反射执行方法,获取返回值
Object fieldValue = each.invokeExplosively(test);
results.add(valueClass.cast(fieldValue));
}
} catch (Throwable e) {
throw new RuntimeException(
"Exception in " + each.getName(), e);
}
}
return results;
}
以下是获取带有注解的field或method集合的方法实现
public List<FrameworkMethod> getAnnotatedMethods() {
List<FrameworkMethod> methods = collectValues(methodsForAnnotations);
Collections.sort(methods, METHOD_COMPARATOR);
return methods;
}
public List<FrameworkField> getAnnotatedFields() {
return collectValues(fieldsForAnnotations);
}
//将Map<?, List<T>>的所有值,添加到一个列表中
private <T> List<T> collectValues(Map<?, List<T>> map) {
Set<T> values = new LinkedHashSet<T>();
for (List<T> additionalValues : map.values()) {
values.addAll(additionalValues);
}
return new ArrayList<T>(values);
}
public List<FrameworkMethod> getAnnotatedMethods(
Class<? extends Annotation> annotationClass) {
return Collections.unmodifiableList(getAnnotatedMembers(methodsForAnnotations, annotationClass, false));
}
public List<FrameworkField> getAnnotatedFields(
Class<? extends Annotation> annotationClass) {
return Collections.unmodifiableList(getAnnotatedMembers(fieldsForAnnotations, annotationClass, false));
}
//从Map<Class<? extends Annotation>, List<T>>中获取某Key,对应的值,如果该key不存在,且fillAbsent为真,那么就会在map中放入,大小为0的List对象
private static <T> List<T> getAnnotatedMembers(Map<Class<? extends Annotation>, List<T>> map,
Class<? extends Annotation> type, boolean fillIfAbsent) {
if (!map.containsKey(type) && fillIfAbsent) {
map.put(type, new ArrayList<T>());
}
List<T> members = map.get(type);
return members == null ? Collections.<T>emptyList() : members;
}