Java - 如何在没有servlet上下文的情况下访问WEB-INF文件夹中的文件

6

我有一个中央类 (RuntimeConfiguration),它保存了应用程序约 100 个参数。 这些参数取自存储在WEB-INF文件夹中的一个XML文件。 在我的开发环境中,我硬编码了路径,但出于非常明显的原因,这不是一种生产性的解决方案。

出于可移植性的原因(我的应用程序可能在Windows或Unix下执行Tomcat或Jetty),我希望从我的RuntimeConfiguration类内部以通用方式访问此文件。

我知道,如果我有一个servlet,我可以使用getRealPath,但是RuntimeConfiguration并未在servlet上下文中启动。它是通过applicationContext.xml从quartz工作控制启动的作业(爬虫)调用的。现在这似乎是我的核心问题。

有人能告诉我如何在这种环境下获取我的XML的绝对路径吗?

我尝试过这个ClassLoader方法,但它给了我一个NullPointerException

ClassLoader loader = this.getClass().getClassLoader();
loader.getResource("WEB-INF");
result = loader.getSystemResource(RTCF).toString(); 

我也尝试过

URL url = this.getClass().getClassLoader().getResource("WEB-INF");

这也会抛出空指针异常

有没有人能给我一个便携的解决方案,可以在我的开发机器上运行,并且可以在部署了WAR文件的生产系统上工作?

顺便说一下,这个方法最好能够处理基于Windows、Mac OS X和Unix的系统。


你调试了你在问题中提到的解决方案吗?你得到了 ClassLoader 对象吗?你在哪个点上得到了 NPE?你可以添加堆栈跟踪以获得更好的理解。 - OO7
你的Web应用程序上下文中是否将RuntimeConfiguration声明为bean? - OO7
RuntimeConfiguration在我的Web应用程序上下文中未注册为bean。 - siliconchris
3个回答

1
您可以始终使用“holder”模式:创建一个类,其中包含一个静态字段(及其静态getter),该字段将包含“WEB-INF”目录的实际路径。此类应实现“ApplicationContextAware”。
在根spring应用程序上下文中创建此holder类的实例,Spring会将其指针指向ApplicationContext,该ApplicationContext将是WebApplicationContext,因为您正在使用Web应用程序。
在init方法中,您使用WebApplicationContext获取路径并将其存储在静态字段中。由于Spring,您可以确保在应用程序真正启动之前仅调用一次该方法。
现在,在应用程序中任何需要获取路径的地方,都可以通过调用静态getter来使用它。
public class ResourcePathHolder implements ApplicationContextAware, InitializingBean{
    private WebApplicationContext wac;
    private static String resourcePath;
    private static ServletContext sc;
    private String path = "WEB-INF";

    public static String getResourcePath() {
        return resourcePath;
    }

    public static ServletContext getServletContext() {
        return sc;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        wac = (WebApplicationContext) applicationContext;
    }

    public void setPath(String path) {
        this.path = path;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ResourcePathHolder.sc = wac.getServletContext();
        ResourcePathHolder.resourcePath = sc.getRealPath(path);
    }
}

这个实现还可以让你访问ServletContext,以便您使用ServletContext.getResource()
编辑:
使用完整路径可能在不展开war的生产服务器上无法正常工作。从ServletContext.getRealPath()的javadoc中可以看到:如果servlet容器无法将给定的虚拟路径转换为实际路径,则此方法返回null。但在这种情况下,您始终可以使用ServletContext.getResource()ServletContext.getResourceAsStream()来访问文件资源。

该死!我太快了,把这个作为工作答案了。它在我的开发机器上运行良好,但在服务器上有一部分路径被省略了 - 请看这里的输出:/opt/apache-tomcat-8.0.12/null/SNC_Runtime_Configuration.xml。你知道为什么会发生这种情况吗?这是我从applicationContext.xml中摘取的代码片段(也许我在那里漏掉了什么):<bean id="contextWebApplicationContextProvider" class="de.comlineag.snc.appstate.ResourcePathHolder"></bean> - siliconchris
@siliconchris: 这就是为什么我加入了对ServletcContext的直接访问的原因:请看我的编辑。 - Serge Ballesta
哎呀 - 我一定是有点被诅咒了,完全没有适应能力,我真的不明白这个。如果我使用getResource,我会得到这样一个路径:jndi:/localhost/SocialNetworkConnector/WEB-INF。但是我该如何访问其中的任何文件呢?我只想要一个简单的完整路径来访问配置文件。难道没有一种简单的方法吗?我的意思是,服务器知道它运行的位置,所以它应该提供一些路径或一些全局变量之类的东西,以便在已知目录下访问文件路径。我是不是想错了? - siliconchris
@siliconchris:假设你的配置文件是/WEB-INF/conf.xml,你能否使用servletContext.getResourceAsStream("/WEB-INF/conf.xml")获取InputStream confStream,并将其输入到XML解析器中? - Serge Ballesta
嗯,我需要重写我的RuntimeConfiguration类。目前它有这样的方法:`private void setRuntimeConfiguration(String configFile){ logger.debug(">>> setting runtime configuration"); String debugMsg = ""; try { File file = new File(configFile); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(file); XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath();` - siliconchris
@siliconchris 我从未这样做过,目前也无法访问计算机,但我刚想到另一种方法 - 如果它有效的话... ServletContext.getResource() 返回一个URL,URL可以转换为URI(URL.toURI()),而File构造函数接受一个URI。我相信它会在你的开发系统上运行良好,但在生产环境中可能会有所不同... - Serge Ballesta

0
在Spring Web应用程序中,除非您在Web应用程序上下文中声明某些类作为bean,否则无法获取上下文路径。请在您的RuntimeConfiguration类中添加以下代码。
@Autowired
private ServletContext servletContext;

public {desired returnType} getDirectoryPath(){
    File webInfPath = new File(this.getClass().getClassLoader().getResource("WEB-INF"));
    // OR use getResourceAsStream() method
    InputStream is = RuntimeConfiguration.class.getClassLoader().getResourceAsStream("WEB-INF");
}

或者

如果您不想使用自动装配,那么您可以通过实现ServletContextAware接口来实现RuntimeConfiguration。 就像下面这样,在此类中获取servlet上下文对象:

public class RuntimeConfiguration implements ServletContextAware {

    private ServletContext context;
    private ServletConfig config;

    @Override
    public void setServletContext(final ServletContext servletContext) {
        this.context = servletContext;
    }

    public ServletContext getServletContext()[
        return context;
    }
    //do other tasks
}

OR

请查看ServletContextResourceServletContextResource - Spring Docs


0

只需将其放入类路径中,并使用“getResourcesAsStream()”加载它,或者更好地将其作为“.properties”文件并按照通常的方式加载:

ResourceBundle config = ResourceBundle.getBundle("filename");

抱歉,但配置文件有超过200个选项,其中一些需要在不同的部分中使用相同的名称。这将为我带来很多新问题。 - siliconchris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接