【问题标题】:Where to store properties from properties file in java webapp在 java webapp 中的属性文件中存储属性的位置
【发布时间】:2014-06-22 09:03:37
【问题描述】:

不确定以前是否有人问过这个问题,但我似乎找不到答案。

在 web 应用程序中加载时将属性存储在哪里。 我有一个 Web 应用程序,它的设置允许系统管理员通过用户界面更改应用程序中的设置。

例如,该应用只允许选定的用户组能够进入某个页面。 我想让系统管理员进去设置可以进入上述页面的用户组,然后在以后更改它。

我面临的问题是,一旦加载了属性文件,我将数据存储在哪里,而不是每次用户进入页面时不断加载属性文件。

我可能不了解如何使用属性的完整概念,因此非常感谢任何指导。

只要确保我可以读取用户组,可以在不重新加载类/应用程序的情况下更改用户组,并允许它在没有两个不同线程具有两个不同属性的情况下线程安全和快速,因为我们使用的是负载平衡环境.使用内容共享,这是存储和访问属性文件的位置(对此没有任何问题,因此无需寻求有关在何处存储属性文件的帮助)。

非常感谢任何帮助。

编辑 1

应用程序在集群环境中运行,这意味着其他应用程序服务器可能由于多个 ServletContexts 而具有不同的值。

【问题讨论】:

    标签: java tomcat servlets properties


    【解决方案1】:

    注册ServletContextListener以在服务器启动时加载初始化参数和属性

    一次性加载属性并使其对其他类静态可见,或者您也可以将其存储在应用程序上下文中,以便从 JSP 和 Servlet 等任何地方访问它。

    注意: 使属性文件位置在web.xml 中可配置,而不是在Java 类中硬编码。您也可以将属性文件位置检索为系统环境变量

    示例代码:

    public class AppServletContextListener implements ServletContextListener {
        private static Properties properties;
        @Override
        public void contextInitialized(ServletContextEvent servletContextEvent) {
            String cfgfile = servletContextEvent.getServletContext().getInitParameter("config_file");
            properties.load(new FileInputStream(cfgfile));
    
            // store it in application scope as well
            servletContextEvent.getServletContext().setAttribute("prop",properties);
        }
        
        public static Properties getProperties(){
            return properties;
        }
    }
    

    web.xml:

    <listener>
        <listener-class>com.x.y.z.AppServletContextListener</listener-class>
    </listener>
    
    <context-param>
          <param-name>config_file</param-name>
          <param-value>config_file_location</param-value>
    </context-param>
    

    请查看我在相同上下文中提出的另一篇帖子:


    编辑

    如果您在运行时更改属性,则不要根据ServletContext javadoc 使用 Servlet 上下文:

    对于在其部署描述符中标记为“分布式”的 Web 应用程序,每个虚拟机将有一个上下文实例。在这种情况下,上下文不能用作共享全局信息的位置(因为信息不会是真正的全局信息)。请改用数据库等外部资源。

    Servlet specification 也在“SRV.4.4.1 分布式容器中的上下文属性”中声明:

    上下文属性对于创建它们的 JVM 来说是本地的。这可以防止 ServletContext 属性成为分布式容器中的共享内存存储。当需要在分布式环境中运行的 servlet 之间共享信息时,应将信息放入会话中(参见第 SRV.7 章“会话”)、存储在数据库中或设置在 Enterprise JavaBeansTM 组件中。

    在这种情况下,您可以尝试使用一些在分布式环境中工作的第三方缓存,如下所述:

    或将所有属性存储在数据库中。

    【讨论】:

    • 对不起,我正在修改另一个 servlet 中的更改属性。因此,我将如何使用这种方式,因为一个 servlet 负责处理用户对应用程序的访问,而另一个只用于配置页面。
    • 好的,在阅读了 ServletContext 之后,这在集群环境中是不可用的。所以我认为这行不通。
    • 你在哪里读到这在集群环境中不起作用?它始终有效,因为在服务器启动时为所有节点初始化了应用程序上下文,您认为是否可以在不初始化应用程序上下文的情况下访问应用程序?
    • 您是否在运行时更改属性?
    【解决方案2】:

    Servlet 容器提供了上下文的概念。我发现将上下文视为一个有用的盒子来存储东西是很有帮助的,并且像地图一样运作。

    Java Webapp 有许多不同的上下文可用,它们的范围不同(即上下文中保存的数据持续多长时间,以及可以从哪里访问它)。有 Page ContextSession ContextServlet Context

    页面上下文的范围最窄,只持续一个页面需要处理的时间。

    会话上下文的范围更大,持续时间与单个用户会话一样长,即来自浏览器的多个请求。如果您的 webapp 需要身份验证,这很有用 - 有关已验证用户的信息将存储在 Session Context 中。

    Servlet 上下文实际上是全局的,并且始终可供整个应用程序使用。这是我建议存储影响应用程序功能的配置属性的地方。

    在 Servlet 中,您可以像这样访问 Servlet 上下文:

    ServletContext context = request.getSession().getServletContext();
    

    你可以像这样在上下文中存储一些东西:

    context.setAttribute("key", object);
    

    其中key 是一个字符串 - 属性的名称。

    您可以像这样再次检索它:

    object = context.getAttribute("key");
    

    返回一个对象。您可以将它转换为它真正的任何类型。如果您愿意,可以在其中存储一个 Properties 对象:

    Properties props = //... get the properties from file
    context.setAttribute("props", props);
    

    然后检索它们:

    Properties props = (Properties) context.getAttribute("props");
    

    或者您可以将各个属性作为单独的属性存储在上下文中。

    所有上下文都以相同的方式访问。

    【讨论】:

    • 对不起,我正在修改另一个 servlet 中的更改属性。因此,我将如何使用这种方式,因为一个 servlet 负责处理用户对应用程序的访问,而另一个只用于配置页面。
    • 好的,在阅读了 ServletContext 之后,这在集群环境中是不可用的。所以我认为这行不通。
    【解决方案3】:

    您可以使用经典的单例模式,其中您有一个 ApplicationProperties 类,它为您的应用程序保存全局有效值,该值由属性文件支持,因此您的应用程序的任何部分都不必关心如何存储属性。伪代码:

    public class ApplicationProperties {
    
        private static final String PATH = "app.properties";
    
        private static final ApplicationProperties INSTANCE = new ApplicationProperties();
    
        private String userGroup;
    
        private ApplicationProperties() {
            // Load properties from PATH and populate fields.
            this.userGroup = ...
        }
    
        public static ApplicationProperties getInstance() {
            return INSTANCE;
        }
    
        public String getUserGroup() {
            return this.userGroup;
        }
    
        public String setUserGroup(String userGroup) {
            // Save to property file to persist.
            this.userGroup = userGroup;
        }
    }
    

    您只需要同步对没有两个线程覆盖属性并创建竞争条件的字段的访问。

    【讨论】:

    • 当你提到同步时,我准备投反对票,直到我走到最后......很好保存。
    • @NickJ 抱歉,我不想在这里用这个可怜的“编辑器”来编写整个班级的代码 :-)
    • 不用担心。使用这种方法,您需要将 synchronized 关键字添加到所有公共方法中。
    猜你喜欢
    • 1970-01-01
    • 2021-11-11
    • 1970-01-01
    • 1970-01-01
    • 2015-07-13
    • 2017-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多