一直好奇JUnit里面@Test的实现,搜了不少网页,大部分都是讲的使用方式,很少有人去讲解实现原理。自己看源码看了半天也没有找到头绪。
索性自己摸索着实现了一套类似的效果,下一步准备应用在已经完成的仿真测试上,虽然很初级,但也算一个框架了。
1 定义注解
//Simulation.javapackage com.sigh.test;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/**
* Created by sigh on 2015/6/10.
*/@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.TYPE})public @interface Simulation {
}//Run.javapackage com.sigh.test;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/**
* Created by sigh on 2015/6/10.
*///类似junit的@Test效果@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface Run { //该操作发生的概率
double rate() default 0;
}//Report.javapackage com.sigh.test;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/**
* Created by sigh on 2015/6/10.
*///在所有@Run运行完之后报告结果@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.METHOD})public @interface Report {
}这三个注解就是对外提供的全部接口了。
2 测试类
//First.javapackage com.sigh.test;import org.springframework.stereotype.Service;/**
* Created by sigh on 2015/6/9.
*/@Simulation@Servicepublic class First { @Run(rate = 0.5) boolean run() {
System.out.println("first"); return true;
} @Report
void report() {
System.out.println("report first");
}
}//Second.javapackage com.sigh.test;import org.springframework.stereotype.Service;/**
* Created by sigh on 2015/6/9.
*/@Simulation@Servicepublic class Second { @Run(rate = 0.5) long doWork() {
System.out.println("second"); return 3;
} @Report
void display() {
System.out.println("display second");
}
}比较特殊的是rate,用于提供一种概率性的运行方式。
3 框架核心代码
package com.sigh.test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.FileSystemXmlApplicationContext;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.Objects;import java.util.concurrent.atomic.AtomicInteger;/**
* Created by sigh on 2015/6/10.
*/public class SimulationFacade { interface RunMethod { void run(); double getRate();
} interface ReportMethod { void report();
} private static List<Object> classes = null; private static List<RunMethod> runMethods = null; private static List<ReportMethod> reportMethods = null; private final static int MAX_OPERATION_TIMES = 100; static {
classes = new ArrayList<>();
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("src/spring-config.xml");
Map<String, Object> beanNames = applicationContext.getBeansWithAnnotation(Simulation.class); for (Object o : beanNames.values()) {
classes.add(o);
}
System.out.println(beanNames);
runMethods = new ArrayList<RunMethod>();
reportMethods = new ArrayList<ReportMethod>(); for (final Object o : classes) {
Method[] methods = o.getClass().getDeclaredMethods(); for (final Method method : methods) { if (method.isAnnotationPresent(Run.class)) {
runMethods.add(new RunMethod() { @Override
public void run() { try {
method.invoke(o);
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} @Override
public double getRate() { return method.getAnnotation(Run.class).rate();
}
});
} else if (method.isAnnotationPresent(Report.class)) {
reportMethods.add(new ReportMethod() { @Override
public void report() { try {
method.invoke(o);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
}
}
}
} public void run() { double rate = Math.random(); for (RunMethod method : runMethods) { if (rate <= method.getRate()) {
method.run(); break;
} else {
rate -= method.getRate();
}
}
} public void report() { for (ReportMethod method : reportMethods) {
method.report();
}
} public static class MulTiThreadSimulation { private final static int THREAD_NUM = 10;
SimulationFacade simulationFacade = new SimulationFacade(); static AtomicInteger operationTimes = new AtomicInteger(0); public void run() {
List<Thread> threadList = new ArrayList<Thread>(); for (int i = 0; i < THREAD_NUM; i++) {
Thread thread = new Thread(new Runnable() { @Override public void run() { while (operationTimes.getAndIncrement() < SimulationFacade.MAX_OPERATION_TIMES) { try { //仿真测试
simulationFacade.run();
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.start();
threadList.add(thread);
} for (Thread thread : threadList) { try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public void report() {
simulationFacade.report();
}
} public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException {
MulTiThreadSimulation mulTiThreadSimulation = new MulTiThreadSimulation();
mulTiThreadSimulation.run();
mulTiThreadSimulation.report();
}
}基本的思路也相对比较清晰,所以也没有太多需要解释的地方。
java的内部类确实有很多很有意思的地方,许多地方现在想来还是有些复杂。估计还需要一段时间来慢慢理解java的内存模型了。
作者:littlersmall
链接:https://www.jianshu.com/p/04dc3adac503