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

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

XSD文件需要存储在本地,即不能通过http调用。我应该在Struts应用程序中的哪个位置存储XSD文件?例如,它应该进入WEB-INF吗?我需要将其放在我的src软件包之一中吗?我已经了解了很多有关本地存储的XSD的信息,但对Struts没什么特别的了解。
存储后,如何从Java代码引用此XSD文件?我不知道如何告诉程序在哪里找到文件。

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

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

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

我在这个论坛上看过类似的问题,但仍然有些失落。我希望社区提供任何帮助。

提前致谢!

参考方案

因此,在进行了许多额外的研究之后,我了解到我对Java反射和Struts上下文缺乏了解受到了阻碍。哎呀,我什至不知道什么是“资源”。

我已经解决了问题,但是在寻求解决方案的过程中,我写了以下教程来帮助我跟踪正在学习的新概念。希望这将帮助遇到此问题并需要更多明确细节的下一个人。请让我知道是否有一些我不清楚的地方,或者是否误解了一个概念,我将予以纠正和澄清。

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

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

this.getClass().getResource("file.txt");

其中file.txt是我们要查找的文件的名称。这将返回URL对象,这是一种特殊的Java对象,它包含文件路径或URL。我们可以调用对象的toString()方法来获取完整的URL。因此,以下代码块

URL thisFile1 = this.getClass().getResource("resource.txt");
System.out.println(thisFile1.toString());

打印以下内容:

file:/C:/path/to/workspace/folder/bin/sandbox/test/pkg1/resource.txt

但是,仅当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");

不太明显的是为什么我们必须先调用getClass()才能调用getResource()。我想知道为什么我们需要完全调用getClass()getResource()成为Object方法会不会更容易?这似乎是一个令人困惑且不必要的步骤。

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

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

      public void method2() {...}

      public void method3() {...}
 }

如果调用myMethod(),则会得到Class的实例,该实例包含有关myClass对象的信息。例如,如果我们再致电

Method[] theseMethods = thisClass.getMethods();

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

那么,如果我们获得外部资源,为什么我们应该关心这一点?因为要获取资源,所以我们首先需要获取有关要获取资源的类的信息。

让我们回到前面的示例:

URL thisFile1 = this.getClass().getResource("resource.txt");

这会在当前目录中搜索文件“ 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());

产生

 file:/C:/path/to/Tomcat/server/folder/.metadata/.me_tcat/webapps/ProjectName/WEB-INF/classes/sandbox/test/pkg1/resource.txt

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

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

file:/C:/path/to/Tomcat/server/folder/.metadata/.me_tcat/webapps/ProjectName/WEB-INF/classes/

因此,要想进一步提高文件层次结构,我们必须使用一个或多个../制定一个复杂的相对路径。那是一团糟,很难遵循。有没有更优雅的方式?

有!

URL thisFile1 = this.getServlet().getServletContext().getResource("/WEB-INF/resources/resource.txt");

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

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

这就是this.getServlet().getServletContext().getResource()this.getClass().getResource()的不同之处。前者从运行Struts的Web应用程序的角度处理本地资源,而后者从要求资源的类的角度处理本地资源。因此,前者将以Web应用程序(在上面的示例文件路径中为ProjectName)作为根目录查找资源,而后者则将包层次结构的顶部作为根目录查找资源。这很有道理。在传统的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));

我将其封装在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

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

jndi:/localhost/ProjectName/WEB-INF/resources/schema.xsd

我了解到,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);

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

在Java Swing中输入JButton的键焦点? - java

如何在Java Swing中使JButton的Enter键成为焦点?我已经这样做了btn_Login.registerKeyboardAction(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("enter key pre…

在Java 8中,为什么ArrayList的默认容量现在为零? - java

我记得,在Java 8之前,ArrayList的默认容量为10。出乎意料的是,对默认(无效)构造函数的评论仍然显示:Constructs an empty list with an initial capacity of ten.来自ArrayList.java:/** * Shared empty array instance used for defau…

在Java 8流中,如何过滤掉不是枚举有效值的字符串? - java

我有一个枚举,我称之为对我的应用程序重要的安全权限的Permission。在数据库中,用户可能具有与我的应用程序无关的其他权限。从数据库中读取用户时,我得到一个List<String>,并且我想建立一个List<Permission>,而忽略了那些不是枚举值的字符串。public enum Permission { ADMIN, US…

JAVA:字节码和二进制有什么区别? - java

java字节代码(已编译的语言,也称为目标代码)与机器代码(当前计算机的本机代码)之间有什么区别?我读过一些书,他们将字节码称为二进制指令,但我不知道为什么。 参考方案 字节码是独立于平台的,在Windows中运行的编译器编译的字节码仍将在linux / unix / mac中运行。机器代码是特定于平台的,如果在Windows x86中编译,则它将仅在Win…

java:继承 - java

有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…