我正在尝试使用@ApplicationScoped @ManagedBean调用计划任务,该任务每2秒将一些属性加载到我的JSF应用程序中。由于某种原因,该方法无法正常工作。
请参阅我遵循的步骤。

我要做的第一件事是创建一个每2秒从文件系统加载一次的类:

@ManagedBean
@ApplicationScoped
public class ProppertyReader {


    @PostConstruct
    public void init(){
        SystemReader systemReader = new SystemReader();
        systemReader.schedule();
    }

    private class SystemReader {
        private final  ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        private  Logger LOGGER = Logger.getLogger(ProppertyReader.class.getName());

        public void schedule(){
            scheduler.scheduleAtFixedRate(new Runnable() {
                public void run() {
                    Properties properties = loadProperties();
                    LOGGER.info("Loaded property enabled:" + properties.getProperty("enabled"));
                }
            }, 0L, 2L, TimeUnit.SECONDS);
        }

        private Properties loadProperties() {
            try {
            Properties properties = new Properties();
            properties.load(new FileInputStream("~/Desktop/propertiesRepo/example.properties"));
                return properties;
            } catch (IOException e) {
            e.printStackTrace();
            }
            return null;
        }
    }
}


然后,我转到另一个bean并尝试使用该属性:

@ManagedBean
@SessionScoped
public class SomeBean {
    //...
    private Properties properties = new Properties();
    private boolean enabled = new Boolean(properties.getProperty("enabled"));
    //...
    public boolean isEnabled() {
        return enabled;
    }
}


当我尝试在JSF if语句中使用#{someBean.enabled}使用某些bean来显示或隐藏依赖于该值的组件时,似乎不起作用:

<c:if test="#{someBean.enabled}">
          <h1>Works!</h1>
</c:if>


我没有错,有什么想法吗?

更新:
我在Properties类中看到了我的错误。我现在正在尝试创建未处理的属性,因此我清除了一些代码,但在应用启动时得到了NullPointer。

我将属性阅读器分为两个类:

@ManagedBean
@ApplicationScoped
public class ProppertyReader {

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private SystemReader systemReader = new SystemReader();
    public static Properties appProperties;

    @PostConstruct
    public void init(){
        schedule();
    }

    private void schedule(){
        scheduler.scheduleAtFixedRate(new Runnable() {
            public void run() {
                appProperties = systemReader.loadProperties();
            }
        }, 0L, 2L, TimeUnit.SECONDS);
    }
}


这是我从系统中读取数据的位置:

public class SystemReader {

        public Properties loadProperties() {
            try {
                Properties properties = new Properties();
                properties.load(new FileInputStream("~/Desktop/propertiesRepo/example.properties"));
                return properties;
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
}


我现在所说的是:

@ManagedBean
@SessionScoped
public class SomeBean {

    private boolean enabled = new Boolean(ProppertyReader.appProperties.getProperty("enabled"));
//...


目前,我正在获取NullPointer异常,我想我正在接近。

最佳答案

这不是Java的工作方式。

为了实现您的目标,您应该将属性文件作为应用程序作用域Bean的实例变量保存,并每次重新加载其内容,而不是每次都重新创建并对其进行垃圾回收(!!)。您应该在会话作用域的bean中也不要创建属性类的完全独立的实例,而是实际使用应用程序作用域的bean中保存的那个实例。

这是一个重写:

@ManagedBean
@ApplicationScoped
public class PropertiesHolder {

    private static final String PATH = "~/Desktop/propertiesRepo/example.properties";
    private Properties properties;
    private ScheduledExecutorService scheduler;

    @PostConstruct
    public void init() {
        properties = new Properties();
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    properties.load(new FileInputStream(PATH));
                } catch (IOException e) {
                    throw new RuntimeException("Failed to load properties", e);
                }
            }
        }, 0L, 2L, TimeUnit.SECONDS);
    }

    @PreDestroy
    public void destroy() {
        scheduler.shutdownNow();
    }

    public String getProperty(String key) {
        return properties.getProperty(key);
    }

}


请注意,我还添加了@PreDestroy来关闭调度程序,否则,每次Java重新启动时,您可能会泄漏线程,直到Java运行时环境用完线程为止。

如果要获取最新值,请在会话范围的bean中使用它:

@ManagedBean
@SessionScoped
public class SomeBean {

    @ManagedProperty("#{propertiesHolder}")
    private PropertiesHolder propertiesHolder;

    public void setPropertiesHolder(PropertiesHolder propertiesHolder) {
        this.propertiesHolder = propertiesHolder;
    }

    public boolean isEnabled() {
        return new Boolean(propertiesHolder.getProperty("enabled"));
    }

}


请注意,该属性不是在bean的实例化时获得的,否则,您将在每次getter调用时仅获得与bean实例化时相同的值,并且永远不会在同一会话的后续请求中获得更新的值。

更重要的是,该值本质上是请求范围的,因此会话范围就是保存该值的错误范围。使其成为请求范围的Bean:

@ManagedBean
@RequestScoped
public class SomeBean {

    @ManagedProperty("#{propertiesHolder.getProperty('enabled')}")
    private boolean enabled;

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

}

07-24 09:37
查看更多