1

我想在一个人打开应用程序时设置一个 applicationScope 变量。在当前会话期间,设置的值不会更改。可以作为 sessionScope 来做,但有同样的问题,你如何在会话打开时设置它。

4

1 回答 1

8

执行此操作的“正确”方法是定义托管 bean。

将 Java 设计元素添加到您的应用程序。因为您的问题并未表明您要解决什么业务需求,所以以下是一个完全通用的示例,希望能说明该模式:

package com.example.bean;

import java.io.Serializable;

public class ExampleBean implements Serializable {
    private static final long serialVersionUID = 1L;
    private Object exampleProperty;

    public ExampleBean() {

    }

    public Object getExampleProperty() {
        if (exampleProperty == null) {
            /*
             * This is where you would auto-initialize the property value
             */
        }
        return exampleProperty;
    }

    public void setExampleProperty(Object exampleProperty) {
        this.exampleProperty = exampleProperty;
    }
}

上面示例中的关键部分是getExampleProperty(). 在您的真实课程中,您可以用任何逻辑替换该注释,以确定属性的当前值是什么,并分配exampleProperty给该逻辑的结果。该代码只会在每个范围实例中运行一次,因为每次访问该属性时,其值将不再为空,因此将简单地返回当前值。

exampleProperty将自动初始化的逻辑放在类构造函数中似乎更直观(public ExampleBean() {...),但不要。遵循这种“延迟加载”模式有三个原因。首先是可以想象,用户可能永远不会触发了解属性值的需求。同样,我不知道您为什么要在访问应用程序时立即存储一个值,因此在您的特定用例中,用户可能总是立即需要该属性值。但是假设目标是存储一些应用程序设置,但用户从未在应用程序中执行任何需要知道该设置的操作……您将检索设置而不会从中获得任何好处。使用上述模式,您不会在用户“请求”之前检索它,但在他们这样做的那一刻,它就在那里,并一直存在到范围到期。

第二个原因是,随着你的类随着时间的推移变得越来越复杂(因为你以这种方式存储越来越多的属性值),如果你将所有用于初始化属性的逻辑都放在构造函数中,那么该方法将变得巨大,它会是很难立即看出哪个代码用于初始化哪个属性。通过将每个属性的初始化逻辑放在其关联的“getter”方法中,您可以隔离每个属性的逻辑;类保持尽可能清晰,便于日常维护。

但也许最重要的原因是,随着您将越来越多的应用程序逻辑移至 bean(我们所有人都应该这样做),您将确定定义viewScope(特定于页面的)bean 的用例。除非您将应用程序设置显式设置为将所有页面存储在内存中,否则viewScope只要应用程序认为它们应该是,bean 就会自动序列化到磁盘(因此在类声明中指示 class implements Serializable。稍后根据需要将它们加载回内存中。这会触发类构造函数,然后将属性值设置回它们之前的值。换句话说,如果你初始化exampleProperty在构造函数中,执行此操作的代码将在页面加载时执行,然后在对页面执行的任何事件期间再次执行......但如果它的值在某个时候发生变化,它将继续在每个事件上重新初始化,仅被它后来变成的任何值覆盖。这几乎完全违背了将值存储在范围内的目的。不管范围如何,让所有 bean 都可序列化也是一个好主意,因为如果 IBM 向 XPage 添加真正的集群支持,那么应用程序和会话 bean 也需要序列化到磁盘,以便将它们复制到其他服务器,所以现在只需让所有 bean 可序列化,当您升级到包含集群支持的 Domino 版本时,您无需担心应用程序突然中断。

总之,只要记住“延迟加载”模式对 bean 来说“更好”。:)

一旦你定义了你的 Java 类,在faces-config.xml. 此文件存储在 中WebContent/WEB-INF,您可以通过 Package Explorer 找到该文件,但在 Domino 9 中,它也作为“应用程序配置”类别下的设计元素出现。

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
  <managed-bean>
    <managed-bean-name>exampleBean</managed-bean-name>
    <managed-bean-class>com.example.bean.ExampleBean</managed-bean-class>
    <managed-bean-scope>application</managed-bean-scope>
  </managed-bean>
  <!--AUTOGEN-START-BUILDER: Automatically generated by IBM Domino Designer. Do not modify.-->
  <!--AUTOGEN-END-BUILDER: End of automatically generated section-->
</faces-config>

我上面包含的示例类名为ExampleBean,我给它起了一个包名com.example.bean,所以这个类的“规范”名称是com.example.bean.ExampleBean,你可以在上面的 XML 中看到。名称是您想从应用程序中的任何位置引用的变量。XML 的作用域部分定义了您希望 bean 驻留在四个作用域中的哪一个。这样做特别方便的是,如果您稍后决定这更适合存储在 中sessionScope,那么您可以返回faces-config并更改applicationsession... 并立即所有引用它的代码现在都将访问sessionScope变量而不是applicationScope变量。您根本不需要更改任何 XPage 代码。

说到这里,既然它被定义为托管 bean,那么您实际上是如何引用它的:

  1. 在 SSJS 中的任何地方,您都可以通过将 bean 名称视为对象来调用 bean 的公共方法:

    return exampleBean.getExampleProperty();
    

    ...或者...

    exampleBean.setExampleProperty(someNewValue);
    
  2. 将 bean 视为数据源。您可以将任何组件属性(即valuetitlerendered等)直接绑定到 bean 的任何属性:

    #{exampleBean.exampleProperty}

    这会自动评估属性是在“读取”上下文还是“写入”上下文中访问的。如果用户当前正在执行的操作导致 Domino “询问”属性值是什么,那么这就是读取。结果,它会自动知道调用“getter”方法(在本例中为getExampleProperty())。但是,如果您将可编辑的组件属性(即value编辑框、单选按钮等的属性)绑定到 bean 属性,并且用户现在正在向该组件发布数据,Domino会自动知道调用“setter” ( setExampleProperty()) 而是将新值传递给它。

因此,如果这是一个应用程序设置,您可以包含一个只有进程所有者才能访问的设置页面,并让他们可以编辑此属性。在 setter 方法中,除了更新内部属性值之外,您还可以将新值写回您从哪里获得它(即配置文档)。现在所有用户都可以访问新值,而无需再次从磁盘检索它,但新值也会被存储,因此如果 bean 超出范围,下次加载 bean 时会自动再次检索更新的值.

相反,如果它是用户设置(即存储在 中sessionScope),那么您可以类似地从应用程序中的任何位置访问该属性,但也可以为他们提供修改设置的方法。与应用程序设置一样,当前值将始终在内存中,但是如果您在修改时将其写回磁盘,则内存中的值将始终是当前值,但是当该范围到期时,可以检索该值(一次)在需要时再次。

所有这些行为在技术上也可以通过 SSJS 实现。但是,正如我在这个答案开头提到的那样,在范围内存储数据的最正确方法是通过托管 bean。

于 2013-10-27T02:31:32.300 回答