Java Platform, Enterprise Edition (Java EE) 8
Учебник по Java EE

Назад Вперёд Содержание

Cессионный компонент-синглтон counter

Пример counter демонстрирует, как создать сессионный компонент-синглтон.

Здесь рассматриваются следующие темы:

Создание сессионного компонента-синглтона

Аннотация javax.ejb.Singleton используется для указания того, что класс реализации Enterprise-бина является сессионным компонентом-синглтоном:

@Singleton
public class SingletonBean { ... }

Инициализация сессионных компонентов-синглтонов

Контейнер EJB отвечает за определение того, когда инициализировать объект сессионного бина-синглтона, если только класс реализации сессионного компонента-синглтона не аннотирован javax.ejb.Startup. В этом случае, иногда называемом ранней инициализацией, контейнер EJB должен инициализировать сессионный компонент-синглтон при запуске приложения. Сессионный компонент-синглтон инициализируется до того, как контейнер EJB доставляет клиентские запросы всем Enterprise-бинам в приложении. Это позволяет сессионному компоненту-синглтону выполнять, например, задачи запуска приложения.

Следующий сессионный компонент-синглтон хранит состояние приложения и будет инициализирован рано:

@Startup
@Singleton
public class StatusBean {
  private String status;

  @PostConstruct
  void init {
    status = "Ready";
  }
  ...
}

Иногда для инициализации данных для приложения используются несколько сессионных EJB-компонентов, поэтому их следует инициализировать в определённом порядке. В этих случаях используйте аннотацию javax.ejb.DependsOn для объявления зависимостей запуска сессионного компонента-синглтона. Атрибут value аннотации @DependsOn представляет собой одну или несколько строк, которые задают имя целевого сессионного сессионного компонента-синглтона. Если в @DependsOn указано более одного зависимого компонента-синглтона, порядок их перечисления не обязательно является порядком, в котором контейнер EJB инициализирует целевые сессионные компоненты-синглтоны.

Так, сессионный компонент-синглтон PrimaryBean должен быть запущен первым:

@Singleton
public class PrimaryBean { ... }

SecondaryBean зависит от PrimaryBean:

@Singleton
@DependsOn("PrimaryBean")
public class SecondaryBean { ... }

Это гарантирует, что контейнер EJB инициализирует PrimaryBean раньше SecondaryBean.

Следующий сессионный компонент-синглтон TertiaryBean зависит от PrimaryBean и SecondaryBean:

@Singleton
@DependsOn({"PrimaryBean", "SecondaryBean"})
public class TertiaryBean { ... }

SecondaryBean с помощью аннотации @DependsOn требует, чтобы его инициализация выполнялась после инициализации PrimaryBean. В этом случае контейнер EJB сначала инициализирует PrimaryBean, затем SecondaryBean и, наконец, TertiaryBean.

Однако, если SecondaryBean явно не зависит от PrimaryBean, контейнер EJB может сначала инициализировать либо PrimaryBean, либо SecondaryBean. То есть контейнер EJB может инициализировать синглтоны в следующем порядке: SecondaryBean, PrimaryBean, TertiaryBean.

Управление параллельным доступом в сессионном компоненте-синглтоне

Дизайн сессионных компонентов-синглтонов позволяет одновременный доступ к нему множества клиентов. Клиенту синглтона требуется только ссылка на синглтон для вызова любых бизнес-методов, предоставляемых синглтоном, и ему не нужно беспокоиться о любых других клиентах, которые могут одновременно вызывать бизнес-методы в одном и том же синглтоне.

При создании сессионного компонента-синглтона одновременный доступ к бизнес-методам синглтона можно контролировать двумя способами: параллелизм, управляемый контейнером, и параллелизм, упраляемый Managed-бином.

Аннотация javax.ejb.ConcurrencyManagement используется для указания параллелизма, управляемого контейнером или бином, для синглтона. В @ConcurrencyManagement атрибут типа должен быть установлен либо в javax.ejb.ConcurrencyManagementType.CONTAINER, либо в javax.ejb.ConcurrencyManagementType.BEAN. Если в классе реализации синглтона нет аннотации @ConcurrencyManagement, для управления параллелизмом по умолчанию используется контейнер EJB.

Управляемый контейнером параллелизм

Если в синглтоне используется управляемый контейнером параллелизм, контейнер EJB контролирует клиентский доступ к бизнес-методам синглтона. Аннотации javax.ejb.Lock и javax.ejb.LockType используются для указания уровня доступа для бизнес-методов синглтона или методу @Timeout. Значениями LockType являются READ и WRITE.

Аннотируйте бизнес-метод или метод тайм-аута с помощью @Lock(LockType.READ), если к методу можно одновременно обращаться или совместно использовать его несколькими клиентами. Аннотируйте бизнес-метод или метод тайм-аута с помощью @Lock(LockType.WRITE), если сессионный компонент-синглтон должен быть заблокирован для других клиентов, пока этот метод выполняется одним из клиентов. Как правило, аннотация @Lock(LockType.WRITE) используется, когда клиенты изменяют состояние синглтона.

Аннотирование класса синглтона с помощью @Lock указывает, что все бизнес-методы и любые методы тайм-аута синглтона будут использовать указанный тип блокировки, если они явно не установят тип блокировки с помощью аннотации @Lock уровня метода. Если в классе синглтона нет аннотации @Lock, тип блокировки по умолчанию @Lock(LockType.WRITE) применяется ко всем бизнес-методам и методам тайм-аута.

В следующем примере показано, как использовать аннотации @ConcurrencyManagement, @Lock(LockType.READ) и @Lock(LockType.WRITE) для синглтона, который использует управляемый контейнером параллелизм.

Хотя по умолчанию в синглтонах используется управляемый контейнером параллелизм, аннотация @ConcurrencyManagement(CONTAINER) может быть добавлена ​​на уровне класса синглтона, чтобы явно установить тип управления параллелизмом:

@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Singleton
public class ExampleSingletonBean {
  private String state;

  @Lock(LockType.READ)
  public String getState() {
    return state;
  }

  @Lock(LockType.WRITE)
  public void setState(String newState) {
    state = newState;
  }
}

Метод getState может быть доступен многим клиентам одновременно, потому что он аннотирован с помощью @Lock(LockType.READ). Однако когда вызывается метод setState, все методы в ExampleSingletonBean будут заблокированы для других клиентов, поскольку setState помечен @Lock(LockType.WRITE). Это не позволяет двум клиентам одновременно пытаться изменить переменную state в ExampleSingletonBean.

Методы getData и getStatus в следующем синглтоне имеют тип READ, а метод setStatus имеет тип WRITE:

@Singleton
@Lock(LockType.READ)
public class SharedSingletonBean {
  private String data;
  private String status;

  public String getData() {
    return data;
  }

  public String getStatus() {
    return status;
  }

  @Lock(LockType.WRITE)
  public void setStatus(String newStatus) {
    status = newStatus;
  }
}

Если метод имеет тип блокировки WRITE, клиентский доступ ко всем методам синглтона блокируется до тех пор, пока текущий клиент не завершит свой вызов метода или не истечёт время ожидания доступа. По истечении времени ожидания доступа контейнер EJB выбрасывает исключение javax.ejb.ConcurrentAccessTimeoutException. Аннотация javax.ejb.AccessTimeout используется для указания количества миллисекунд до истечения времени ожидания доступа. Добавление @AccessTimeout на уровне класса синглтона указывает значение времени ожидания доступа для всех методов в синглтоне, если только метод явно не переопределяет значение по умолчанию своей собственной аннотацией @AccessTimeout.

Аннотация @AccessTimeout может применяться к методам @Lock(LockType.READ) и @Lock(LockType.WRITE). Аннотация @AccessTimeout имеет один обязательный элемент value и один необязательный элемент unit. По умолчанию value указывается в миллисекундах. Чтобы изменить единицу value, установите для unit одну из констант java.util.concurrent.TimeUnit: NANOSECONDS, MICROSECONDS, MILLISECONDS или SECONDS.

В следующем синглтоне значение тайм-аута доступа по умолчанию составляет 120 000 миллисекунд или 2 минуты. Метод doTediousOperation переопределяет время ожидания доступа по умолчанию и устанавливает значение 360 000 миллисекунд или 6 минут:

@Singleton
@AccessTimeout(value=120000)
public class StatusSingletonBean {
  private String status;

  @Lock(LockType.WRITE)
  public void setStatus(String new Status) {
    status = newStatus;
  }

  @Lock(LockType.WRITE)
  @AccessTimeout(value=360000)
  public void doTediousOperation {
    ...
  }
}

Следующий синглтон имеет значение тайм-аута доступа по умолчанию 60 секунд, указанное с помощью константы TimeUnit.SECONDS:

@Singleton
@AccessTimeout(value=60, unit=TimeUnit.SECONDS)
public class StatusSingletonBean { ... }

Параллелизм, управляемый Managed-бином

Синглтоны, использующие управляемый Managed-бином параллелизм, обеспечивают полный параллельный доступ ко всем бизнес-методам и методам тайм-аута в синглтоне. Разработчик синглтона отвечает за синхронизацию состояния синглтона между всеми клиентами. Разработчикам, которые создают синглтоны с управляемым бином параллелизмом, разрешается использовать примитивы синхронизации Java, такие как synchronization и volatile, для предотвращения ошибок при одновременном доступе.

Добавьте аннотацию @ConcurrencyManagement с типом, установленным в ConcurrencyManagementType.BEAN на уровне класса синглтона, чтобы указать параллелизм, управляемый Managed-бином:

@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
@Singleton
public class AnotherSingletonBean { ... }

Обработка ошибок в сессионном компоненте-синглтоне

Если при инициализации контейнером EJB сессионный компонент-синглтон выдаст ошибку, он будет уничтожен.

В отличие от других Enterprise-бинов, после инициализации объекта сессионного компонента-синглтона он не уничтожается, если методы бизнес-процесса или жизненного цикла синглтона выбрасывают системные исключения. Это гарантирует, что один и тот же объект синглтона используется на протяжении всего жизненного цикла приложения.

Пример архитектуры counter

Пример counter состоит из сессионного компонента-синглтона CounterBean и веб-интерфейса JavaServer Faces Facelets.

CounterBean — это простой синглтон с одним методом getHits, который возвращает целое число, представляющее количество обращений к веб-странице. Вот код CounterBean:

package javaeetutorial.counter.ejb;

import javax.ejb.Singleton;

/**
 * CounterBean — простой сессионный компонент-синглтон, который записывает число
 * обращений к веб-странице.
 */
@Singleton
public class CounterBean {
    private int hits = 1;

    // Увеличить значение и вернуть результат
    public int getHits() {
        return hits++;
    }
}

Аннотация @Singleton помечает CounterBean как сессионный компонент-синглтон. CounterBean использует локальное представление без интерфейса.

CounterBean использует значения метаданных контейнера EJB по умолчанию для синглтонов, чтобы упростить кодирование класса реализации синглтона. В классе нет аннотации @ConcurrencyManagement, поэтому по умолчанию применяется управляемый контейнером параллельный доступ. В классе или бизнес-методе нет аннотации @Lock, поэтому по умолчанию @Lock(WRITE) применяется к единственному бизнес-методу getHits.

Следующая версия CounterBean функционально эквивалентна предыдущей версии:

package javaeetutorial.counter.ejb;

import javax.ejb.Singleton;
import javax.ejb.ConcurrencyManagement;
import static javax.ejb.ConcurrencyManagementType.CONTAINER;
import javax.ejb.Lock;
import javax.ejb.LockType.WRITE;

/**
 * CounterBean — простой сессионный компонент-синглтон, который записывает число
 * посещений веб-страницы.
 */
@Singleton
@ConcurrencyManagement(CONTAINER)
public class CounterBean {
    private int hits = 1;

    // Увеличивает значение и возвращает результат
    @Lock(WRITE)
    public int getHits() {
        return hits++;
    }
}

Веб-интерфейс counter состоит из Managed-бина JavaServer Faces Count.java, который используется XHTML-файлами Facelets template.xhtml и index.xhtml. Managed-бин JavaServer Faces Count получает ссылку на CounterBean посредством инъецирования зависимостей. Count определяет свойство hitCount JavaBeans. Когда get-метод getHitCount вызывается из файлов XHTML, вызывается метод CounterBean.getHits для возврата текущего числа обращений к странице.

Вот класс Managed-бина Count:

@Named
@ConversationScoped
public class Count implements Serializable {
    @EJB
    private CounterBean counterBean;

    private int hitCount;

    public Count() {
        this.hitCount = 0;
    }

    public int getHitCount() {
        hitCount = counterBean.getHits();
        return hitCount;
    }

    public void setHitCount(int newHits) {
        this.hitCount = newHits;
    }
}

Файлы template.xhtml и index.xhtml используются для визуализации представления Facelets, которое отображает количество совпадений для этого представления. В файле index.xhtml используется оператор языка выражений #{count.hitCount} для доступа к свойству hitCount Managed-бина Count. Вот содержимое index.xhtml:

<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html">
    <ui:composition template="/template.xhtml">
        <ui:define name="title">
            This page has been accessed #{count.hitCount} time(s).
        </ui:define>
        <ui:define name="body">
            Hooray!
        </ui:define>
    </ui:composition>
</html>

Запуск counter

Вы можете использовать IDE NetBeans или Maven для сборки, упаковки, развёртывания и запуска примера counter.

Здесь рассматриваются следующие темы:

Здесь рассматриваются следующие темы:

Запуск counter в IDE NetBeans

  1. Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).

  2. В меню «Файл» выберите «Открыть проект».

  3. В диалоговом окне «Открыть проект» перейдите к:

    tut-install/examples/ejb
  4. Выберите каталог counter.

  5. Нажмите Открыть проект.

  6. На вкладке «Проекты» кликните правой кнопкой мыши проект counter и выберите «Выполнить».

    Веб-браузер откроет URL http://localhost:8080/counter, который отображает количество обращений.

  7. Перезагрузите страницу, чтобы увидеть увеличение количества просмотров.

Запуск counter с использованием Maven

  1. Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).

  2. В окне терминала перейдите в:

    tut-install/examples/ejb/counter/
  3. Введите следующую команду:

    mvn install

    Эта команда собирает и развёртывает counter в GlassFish Server.

  4. В веб-браузере введите следующий URL:

    http://localhost:8080/counter
  5. Перезагрузите страницу, чтобы увидеть увеличение количества просмотров.


Назад Вперёд Содержание
Логотип Oracle  Copyright © 2017, Oracle и/или её дочерних компаний. Все права защищены. Версия перевода 1.0.5 (Java EE Tutorial — русскоязычная версия)