Масштабируемость и потокобезопасность управляемых методов в области приложений

#jsf

#jsf

Вопрос:

Во время тестирования была выявлена слабость в том, как наше приложение создает f:selectItems списки, в частности, ввод действительно длинных имен для некоторых наших объектов улучшает выравнивание страницы, делая действительно широкие выборки.

Многие из этих списков SelectItem дублируются в нескольких представлениях и вспомогательных компонентах, поэтому я хотел бы объединить их создание.

У нас уже есть компонент в области приложений, который предоставляет список<SelectItem> для перечислений, и моей первоначальной мыслью было разместить их там.

Однако у меня есть несколько вопросов. Мы используем jsf 1.2 (если это имеет значение)

1) Насколько я понимаю, компоненты в области приложений являются одноэлементными просто потому, что создается один экземпляр и помещается в контекст сеанса. Они не похожи на синглтоны EJB3 в том смысле, что только один поток может получить доступ к любому методу, поэтому множественные запросы не будут блокировать попытки доступа к разным методам. Это правильно?

2) Я подозреваю, что каждый метод должен быть синхронизирован, чтобы несколько потоков, вызывающих один и тот же метод, не загромождали друг друга. Так ли это, даже если единственным членом класса, к которому осуществляется доступ в методе, является потокобезопасный @EJB без состояния?

Ниже приведена реализация одного из них, который будет использоваться в 20 представлениях. Реализации для 10 других объектов будут аналогичными. Также зарегистрированы соответствующие преобразователи.

 public synchronized List<SelectItem> getAccountSelect(){
    List<Account> list = new ArrayList<Account>(pemEJB.list(Account.class));
    Collections.sort(list, new AccountByActiveByName());
    List<SelectItem> result=new ArrayList<SelectItem>(list.size());
    for(Account row : list){
        result.add(new SelectItem(row, 
                StringUtil.prefixTruncate(row.getName(), MAX_ACCT_LENGTH, row.isActive())));
    }
    return resu<
}
  

Приветствуются любые советы

Ответ №1:

Если действительно обязательно выполнять загрузку данных в геттере, а не в конструкторе / postconstruct, то определенно нет смысла делать это компонентом в области приложений. Просто сделайте это в области запроса, где вы выполняете задание загрузки данных в конструкторе / postconstruct.

Комментарии:

1. Спасибо, лампочка только что погасла. Я застрял в мышлении, основанном на обратных представлениях beans, и никогда не рассматривал простое использование отдельного компонента запроса из всех представлений

Ответ №2:

В приложениях на jsf, над которыми я работаю, мы загружаем почти все наши справочные данные (в первую очередь значения для selectonemen-ов) в компоненты области приложений и устанавливаем значения в конструкторе этих компонентов. Затем данные становятся доступными для других управляемых компонентов и представлений через средства получения, но глобализируются и централизовываются для приложения. Поскольку значения считываются только с помощью методов получения, нет необходимости в синхронизации.

Затем мы предоставляем beans в виде mbeans через jmx с методом перезагрузки, чтобы их можно было обновлять по мере необходимости. Методы перезагрузки синхронизированы таким образом, чтобы блокировать во время коротких перезагрузок.

В вашем примере выше кажется, что вы могли бы просто вернуть коллекцию SelectItems, поэтому, пока значения настроены заранее, вы можете использовать этот метод и по-прежнему отлично обслуживать несколько потоков:

 public List<SelectItem> getAccountSelectItems() {
    return this.accountSelectItems;
}
  

Просто добавьте этот закрытый элемент в свой компонент:

 private List<SelectItem> accountSelectItems;
  

и настройте это в конструкторе:

 public AccountBean() {
    List<Account> list = new ArrayList<Account>(pemEJB.list(Account.class));
    Collections.sort(list, new AccountByActiveByName());
    this.accountSelectItems = new ArrayList<SelectItem>(list.size());

    for(Account row : list) {
        this.accountSelectItems.add(new SelectItem(row, StringUtil.prefixTruncate(row.getName(), MAX_ACCT_LENGTH, row.isActive())));
    }
}
  

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

Комментарии:

1. К сожалению, это требование для сборки по запросу. Я думаю, что отдельный компонент с ограниченной областью запроса — это правильный путь.