Почему в этом случае работает @PostConstruct подкласса?

У меня есть следующая структура Spring Bean:

public abstract class XmlBaseChild {
    protected Integer value;
    protected String text;

@Autowired
transient protected MasterCodeService masterCodeService;



    public XmlBaseChild(Integer value) {
        setValue(value);
    }

    /**
     * Set the Numeric value of the ChildView.
     * This code is common for all childViews and handles a null value.
     * @param value Numeric value of the ChildView
     */
    @JsonProperty(value="id")
    public void setValue(Integer value) {
        if (value == null) {
                this.value = null;
                this.text = null;
                return;
        }
        setConcreteValue(value);
    }

    /**
     * Set the Numeric value of the ChildView.
     * This code must be overridden by the concrete childViews.
     * @param value Numeric value of the ChildView
     */
    protected void setConcreteValue(Integer value){
        boolean keyNotFound = true;
        if (value != null && value > -1) {
            this.value = value;
            String messageKey = getValueFromMap(value, GetMasterCodeMapForChildView());
            if (messageKey != null) {
                this.text = LocalizeString(messageKey, null, getLocale);
                keyNotFound = false;
            }
        }
        if (keyNotFound){
            throw new NotFoundException();
        }
    }

    protected abstract Map<String, MasterCodeView> GetMasterCodeMapForChildView();
}

И подкласс:

@Component
@XmlRootElement(name=XmlDeployTool.VIEW_NAME)
public class XmlDeployTool extends XmlBaseChild {

    public static Map<String, MasterCodeView> toolTypeCodes = new HashMap<String, MasterCodeView>();


    /**
     * Constructor for creating this object and preparing for marchalling (from java objects to xml/json).
     * @param value         Numeric value of the ChildView
     * @param request       HttpServletRequest
     * @param includeSelf   Include SELF link
     * @param includeUP     Include UP link
     */
    public XmlDeployTool(Integer value) {
        super(value);
    }

    /**
     * Initialize the Tool Type codes after the component is wired (postconstruct),
     * so that they are available in the constructor when an XmlDeploy object is created.
    */
    @PostConstruct
    protected void initializeDeployToolTypeCodes() {
    toolTypeCodes = convertListToMap(masterCodeService.getToolTypeCodes());
    }
    @Override
    protected Map<String, MasterCodeView> GetMasterCodeMapForChildView() {
        return toolTypeCodes;
    }
}

Однако, насколько я понимаю из других сообщений, таких как Порядок @PostConstruct и наследования, @PostConstruct здесь обычно выполняется ПОСЛЕ вызова конструктора. Тогда почему карта toolTypeCodes заполняется во время конструктора? Является ли это частью аннотации @Component Spring?

Я также пытался сделать это с картой masterCodeView, определенной в XmlBaseChild, и только с методом PostConstruct, определенным в классе XmlDeployTool, но это не сработало, в этом случае список не инициализировался. Почему это?


person Nzall    schedule 21.05.2019    source источник
comment
Можете ли вы успешно создать XmlDeployTool? Конструктору требуется bean-компонент типа 'java.lang.Integer', который не может быть найден.   -  person jin    schedule 22.05.2019
comment
@jin Я не включал импорт, и я упростил свои конструкторы, так как у них была куча лишнего кода, который, насколько мне известно, не имел отношения к делу.   -  person Nzall    schedule 22.05.2019
comment
Здесь происходит много всего, по-видимому, это нужно JAXB, но это также объект Spring. Что это за штуковина, похоже, вы делаете то, что не должны делать.   -  person M. Deinum    schedule 22.05.2019
comment
@M.Deinum Пример: в проекте определен DeployTool, который может быть одним из 4 типов инструментов, определенных картой toolTypeCodes MasterCodeView, с типом инструмента в базе данных, являющимся строкой. При выполнении вызова Project REST объект XmlDeployTool создается посредством отражения, и как часть конструктора создается локализованное имя для типа инструмента. Таким образом, большая часть повторяющегося кода для извлечения и локализации ссылочного ресурса дедуплицируется.   -  person Nzall    schedule 22.05.2019


Ответы (1)


Проверив документацию и прочитав еще немного, я понял, что здесь происходит:

  1. Поскольку мой подкласс помечен @Component, PostConstruct срабатывает как часть процесса запуска Spring, даже до любых вызовов обычного конструктора. Из-за этого статическая карта с MasterCodeViews заполняется, и, поскольку она является статической, она остается заполненной как часть статических свойств подкласса. Из-за этого эта карта имеет правильные данные, которые можно использовать во время построения.

  2. Когда я попытался переместить карту в базовый класс, фактически я превратил это из статического свойства подкласса в статическое свойство подкласса, что означало, что каждый конструктор, в свою очередь, заполнил его отдельными свойствами, что привело к карте, имеющей неверные данные большую часть времени. Когда после этого я попытался сделать это с нестатической картой, данные не сохранились, когда я вызвал конструктор из кода, потому что это фактически был новый объект без инициализированных компонентов.

person Nzall    schedule 22.05.2019
comment
@PostConstruct просто нельзя вызывать для несконструированного объекта, поэтому пункт 1 совершенно не имеет смысла. Java просто запрещает это... - person M. Deinum; 22.05.2019
comment
@M.Deinum Spring, по-видимому, создает объект как часть аннотации компонента. Хотя кажется, что этот конкретный способ сделать это не совсем предназначен. - person Nzall; 22.05.2019
comment
То, что вы заявляете, просто невозможно, вы не можете вызвать метод для несконструированного объекта. Это просто невозможно в Java, и даже Spring не может обойти это. - person M. Deinum; 22.05.2019
comment
@M.Deinum M.Deinum Да, но Spring действительно создает экземпляр любого объекта, отмеченного аннотацией Component. Они предназначены для внедрения в контроллеры либо с помощью аннотации Autowired (вы можете увидеть ее в XmlBaseChild), либо с помощью WebApplicationContext.getBean(). Это происходит как часть процесса запуска Spring. Что я имел в виду под любым вызовом обычного конструктора, так это то, что это происходит до того, как происходят какие-либо вызовы REST, и они могут сами вызывать конструктор. - person Nzall; 22.05.2019
comment
Spring вызывает после создания экземпляра. Вы заполняете статическую карту, чтобы она была доступна для всех экземпляров. То, что вы построили, по-прежнему является уродливой штуковиной. В этой работе слишком много магии. - person M. Deinum; 22.05.2019
comment
@ M.Deinum Да, это не так уж и красиво. Я, вероятно, задам вопрос об этом завтра в Code Review, когда вернусь к работе. - person Nzall; 22.05.2019
comment
Кроме того, если вы вводите их и строите их самостоятельно? Тогда зачем использовать это хитроумное изобретение? Если вы его сконструируете, то зачем все это волшебство? - person M. Deinum; 23.05.2019
comment
@M.Deinum M.Deinum Я создал вопрос об этом коде в Code Review. Его можно найти по адресу codereview.stackexchange.com/questions/220810/, и вы, вероятно, сможете лучше давать подробные отзывы и комментарии по Структура и работа там. - person Nzall; 23.05.2019