Статьи

ADF: область поддержки компонента в финализаторе потока задач

Введение
Это очень распространенная и рекомендуемая практика использования финализаторов потока задач, когда нам нужно выполнить какое-то окончательное задание (очистить ресурсы, закрыть соединения и т. Д.), Прежде чем поток задач исчезнет. Как обычно, мы работаем с управляемыми bean-компонентами, объявленными внутри потока задач. Управляемые bean-компоненты могут иметь различные области действия — запрос, поток страниц, представление, вспомогательный bean-компонент и т. Д. Область действия зависит от того, для чего фактически используется bean-компонент. Существует небольшая проблема, когда мы получаем доступ к управляемому компоненту области backingBean в финализаторе. Давайте посмотрим на пример ниже.
У нас ограниченный поток задач с фрагментами страницы:
И мы управляли bean-компонентами в потоке задач трех разных областей действия — page page, view и backingBean:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<managed-bean id="__3">
 <managed-bean-name id="__5">FlowBean</managed-bean-name>
 <managed-bean-class id="__4">view.BackBean</managed-bean-class>
 <managed-bean-scope id="__2">pageFlow</managed-bean-scope>
</managed-bean>
<managed-bean id="__9">
 <managed-bean-name id="__6">ViewBean</managed-bean-name>
 <managed-bean-class id="__7">view.BackBean</managed-bean-class>
 <managed-bean-scope id="__8">view</managed-bean-scope>
</managed-bean>
<managed-bean id="__10">
 <managed-bean-name id="__11">BackBean</managed-bean-name>
 <managed-bean-class id="__12">view.BackBean</managed-bean-class>
 <managed-bean-scope id="__13">backingBean</managed-bean-scope>
</managed-bean>
На странице у нас есть три кнопки, связанные с управляемыми bean-компонентами каждой области:
1
2
3
4
5
6
7
8
9
<af:commandButton text="commandButton 1" id="cb1"
   action="go" binding="#{backingBeanScope.BackBean.button}">
</af:commandButton>
 
<af:commandButton text="commandButton 1" id="cb2" 
  binding="#{viewScope.ViewBean.button}"/>
 
<af:commandButton text="commandButton 1" id="cb3" 
  binding="#{pageFlowScope.FlowBean.button}"/>
Класс EJB имеет атрибут button и атрибут testString, который указывает, назначена ли кнопка:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
private RichCommandButton button;
 
public void setButton(RichCommandButton button)
{
  this.button = button;
}
 
public RichCommandButton getButton()
{
  return button;
}
 
public String getTestString()
{
  if (this.button == null)
    return "The button is not assigned";
  else
    return "The button is assigned";
}
Когда мы нажимаем cb1, мы переходим к операции возврата и выполняем финализатор:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public static String resolveExpression(String expression)
 {
   FacesContext fc = FacesContext.getCurrentInstance();
   return (String) fc.getApplication().evaluateExpressionGet(fc, expression,
                                                    String.class);
 }
 
public void theFinalizer()
{
  //Just to have test access to the managed beans
  //and to be sure we work with the same instances
  System.out.println(resolveExpression("#{pageFlowScope.FlowBean.testString}")+
                     " " + resolveExpression("#{pageFlowScope.FlowBean.button}"));
  System.out.println(resolveExpression("#{viewScope.ViewBean.testString}")+
                     " " + resolveExpression("#{viewScope.ViewBean.button}"));
  System.out.println(resolveExpression("#{backingBeanScope.BackBean.testString}")+
                     " " + resolveExpression("#{backingBeanScope.BackBean.button}"));
}
Запустите приложение, нажмите кнопку cb1 и увидите следующее в системном журнале:
Кнопке назначается RichCommandButton [UIXFacesBeanImpl, id = cb3]
Кнопке назначается RichCommandButton [UIXFacesBeanImpl, id = cb2]
Кнопке назначается RichCommandButton [UIXFacesBeanImpl, id = cb1]
Кажется, все в порядке. Поток задач завершен, и в финализаторе мы работаем с правильными экземплярами управляемого компонента. В этом тесте поток задач завершается корректно с использованием действия Возврат.
А теперь давайте оставим наш поток задач — просто отойдите от страницы, на которой установлен поток задач. Финализатор также выполняется, и посмотрите на систему:
Кнопке назначается RichCommandButton [UIXFacesBeanImpl, id = cb3]
Кнопке назначается RichCommandButton [UIXFacesBeanImpl, id = cb2]
Кнопка не назначена
Это означает, что мы работаем с другим экземпляром backingBeanScope.BackBean! В случае изобилующего потока задач контроллер не видит корректный backingBeanScope в финализаторе, он пуст, и контроллер создает новый экземпляр BackBean. В то же время pageFlowScope и viewScope работают отлично. Поэтому будьте осторожны при использовании управляемых bean-компонентов области backingBean в потоках задач, особенно при обращении к ним в финализаторах. Но в любом случае вы можете использовать тот же трюк, описанный в предыдущем посте .