一直好奇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