package org.test4j.module.inject; import static org.test4j.tools.commons.AnnotationHelper.getFieldsAnnotatedWith; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.test4j.module.core.Module; import org.test4j.module.core.TestListener; import org.test4j.module.inject.annotations.Inject; import org.test4j.module.inject.annotations.TestedObject; import org.test4j.module.inject.utility.InjectionModuleHelper; import org.test4j.tools.commons.AnnotationHelper; import org.test4j.tools.commons.ClazzHelper; import org.test4j.tools.commons.StringHelper; import org.test4j.tools.reflector.FieldAccessor; import org.test4j.tools.reflector.PropertyAccessor; import org.test4j.tools.reflector.imposteriser.Test4JProxy; @SuppressWarnings({ "rawtypes", "unchecked" }) public class InjectModule implements Module { @Override public void init() { } @Override public void afterInit() { } /** * test4j扩展的注入<br> * * @param testedObject */ private void injectInto(Object testedObject) { Class testedClazz = testedObject.getClass(); injectIntoByInejctTargets(testedObject); injectIntoByTestedObject(testedClazz, testedObject); } /** * 根据@TestedObject标识来注入对象 * * @param testedObject */ private void injectIntoByTestedObject(Class testedClazz, Object testedObject) { Set<Field> targets = AnnotationHelper.getFieldsAnnotatedWith(testedClazz, TestedObject.class); Map<String, Object> propertyValues = this.findValuesForInejctInto(testedClazz, testedObject); for (Field target : targets) { Object targetObject = new FieldAccessor(testedClazz, target).get(testedObject); targetObject = ClazzHelper.getProxiedObject(targetObject); for (Entry<String, Object> entry : propertyValues.entrySet()) { FieldAccessor accessor = new FieldAccessor(target.getType(), entry.getKey()); Object value = entry.getValue(); accessor.set(targetObject, value); } } } /** * 在@Inject属性中显式指定了插入目标对象 * * @param testedObject */ private void injectIntoByInejctTargets(Object testedObject) { Set<Field> injects = getFieldsAnnotatedWith(testedObject.getClass(), Inject.class); for (Field injectField : injects) { Class injectedClazz = injectField.getType(); Inject inject = injectField.getAnnotation(Inject.class); if (inject.targets().length == 0) { continue; } try { Object injectedObject = Test4JProxy.proxy(testedObject.getClass(), injectField); injectedInto(testedObject, injectedObject, injectedClazz, inject.targets(), inject.properties()); } catch (IllegalArgumentException e) { Object injectedObject = new FieldAccessor(testedObject.getClass(), injectField).get(testedObject); injectedInto(testedObject, injectedObject, injectedClazz, inject.targets(), inject.properties()); } } } /** * 查找注入@TestedObject的属性对象 * * @param testedClazz * @param inject * @return */ Map<String, Object> findValuesForInejctInto(Class testedClazz, Object testedObject) { Set<Field> fields = AnnotationHelper.getFieldsAnnotatedWith(testedClazz, Inject.class); Map<String, Object> values = new HashMap<String, Object>(); for (Field field : fields) { Inject inject = field.getAnnotation(Inject.class); if (inject.targets().length == 0) { try { Object value = Test4JProxy.proxy(testedClazz, field); values.put(field.getName(), value); } catch (IllegalArgumentException ie) { Object value = new FieldAccessor(testedClazz, field).get(testedObject); values.put(field.getName(), value); } } } return values; } /** * 把对象injectedObject注入到testedObject对应的变量targets的属性中 * * @param testedObject 测试类实例 * @param injectedObject 被注入实例 * @param injectedClazz 被注入实例定义类型 * @param targets 要注入的对象列表 * @param properties 注入到对象的那个属性中列表;如果属性为空,则按类型注入 */ private static void injectedInto(Object testedObject, Object injectedObject, Class injectedClazz, String[] targets, String[] properties) { for (int index = 0; index < targets.length; index++) { String target = targets[index]; Object targetObject = PropertyAccessor.getPropertyByOgnl(testedObject, target, true); targetObject = ClazzHelper.getProxiedObject(targetObject); if (targetObject == null) { throw new RuntimeException("can't inject a mock object into a null object, ongl = " + target); } String property = index < properties.length ? properties[index] : null; if (StringHelper.isBlankOrNull(property)) { InjectionModuleHelper.injectIntoByType(injectedObject, injectedClazz == null ? targetObject.getClass() : injectedClazz, targetObject); } else { InjectionModuleHelper.injectInto(injectedObject, targetObject, property); } } } @Override public TestListener getTestListener() { return new InjectTestListener(); } protected class InjectTestListener extends TestListener { @Override public void beforeRunning(Object testObject, Method testMethod) { injectInto(testObject); } @Override protected String getName() { return "InjectTestListener"; } } }