Добавить отсутствующее свойство в вызове конструктора bean-компонента @Canonical?

Я новичок в Groovy и только начал изучать его возможности метапрограммирования. Я застрял с добавлением недостающих свойств при вызове конструктора бина.

В классе, который будет использоваться с FactoryBuilderSupport, я хочу динамически добавлять те свойства, которые еще не определены и предоставлены во время вызова конструктора. Вот урезанная версия:

@Canonical
class MyClass {
    def startDate
    def additionalProperties = [:]

    def void propertyMissing(String name, value) {
        additionalProperties[name] = value
    }
}

Тем не менее, если я создаю класс с неизвестными свойствами, proprty не добавляется, но я получаю MissingPropertyException вместо:

def thing = new MyClass(startDate: DateTime.now(), duration: 1234)

Продолжительность свойства не существует, и я ожидал, что она будет обработана с помощью propertyMissing, Насколько я понимаю, groovy, вызов конструктора tuple приводит к вызову конструктора без аргументов, за которым следуют вызовы сгенерированных groovy сеттеров. Так почему я получаю MissingPropertyException?

Поскольку я новичок в Groovy, я, вероятно, скучаю по некоторым основным правилам AST или MOP. Я был бы очень признателен за вашу помощь.

1 ответ

Решение

Если вы используете @Canonical и вы определяете объект первого класса с def как вы делаете с startDate аннотация генерирует следующие конструкторы:

@Canonical
class MyClass {
    def startDate
    def additionalProperties = [:]

    def void propertyMissing(String name, value) {
            additionalProperties[name] = value
    }
}

// use reflection to see the constructors
MyClass.class.getConstructors() 

Сгенерированные конструкторы:

public MyClass() 
public MyClass(java.lang.Object)
public MyClass(java.util.LinkedHashMap)
public MyClass(java.lang.Object,java.lang.Object)

в @Canonical В документации вы можете увидеть следующие ограничения:

Обычные правила именования в стиле карты Groovy будут недоступны, если первое свойство имеет тип LinkedHashMap или если имеется одно свойство Map, AbstractMap или HashMap

Из-за public MyClass(java.util.LinkedHashMap) генерируется вы не можете использовать tuple-constructor и вы получите MissingPropertyException,

Удивительно, если вы определите свой first объект (обратите внимание, что я говорю first) с type Вместо того, чтобы использовать def, @Canonical аннотация не добавляет public MyClass(java.util.LinkedHashMap) а потом твой tuple-constructor вызов работает, см. следующий код:

@Canonical
class MyClass {
    java.util.Date startDate
    def additionalProperties = [:]

    def void propertyMissing(String name, value) {
            additionalProperties[name] = value
    }
}
// get the constructors
MyClass.class.getConstructors()
// now your code works
def thing = new MyClass(startDate: new java.util.Date(), duration: 1234)

Теперь созданные конструкторы:

public MyClass()
public MyClass(java.util.Date)
public MyClass(java.util.Date,java.lang.Object)

Так как нет public MyClass(java.util.LinkedHashMap) ограничение не распространяется, и вы tuple-constructor звонок работает.

Кроме того, я хочу сказать, что, поскольку это решение работает, я не могу спорить, почему... Я прочитал @Canonical документация снова и снова, и я не вижу той части, где описывается это поведение, поэтому я не знаю, почему это работает, также я делаю некоторые попытки, и я немного сбиваю с толку, только когда first элемент def public MyClass(java.util.LinkedHashMap) создан то есть:

@Canonical
class MyClass {
    def a
    int c
}
// get the constructors
MyClass.class.getConstructors()

Первый объект, определенный как def...

public MyClass()
public MyClass(java.lang.Object)
public MyClass(java.util.LinkedHashMap) // first def...
public MyClass(java.lang.Object,int)

Теперь, если я изменю порядок:

@Canonical
class MyClass {
    int c
    def a
}
// get the constructors
MyClass.class.getConstructors()

Теперь сначала нет def а также public MyClass(java.util.LinkedHashMap) не генерируется:

public MyClass() 
public MyClass(int)
public MyClass(int,java.lang.Object)

Надеюсь это поможет,

Другие вопросы по тегам