在 Java Struts 应用程序中放置以及如何调用本地 XSD 文件?

123*_*erd 2 java xml xsd struts

我正在编写一个 Java Struts 应用程序,它根据本地存储的 XSD 文件验证传入的 XML 文件。我遇到了两个问题:

  1. XSD 文件需要存储在本地,即不能通过http 调用。我应该在 Struts 应用程序中的哪个位置存储 XSD 文件?例如,它应该进入 WEB-INF 吗?我需要把它放在我的 src 包之一中吗?我已经看到了很多关于本地存储的 XSD,但没有任何特定于 Struts 的内容。

  2. 存储后,如何从 Java 代码引用此 XSD 文件?我不知道如何告诉我的程序在哪里可以找到文件。

换句话说,这就是我所拥有的:

SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 
Schema schema = schemaFactory.newSchema(new File("/path/to/schema.xsd"));
Run Code Online (Sandbox Code Playgroud)

我不知道/path/to/schema.xsd应该是什么样子。

我在这个论坛上看过类似的问题,但还是有点迷茫。我将不胜感激社区可以提供的任何帮助。

提前致谢!

123*_*erd 6

因此,经过大量额外研究后,我了解到我因缺乏有关 Java 反射和 Struts 上下文的知识而受到阻碍。哎呀,我什至不知道什么是“资源”。

我已经解决了这个问题,但是在我朝着解决方案前进的过程中,我编写了以下教程来帮助我跟踪我正在学习的新概念。希望这将有助于下一个遇到此问题并需要更明确细节的人。如果有什么我没有说清楚或者我误解了一个概念,请告诉我,我会更正和澄清。

首先,让我们从一个资源开始。我了解到,在 Java 上下文中,“资源”是与应用程序相关的任何文件或数据,而不是 Java 代码。例如,这可以是文本或图像文件,或者,就我而言,是 XSD 文件。

现在,要获得该资源,我们必须告诉 Java 程序在哪里可以找到它。这意味着我们需要资源的完整(绝对)文件路径。对于普通的 Java 项目,获取此文件路径的一种方法是使用

this.getClass().getResource("file.txt");
Run Code Online (Sandbox Code Playgroud)

file.txt我们要查找的文件名在哪里。这将返回一个 URL 对象,这是一种特殊类型的 Java 对象,用于保存文件路径或 URL。我们可以调用对象的toString()方法来获取完整的 URL。因此,下面的代码块

URL thisFile1 = this.getClass().getResource("resource.txt");
System.out.println(thisFile1.toString());
Run Code Online (Sandbox Code Playgroud)

打印以下内容:

file:/C:/path/to/workspace/folder/bin/sandbox/test/pkg1/resource.txt
Run Code Online (Sandbox Code Playgroud)

但是,只有当 resource.txt 与sandbox.test.pkg1进行上述调用的源代码(在本例中为)位于同一包中时,这才是正确的。

这是因为getResource()假定其输入参数是相对文件路径,除非文件路径以“/”开头。如果是,getResource()则将输入参数视为绝对文件路径,其中根目录是编译后的代码目录,即包层次结构的顶部(“bin”文件夹)。换句话说,这两行代码将产生相同的结果:

URL thisFile1 = this.getClass().getResource("resource.txt");
URL thisFile1 = this.getClass().getResource("/sandbox/test/pkg1/resource.txt");
Run Code Online (Sandbox Code Playgroud)

不太明显的是为什么我们必须先调用getClass()才能调用getResource(). 我想知道为什么我们需要打电话getClass()getResource()成为一个对象方法不是更容易吗?这似乎是一个令人困惑和不必要的步骤。

好吧,getClass()返回一个 Java Class 对象的实例,一个特殊的 Java 对象,它返回有关 Java 类的信息。换句话说,使用以下代码块:

public class myClass 
{
      public void myMethod()
      {
           Class thisClass = this.getClass();
      }

      public void method2() {...}

      public void method3() {...}
 }
Run Code Online (Sandbox Code Playgroud)

如果我们调用myMethod(),我们会得到一个 Class 实例,其中包含有关 myClass 对象的信息。例如,如果我们然后调用

Method[] theseMethods = thisClass.getMethods();
Run Code Online (Sandbox Code Playgroud)

我们得到的MyClass中(所有方法的数组myMethod()method2()method3(),等)。这就是 Java 反射,它具有广泛的用途,因为我们可以在程序运行时收集和检查 Java 类的属性。

那么,如果我们获得外部资源,我们为什么要关心这个呢?因为要获取资源,我们首先需要获取想要获取资源的类的信息。

让我们回到我们之前的例子:

URL thisFile1 = this.getClass().getResource("resource.txt");
Run Code Online (Sandbox Code Playgroud)

这会在当前目录中搜索文件“resource.txt”,但getResource()不会自动知道当前目录是什么。它需要获取有关调用它的类的信息以了解要搜索的目录。Class 对象只包含这种信息,这就是为什么getResource()是 Class 对象的方法而不是 Object 对象的原因。

到现在为止还挺好。但是,当我们尝试从运行在本地 Tomcat 服务器上的 Java Struts 应用程序中获取资源时,而不是尝试从传统 Java 项目中获取资源时,事情就变得复杂了。使用 Struts 和 Tomcat,文件最终位于非常不同的位置:

URL thisFile1 = this.getClass().getResource("resource.txt");
System.out.println(thisFile1.toString());
Run Code Online (Sandbox Code Playgroud)

产生

 file:/C:/path/to/Tomcat/server/folder/.metadata/.me_tcat/webapps/ProjectName/WEB-INF/classes/sandbox/test/pkg1/resource.txt
Run Code Online (Sandbox Code Playgroud)

这要复杂得多,也不直观。另外,如果我们想要的文件与调用它的源代码不在同一目录中怎么办?对于 Struts 应用程序,习惯上将资源文件放在其他地方。这是因为 Struts 建立在分离程序的不同部分以更好地管理程序流(MVC 范式)的原则之上,因此将资源文件与源代码捆绑在一起似乎违反了该原则(无论如何,在我看来)。

我想将我的资源文件放入 WEB-INF 但在“classes”文件夹之外——比如在“resources”文件夹中——但这会变得getResource()更加复杂。从 的角度来看getResource(),根目录是

file:/C:/path/to/Tomcat/server/folder/.metadata/.me_tcat/webapps/ProjectName/WEB-INF/classes/
Run Code Online (Sandbox Code Playgroud)

因此,为了在文件层次结构中更上一层楼,我们必须制定一个复杂的相对路径,其中包含一个或多个../. 这很混乱,很难遵循。有没有更优雅的方式?

有!

URL thisFile1 = this.getServlet().getServletContext().getResource("/WEB-INF/resources/resource.txt");
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?好吧,请记住,Struts 是一个使用 Java servlet 的框架。这意味着我们的源代码在 servlet 中。反过来,该 servlet 是 Web 应用程序的一部分(我了解到,该应用程序可以运行多个 servlet)。该应用程序是运行 servlet 的上下文。应用程序有自己的getResource()方法,该方法用于为作为应用程序一部分运行的所有 servlet 提供资源。

换句话说,我们获取我们的 servlet,然后获取我们的 servlet 的上下文(Web 应用程序),然后从上下文中获取资源。

这就是这this.getServlet().getServletContext().getResource()this.getClass().getResource(). 前者从运行 Struts 的 Web 应用程序的角度处理本地资源,而后者从请求资源的类的角度处理本地资源。因此,前者查找以 web 应用程序(项目名称,在上面的示例文件路径中)为根目录的资源,而后者查找以包层次结构顶部为根目录的资源。这是有道理的。在传统的 Java 项目中,我们从来没有比包层次结构的顶部更高的任何东西,即项目运行时没有“更大的上下文”。但是,对于 Struts,Java 项目是运行 Struts 的更大 Web 应用程序(“更大的上下文”)的一部分。

所以现在我修改后的代码如下所示:

String xsdFile = "";
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 
try
{
    xsdFile = this.getServlet().getServletContext().getResource("/WEB-INF/resources/schema.xsd").toString();
}
catch (Exception e)
{
    return e.getMessage();
}
Schema schema = schemaFactory.newSchema(new File(xsdFile));
Run Code Online (Sandbox Code Playgroud)

我将它封装在一个 try-catch 块中,因为如果我们找不到文件,我们会得到一个 NullPointerException。

但这仍然不太对。我在上述代码块的最后一行出现错误。代码找不到文件 schema.xsd。根据控制台窗口,它正在尝试引用

file:/C:/Users/user/AppData/Local/MyEclipse%20Professional%202014/plugins/com.genuitec.eclipse.easie.tomcat.myeclipse_11.0.0.me201211151802/tomcat/bin/jndi:/localhost/ProjectName/WEB-INF/resources/schema.xsd
Run Code Online (Sandbox Code Playgroud)

看起来不是返回文件的完整路径,this.getServlet().getServletContext().getResource()而是返回本地 URL:

jndi:/localhost/ProjectName/WEB-INF/resources/schema.xsd
Run Code Online (Sandbox Code Playgroud)

我了解到,JNDI 是一种 Java 服务,它允许远程组件通过一个集中位置相互通信。该集中位置是运行 JNDI 的位置,它记录可以找到每个组件的位置,以便其他每个组件都可以找到它。换句话说,要找到组件 B,组件 A 只需 ping JNDI 并询问“B 在哪里?”。JNDI 返回正确的位置。如果两个组件与 JNDI 位于同一服务器上,则 JNDI 返回“localhost”作为 IP,后跟组件相对于 Web 应用程序根目录的路径。这只是说“A 和 B 都在这台机器上”并且工作方式类似于我们习惯的 HTTP URL。

所以一切都默认通过 JNDI 运行。这将保留与 Web 应用程序相关的所有文件引用,这使得将应用程序从一台机器移动到另一台机器、重新定位组件以及如上所述跨机器通信变得更容易。这对于应用程序管理来说很好,但是如果我需要相对于我的机器而不是应用程序的绝对路径,那就不好了。

好吧,我需要一个到我的机器的绝对路径,因为我在 Schema 构造函数中实例化 File 类,而 File 类在使用 String 实例化时,假定其输入参数是本地文件路径。幸运的是,Schema 构造函数还将获取模式文件的 URL(实际上,是 URI),并且 URL 正是上述调用getResource()在我们将其转换为 String 之前返回的内容:

那么,我的最终解决方案如下:

URL xsdFile = "";
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 
try
{
    xsdFile = this.getServlet().getServletContext().getResource("/WEB-INF/resources/schema.xsd");
}
catch (Exception e)
{
    return e.getMessage();
}
Schema schema = schemaFactory.newSchema(xsdFile);
Run Code Online (Sandbox Code Playgroud)

这个新的解决方案非常有效!


Adr*_*dez 6

您可以在任何地方使用的更简单方法是:

SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = schemaFactory.newSchema(loadXSD());
Run Code Online (Sandbox Code Playgroud)

方法loadXSD()

private static Source loadXSD(){
    Source xsd = new StreamSource(Thread.currentThread().getContextClassLoader()
            .getResourceAsStream("File stored in src/"));
    return xsd;
} 
Run Code Online (Sandbox Code Playgroud)