美文网首页Java程序员程序开发
使用反射+注解实现类似JUnit的效果

使用反射+注解实现类似JUnit的效果

作者: littlersmall | 来源:发表于2016-01-09 19:09 被阅读342次

    一直好奇JUnit里面@Test的实现,搜了不少网页,大部分都是讲的使用方式,很少有人去讲解实现原理。自己看源码看了半天也没有找到头绪。
    索性自己摸索着实现了一套类似的效果,下一步准备应用在已经完成的仿真测试上,虽然很初级,但也算一个框架了。
    1 定义注解

    //Simulation.java
    package 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.java
    package 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.java
    package 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.java
    package com.sigh.test;
    import org.springframework.stereotype.Service;
    /**
    * Created by sigh on 2015/6/9.
    */
    @Simulation
    @Service
    public class First {
       @Run(rate = 0.5)
       boolean run() {
           System.out.println("first");
    
           return true;
       }
       @Report
       void report() {
           System.out.println("report first");
       }
    }
    
    //Second.java
    package com.sigh.test;
    import org.springframework.stereotype.Service;
    /**
     * Created by sigh on 2015/6/9.
     */
    @Simulation
    @Service
    public 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的内存模型了。

    相关文章

      网友评论

      本文标题:使用反射+注解实现类似JUnit的效果

      本文链接:https://www.haomeiwen.com/subject/vukqkttx.html