在多个项目中使用Java反射的奇怪问题

时间:2018-11-11 12:41:12

标签: java maven unit-testing intellij-idea

我创建了一个实现状态机的Java项目。其背后的想法是,SM本身在后台线程中运行,就像在代表不同动作的对象数组上的简单循环一样。

有问题的动作来自另一个名为ActionPack的对象,其中包含Action对象。

使用Java反射在运行时查看实例化的ActionPack并将其导入到状态机中,然后将它们推入数组中,效果很好。然后,状态机在被告知运行时遍历数组,并调用每个动作的execute方法。

正如我所说,这很好,并且所有单元测试都是绿色的……但是只有当我从IntelliJ中运行它们时才可以。如果我尝试通过“ mvn测试”运行它们,则反射代码无法在操作包中找到这些类。

这是失败的反射方法:

  /**
   * Use introspection to read in all of the classes in a given action pack
   * Filter out the ones that are actions and return them in an array
   */
  public ArrayList getActionsFromActionPack(
    DataAccessLayer dataAccessLayer,
    String runRoot
  ) {

    String packageName = this.getClass().getPackage().getName();

    System.out.println("PACKAGE NAME = " + packageName);

    List<ClassLoader> classLoadersList = new LinkedList<>();
    classLoadersList.add(ClasspathHelper.contextClassLoader());
    classLoadersList.add(ClasspathHelper.staticClassLoader());

    Reflections reflections = new Reflections(new ConfigurationBuilder()
      .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
      .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
      .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix(packageName))));

    Set<Class<? extends Action>> classes = reflections.getSubTypesOf(Action.class);

    System.out.println("Number of classes = " + classes.size());

    ArrayList<IAction> actions = new ArrayList<>();

    for (Class a : classes) {
      try {
        Action action = (Action) Class.forName(a.getName()).newInstance();
        // Give the action access to the DAL and path to control directory
        action.setDataAccessLayer(dataAccessLayer);
        action.setRunRoot(runRoot);
        actions.add(action);
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
        System.err.println(e.getClass().getName() + ": " + e.getMessage());
        System.exit(1);
      } catch (IllegalAccessException e) {
        e.printStackTrace();
        System.err.println(e.getClass().getName() + ": " + e.getMessage());
        System.exit(1);
      } catch (InstantiationException e) {
        e.printStackTrace();
        System.err.println(e.getClass().getName() + ": " + e.getMessage());
        System.exit(1);
      }
    }
    return actions;
  }

有了这些调试打印语句,当我从IntelliJ中运行单元测试时,我看到以下输出:

    PACKAGE NAME = com.github.museadmin.infinite_state_machine.core.action_pack
    Number of classes = 7
    PACKAGE NAME = com.github.museadmin.infinite_state_machine.core.action_pack
    Number of classes = 7
    PACKAGE NAME = com.github.museadmin.infinite_state_machine.core.action_pack
    Number of classes = 7
    PACKAGE NAME = com.github.museadmin.infinite_state_machine.test.classes
    Number of classes = 1
    PACKAGE NAME = com.github.museadmin.infinite_state_machine.core.action_pack
    Number of classes = 7
    PACKAGE NAME = com.github.museadmin.infinite_state_machine.core.action_pack
    Number of classes = 7

    Process finished with exit code 0

但是当我通过mvn test运行时:

    PACKAGE NAME = com.github.museadmin.infinite_state_machine.core.action_pack
    Number of classes = 0
    Exception in thread "UnitTestThread" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
        at java.util.ArrayList.rangeCheck(ArrayList.java:653)
        at java.util.ArrayList.get(ArrayList.java:429)
        at com.github.museadmin.infinite_state_machine.core.InfiniteStateMachine.run(InfiniteStateMachine.java:36)
        at java.lang.Thread.run(Thread.java:745)

因此,看起来反射方法可以正确识别我们所在的包,但是如果通过maven调用,则无法在其中找到类。

当我调用importActionPack时,失败发生在单元测试的setup()的最后一行中

      @Before
      public void setup() {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        URL is = loader.getResource(PROPERTIES);
        ismCoreActionPack = new ISMCoreActionPack();
        infiniteStateMachine = new InfiniteStateMachine(is.getPath());
        infiniteStateMachine.importActionPack(ismCoreActionPack);
      }

此代码分布在三个项目中:

infinite-state-machine (The state machine itself)
infinite-state-machine-common (Where the action pack and action types are defined)
infinite-state-machine-core (The action pack I'm failing to import)

Action Pack在无限状态机通用中具有一个接口:

    package com.github.museadmin.infinite_state_machine.common.action;

    import com.github.museadmin.infinite_state_machine.common.dal.DataAccessLayer;
    import org.json.JSONObject;
    import java.util.ArrayList;

    public interface IActionPack {
      JSONObject getJsonObjectFromResourceFile(String fileName);
      ArrayList getActionsFromActionPack(
        DataAccessLayer dataAccessLayer,
        String runRoot
      );
    }

动作包的实际实现也处于无限状态机通用状态:

    public class ActionPack implements IActionPack

这就是找到反射方法public ArrayList getActionsFromActionPack()的地方。

Action类及其接口也处于无限状态机通用状态:

    package com.github.museadmin.infinite_state_machine.common.action;

    import com.github.museadmin.infinite_state_machine.common.dal.DataAccessLayer;
    import org.json.JSONObject;

    import java.util.ArrayList;

    public interface IAction {
      void execute();

      Boolean active();
      boolean active(String action);
      void activate(String actionName);
      Boolean afterActionsComplete();
      Boolean beforeActionsComplete();
      void clearPayload(String actionName);
      String createRunDirectory(String directory);
      void deactivate();
      void deactivate(String actionFlag);
      JSONObject getJsonObjectFromFile(String fileName);
      ArrayList<JSONObject> getUnprocessedMessages();
      void insertMessage(JSONObject message);
      void insertProperty(String property, String value);
      void markMessageProcessed(Integer id);
      void setDataAccessLayer(DataAccessLayer dataAccessLayer);
      void setRunRoot(String runRoot);
      String queryProperty(String property);
      String queryRunPhase();
      void setState(String stateName);
      void updatePayload(String actionName, String payload);
      void updateProperty(String property, String value);
      void updateRunPhase(String runPhase);
      void unsetState(String stateName);
    }

Action父类本身太大,无法在此处发布,但在同一包中定义:

public abstract class Action implements IAction {

}

如果有帮助,这是核心程序包中显示出execute方法的典型动作:

    package com.github.museadmin.infinite_state_machine.core.action_pack;

    import com.github.museadmin.infinite_state_machine.common.action.Action;

    /**
     * Action that verifies the state of the ISM and confirms
     * we have booted up correctly and can now run
     */
    public class ActionConfirmReadyToRun extends Action {

      /**
       * Confirm we've got through the bootstrapping process ok.
       * and all BEFORE actions completed
       */
      public void execute() {
        if (active()) {
          if (beforeActionsComplete()) {
            setState("READY_TO_RUN");
            updateRunPhase("RUNNING");
            deactivate();
          }
        }
      }
    }

摘要

我不知道这是我使用Reflections库的方式是否有问题,因为当我从IntelliJ运行测试时,我希望它会失败...

那么也许这是Maven的问题?

我已经构建并本地安装了所有项目,因此,目前肯定可以使用并构建它们。

仅供参考-目前,我已将单元测试放在各自的项目中,以防止在将项目推送到Nexus时此问题破坏构建。

对于调试此问题的任何帮助或指导,将不胜感激。

布拉德

0 个答案:

没有答案