Необъяснимое поведение Singleton / Picocli - PullRequest
1 голос
/ 08 мая 2020

Я пишу код и не могу понять, что происходит с моей ошибкой. Я надеюсь, что кто-то здесь может дать мне несколько ответов. Вот мой код (соответствующая часть):

public class AppData implements Callable<Integer> {
    private static AppData appData = new AppData();

    private AppData() {
        System.out.println("AppData-Constructor");
    }

    public static AppData getInstance() {
        return appData;
    }

    @Override
    public Integer call() throws Exception { // your business logic goes here...
        return 0;
    }

    private boolean _validate;

    public boolean validate() {
        return _validate;
    }

    @Option(names = { "--validate" }, description = "", defaultValue = "false", hidden = false, interactive = false, paramLabel = "", required = false, type = boolean.class)
    public void set_validate(boolean validate) {
        System.out.println("Set Validate: " + validate);
        this._validate = validate;

        if(validate)
        {
            System.out.println("\nBeginne Programmvalidierung\n");
            Path tmp = null;
            try {
                // Doing some validation stuff
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

Как видите, мой класс - синглтон. Аннотация взята из пиколи, которую я использую для анализа аргументов командной строки. Вызовы System.out предназначены для отладки. Это поведение, которое я не могу объяснить:

Когда я запускаю свое приложение, например, с «-h» в качестве аргумента, я прекрасно получаю помощь. System.out.println показывает, что синглтон создан и что set_validate() вызывается со значением по умолчанию. Но это меняется, когда я использую --validate в качестве аргумента.

По какой-то причине конструктор и набор по умолчанию вызываются дважды подряд. После этого вызывается set_validate() с true (как и должно). Однако кажется, что первый вызов устанавливает переменную экземпляра stati c, а последний вызов со значением true выполняется во втором экземпляре (моя теория). Как следствие, когда я проверяю состояние _validate с помощью validate() в моем экземпляре singleton из моего основного метода (в другом классе), я получаю false, поскольку он не был установлен в правильном экземпляре.

Я использовал поисковую систему, чтобы проверить:

  1. Конструктор не вызывается нигде, кроме экземпляра stati c singleton (как и ожидалось, поскольку он частный).
  2. _validate нигде не доступен, кроме кода, который я опубликовал.
  3. set_validate() нигде не вызывается. Только Пикокли называет это.

Я не знаю, что проверить дальше. Любые идеи?

С уважением

Thorsten

EDIT: AppData - это один из нескольких классов, содержащих данные. Все они собраны в один большой класс для Picocli, например:

class Data
{
    @AddGroup(...)
    AppData appData = AppData.getInstance();

    @AddGroup(...)
    FooData fooData = FooData.getInstance();

    @AddGroup(...)
    BarData barData = BarData.getInstance();
}

В моем основном методе он используется так:

Data data = new Data();
CommandLine cmd = new CommandLine(data);
cmd.parseArgs(args);

1 Ответ

1 голос
/ 09 мая 2020

Я подозреваю (но могу только догадываться, поскольку эта часть кода не показана), что AppData является либо подкомандой другой команды, либо приложение использует picocli следующим образом:

int exitCode = new CommandLine(AppData.class).execute(args);

В обоих случаях picocli создаст экземпляр AppData, используя отражение. Экземпляр, созданный picocli, является экземпляром, заполненным значениями командной строки. Это другой экземпляр, чем тот, который возвращает AppData::getInstance.

Один из способов гарантировать наличие только одного экземпляра - передать одноэлементный экземпляр в picocli. Например:

AppData singleton = AppData.getInstance();
int exitCode = new CommandLine(singleton).execute(args);

System.out.println("validate=" + singleton.validate());

(Если AppData является подкомандой, существуют другие способы доступа к экземпляру, созданному picocli, например, @Spec аннотация для внедрения модели picocli, и вызывая CommandSpec::userObject() getter для получения экземпляра AppData.)

Теперь другой вопрос: почему метод set_validate вызывается дважды?

Начиная с версии 4.2, picocli сначала вызовет @Option -аннотированные методы со значением по умолчанию перед синтаксическим анализом параметров командной строки. Таким образом, метод set_validate сначала вызывается со значением по умолчанию, а затем вызывается снова со значением, указанным в командной строке.

(Начиная с версии 4.3 (которая скоро будет выпущена), значение по умолчанию будет устанавливается только в том случае, если значение не указано в командной строке, поэтому с версии 4.3 метод set_validate будет вызываться только один раз.)

...