5.8.4. Выполнение кода на старте приложения

Иногда бывает необходимо выполнить некоторый код сразу после старта приложения в момент, когда все механизмы гарантированно работоспособны. Для этого можно воспользоваться слушателем AppContext.Listener.

Рассмотрим следующую задачу: в проекте имеется сущность Employee (сотрудник компании), которая связана один-к-одному с платформенной сущностью User (пользователь системы):

Если атрибут name сущности User изменяется, например через стандартный экран управления пользователями, необходимо, чтобы изменялся также и атрибут name связанной сущности Employee. Это обычная задача для "денормализованных" данных, и решается она, как правило, с использованием entity listeners. В данном случае ситуация осложняется тем, что необходимо отслеживать изменения не проектной, а платформенной сущности User, и добавить entity listener с помощью аннотации @Listeners невозможно. Однако, можно добавить listener динамически через бин EntityListenerManager, и сделать это лучше всего на старте приложения.

Для этого создадим в модуле core приложения бин AppLifecycle, имплементирующий интерфейс AppContext.Listener, и зарегистрируем его вызовом метода AppContext.addListener() в конструкторе объекта:

@ManagedBean("sample_AppLifecycle")
public class AppLifecycle implements AppContext.Listener {

    @Inject
    private EntityListenerManager entityListenerManager;

    public AppLifecycle() {
        AppContext.addListener(this);
    }

    @Override
    public void applicationStarted() {
        entityListenerManager.addListener(User.class, UserEntityListener.class);
    }

    @Override
    public void applicationStopped() {
    }

    public static class UserEntityListener implements BeforeUpdateEntityListener<User> {
        @Override
        public void onBeforeUpdate(User user) {
            Persistence persistence = AppBeans.get(Persistence.class);
            if (persistence.getTools().getDirtyFields(user).contains("name")) {
                EntityManager em = persistence.getEntityManager();
                TypedQuery<Employee> query = em.createQuery(
                        "select e from sample$Employee e where e.user.id = ?1", Employee.class);
                query.setParameter(1, user.getId());
                Employee employee = query.getFirstResult();
                if (employee != null) {
                    employee.setName(user.getName());
                }
            }
        }
    }
}

В результате сразу после старта блока Middleware будет вызван метод applicationStarted() данного бина. В этом методе в качестве entity listener сущности User регистрируется внутренний класс UserEntityListener.

Метод onBeforeUpdate() класс UserEntityListener будет вызываться перед каждым сохранением изменений экземпляров User в базу данных. В методе проверяется, есть ли атрибут name среди измененных, и если да, загружается связанный экземпляр Employee, и в нем устанавливается это же значение name.