2011-12-17 2 views
4

В Digester странное поведение, что я не могу окутать голову.Digester 3 дважды вызывает конструкторы при создании объектов

У меня есть следующий код, который называют конструктор «роль» объекта всякий раз, когда он сталкиваются с «ролей/роль» узел во входном XML:

 AbstractRulesModule loader = (new AbstractRulesModule() { 

     protected void configure() { 
      forPattern("roles/role").createObject().ofType(Role.class) 
        .usingConstructor(String.class, String.class).then() 
        .callParam().fromAttribute("machine").ofIndex(0); 

      forPattern("roles/role").callParam().fromAttribute("name") 
        .ofIndex(1); 

      forPattern("roles/role").setNext("add"); 

     } 
    }); 

    Digester digester = DigesterLoader.newLoader(loader).newDigester(); 
    List<Role> roles = new ArrayList<>(); 

    digester.push(roles); 

    digester.parse(new File("c:/RoleMapping.xml")); 

    System.out.println(roles); 
    System.out.println(Role.count); 

Каждый раз, когда конструктор роль, называется, Role.count увеличивается. Как ни странно, после выполнения приведенного выше кода в отношении следующего xml, Role.count равен 2 вместо 1. Когда я отлаживаю код, кажется, что Digester попытался создать 2 дополнительных объекта с «нулем» в качестве параметров конструктора.

<roles> 
    <role name="m1" machine="mymachine" /> 
</roles> 

Это привело бы к любой проблеме, если у меня есть проверка кода, если аргументы конструктора равны нулю.

Определение моего класса Роли является:

public class Role { 

    private String machine; 
    private String name; 

    static int count = 0; 

    public Role(String machine, String name) { 
     this.machine = machine; 
     this.name = name; 
     count++; 
    } 
} 
+0

Я также заметил это поведение с digester 3.2, пытаясь найти некоторые проблемы с моими конструкторами объектов, которые вызываются только с нулевыми параметрами. Вы узнали, нормально ли звонить дважды? Также были ли у вас какие-либо проблемы с вызовом вашего конструктора с неисправными параметрами? – gsnerf

ответ

0

Я вижу вопрос в 3-х лет, но я наткнулся на это же самое последнее время, и ответ остается в силе ...

причина, по которой конструктор вызывается дважды, - это то, как Digester 3 обрабатывает конструкторы с параметрами. Проблема для Digester - это курица и яйцо ... она не может вызвать конструктор, пока не будет иметь все необходимые параметры, но поскольку правила callParam могут получать свои данные из дочерних элементов, у него нет всех дочерних элементов, пока он не будет полностью обработал элемент.

В вашем случае все параметры доступны в атрибутах, но учтите, если вы изменили XML в:

<roles> 
    <role> 
     <name>m1</name> 
     <machine>mymachine</machine> 
    </role> 
</roles> 

Или даже:

<roles> 
    <role> 
     <name>m1</name> 
     <machine>mymachine</machine> 
     <another> 
      <tag>which</tag> 
      <does>morestuff</does> 
      ... 
     </another> 
    </role> 
</roles> 

Варочный котел эффективно должен помнить все что происходит между <role> и </role>, так как правила param вызова можно было бы вызывать где угодно в дочерних данных, и он должен сделать все это до создания объекта.

Для этого генератор создает прокси-оболочку вокруг создаваемого класса (Роль), создает фиктивный экземпляр, передающий значение null для всех аргументов конструктора, а затем вызывает все другие методы, запускаемые для дочерних элементов основного элемента. Прокси-класс перехватывает эти вызовы методов, записывает их (включая параметры) и передает их на фиктивный экземпляр. Как только тег конечного элемента будет достигнут, фиктивный объект будет отброшен, новый будет создан с реальными параметрами конструктора, и все записанные вызовы методов будут «повторно воспроизведены» обратно на новый объект.

Как вы заметили, это не только создает объект дважды, но и вызывает все методы, запускаемые правилами digester дважды: один раз во время фазы «записи» и один раз во время фазы «воспроизведения».

Все это прекрасно работает для простых объектов данных, но может иметь странные последствия при построении более сложных. См. Пример this digester ticket.

Чтобы просто избежать нулевых исключений указателя, вы можете сказать, варочный котел, какие значения использовать для параметров конструктора по умолчанию с помощью usingDefaultConstructorArguments правила:

forPattern("roles/role").createObject().ofType(Role.class) 
    .usingConstructor(String.class, String.class).then() 
    .usingDefaultConstructorArguments("one", "two").then() 
    .callParam().fromAttribute("machine").ofIndex(0); 

Для более сложных случаев, или просто, если вы предпочитаете подход, вы может использовать класс строителя и настраиваемое правило. Основная идея заключается в том, что вы получаете элемент, который вы нажимаете на класс строителя, в стек, а также настраиваемое правило, инициируемое в теге конца элемента. При обработке тела элемента, digester вызывает все правила как обычные передачи данных в класс строителя. В конце тега запускается настраиваемое правило, которое вызывает построитель для построения объекта, а затем заменяет объект строителя на стеке накопителя на встроенный объект. Для этого требуется специальный класс строителя, но он намного проще, чем кажется. См. this digester ticket для рабочего примера.

Надеюсь, что это очистит тайну!

Смежные вопросы