大量的组件共同的组成了一个Java EE Web应用程序。首先,需要自己的代码和它依赖的第三方库。然后需要部署描述符,其中包含了部署和启动应用程序的指令。还可以添加ClassLoader用于将自己应用程序与同一台服务器上的其他Web应用程序隔开。最后,通过某种方式将应用程序打包,生成WAR和EAR文件。
Servlet是任何Java EEWeb应用程序的一个关键组件。Servlet是用于接收和响应HTTP请求的Java类。几乎发送到应用程序中的所有请求都将经过某种类型的Servlet的处理,除了错误的或被其他组件拦截的请求。过滤器就是这样一种组件,可以拦截发送给Servlet的请求。通过使用过滤器可以满足各种需求,包括数据格式化、对返回的数据进行压缩、认证和授权(角色权限管理)。
Java EEWeb应用程序支持各种不同类型的监听器。这些监听器可以通知代码多种事件,例如应用程序启动、应用程序关闭、HTTP会话创建和会话销毁。
Java EE工具中最强大的一个就是JSP(JavaServer Pages)技术。通过使用JSP可以为web应用程序创建动态的、基于HTML的图形化用户界面。JSP技术包括了许多不同的内容,包括JavaServer Pages Standard Tag Library、Java UnifiedExpression Language、自定义标签、国际化和本地化。
除了Servlet、过滤器、监听器和JSP,JavaEE还有很多特性,但是我们用到的并不会很多。
标准Java EE Web应用程序将作为WAR文件或未归档的Web应用程序目录进行部署。JAR文件就是一个简单的ZIP格式的归档文件,其中包含了可被JVM识别的标准目录结构。没有专门的JAR文件格式,任何ZIP归档应用程序都可以创建和读取JAR文件。Web应用程序归档或WAR是Java EE Web应用程序的归档文件。
所有的Java EE Web应用程序服务器都支持WAR文件应用程序归档。大多数服务器还支持未归档的应用程序目录。无论是归档的还是未归档的文件,它们的目录格式约定都是相同的。如同JAR文件一样,该结构包含了类和其他应用程序资源,但是这些类并未像JAR文件一样存储在应用程序根目录对应的相对路径上。相反,类文件都存储在/WEB-INF/classes中。WEB-INF目录存储了一些包含了信息和指令的文件,Java EE Web应用程序服务器使用它们决定如何部署和运行应用程序。它的classes目录被用作根目录。所有编译后的应用程序类文件和其他资源都被存储在该目录中。
不同于标准的JAR文件,WAR文件可以包含应用程序所以依赖的JAR文件,它们被存储在/WEB-INF/lib中。JAR文件中所有在该目录中的类对于在应用程序路径上的应用程序都是可用的。目录/WEB-INF/tags和/WEB-INF/tld分别用于存储JSP标签文件和标签库描述符。目录il8n实际上并不是Java EE规范的一部分,但大多数应用程序开发者都会遵守这个约定,将国际化(il8n)和本地化(L10n)文件存储在该目录中。
这里有两个不同的META-INF目录存在,但是你记住了最简单的类路径规则就可以区分他们。如同JAR文件的META-INF目录一样,跟级别的/META-INF目录包含了应用程序的清单文件。它也可以存储特定Web容器或应用程序服务器需要的资源。例如,Apache Tomcat在该目录中寻找context.xml文件,并使用该文件自定义在Tomcat中部署的应用程序。这些文件都不是Java EE规范的一部分,并且支持的文件可能随着应用程序服务器或者Web容器的不同而变化。
不同于JAR文件的是,跟级别的/META-INF目录并不在应用程序类路径上。不能使用ClassLoader获得该目录中的资源。不过/WEB-INF/classes/META-INF在类路径上。可以将任何希望使用的资源文件存储在该目录中,这样就可以通过ClassLoader访问这些资源。一些Java EE组件指定了某些文件必须存储在该目录中。例如,Java Persistence API指定两个文件——persistence.xml和orm.xml——必须存储在/WEB-INF/classes/META-INF目录中。
包含在WAR文件或未归档Web应用程序目录中的大多数文件都是可以通过URL访问的资源。例如,存储在应用程序根目录的文件/bar.xml将被部署在http://example.org/foo,可通过http://example.org/foo/bar.html访问。在不使用过滤器或安全规则的情况下,应用程序中的所有符合条件的资源都可以通过这种方式访问,除了在/WEB-INF和/META-INF目录下的文件。因为这些目录中的文件是受到保护的,因此不能通过URL访问。
部署描述符是用于描述Web应用程序的元数据,并为Java EE Web应用程序服务器部署和运行Web应用程序提供指令。从传统上来说,所有元数据都来自于部署描述符文件/WEB-INF/web.xml。该文件通常包含Servlet、监听器和过滤器的定义,以及HTTP会话、JSP和应用程序的配置选项。Java EE 6中的Servlet 3.0添加了使用注解和Java Configuration API配置Web应用程序的能力。它还增加了Web片段的概念-- 应用程序中的JAR文件可以包含Servlet、过滤器和监听器的配置,这些配置将被添加到必要JAR文件的部署描述符文件/META-INF/web-fragment.xml中。Web片段也可以使用注解和Java Configuration API。
JavaEE 6对Web应用程序部署的这个改变为文件部署增加了巨大的复杂性。为了简化操作,可以配置Web片段的顺序,从而按照特定的顺序扫描和激活它们。可以通过下面两种方式实现:
l 每个Web片段的web-fragment.xml文件中可以包含一个
l 如果未创建特定的Web片段,并且不能控制它的内容,那么仍然可以在应用程序的部署描述符中控制Web片段的顺序。通过使用/WEB-INF/web.xml中的
默认情况下,Servlet 3.0及更高版本的环境将扫描Web应用程序和Web片段中的Java EE Web应用程序注解,用于配置Servlet、监听器、过滤器等。如果需要,可以在根
在使用Java EE Web应用程序时,有必要理解类加载器(ClassLoader)架构,因为它不同于你所熟悉的标准Java SE应用程序。在典型的应用程序中,Java SE平台中的java.*类将被加载到特定的根类加载器中,并且不能被覆盖。这是一种安全的方式,它阻止了恶意代码的执行,例如恶意代码可能会替换String类,或者重定义Boolean.TRUE和Boolean.FALSE。
在根类加载器之后是扩展类加载器,它将加载JRE安装目录中的扩展JAR。最后,应用程序Class Loader将加载应用程序中的所有其他类。这组成了类加载器的层次,根类加载器是所有类加载器最早的祖先。当低级别类加载器申请加载一个类时,它总是首先将该任务委托给它的父类加载器。继续向上委托直至根类加载器确认成功。如果它的父类加载器未能找到该类,那么当前的类加载器将尝试从自己的JAR文件和目录中加载该类。
这种类加载的方法被称为双亲优先类加载委托模式,尽管这种方法适用于许多类型的应用程序,但它并不完全适用于Java EE Web应用程序。运行Java EE Web应用程序的服务器通常相当复杂,许多供应商都可以提供其实现。服务器可能使用了与个人应用程序使用的相同的第三方库,但它们的版本可能相互冲突。另外,不同的Web应用程序也可能使用了同一第三方库的冲突版本,导致更多的问题。为了解决这些问题,就需要使用子女优先类加载委托模式。
在Java EE Web应用程序服务器中,每个Web应用程序都被分配了一个自由的相互隔离的类加载器,它们都继承自公共的服务器类加载器。通过隔离不同的应用程序,它们不能访问相互的类。这不仅消除了类冲突的风险,还是一种阻止Web应用程序被其他Web应用程序干扰或伤害的安全方式。另外,Web应用程序类加载器通常会在自己无法加载某个类的时候,请求它的父类加载器帮助加载。通过这种方式,类加载的任务会在最后而不是首先委托给它的父类,Web应用程序中的类和库会被优先使用,而不是服务器提供的版本优先使用。为了维持绑定的JavaSE类的安全状态,Web应用程序类加载器仍然会在尝试加载任何类之前与根类加载器确认。尽管几乎在所有的情况下,这种委托模式都更适用于Web应用程序,但仍然有它不适用的情况。出于这个原因,兼容Java EE的服务器通常会提供修改委托模式的方法,从父类最后改为父类首先。
尽管之前已经学习了WAR文件,但还有另一种Java EE归档文件需要了解:EAR文件。企业级应用程序归档将许多JAR文件、WAR文件和配置文件压缩到单个可部署的归档文件中(与JAR和WAR使用相同的ZIP格式)。
如同WAR文件一样,根部的/META-INF目录包含了归档清单文件,并且该目录中的所有文件都不在应用程序的类路径上。文件/META-INF/application.xml是特有的部署描述符,用于描述如何部署EAR文件中包含的各种不同组件。在EAR文件的根目录中是它所包含的所有Web应用程序模块-- 一个模块对应一个WAR文件。这些WAR文件没有什么特殊之处;它们与独立的WAR文件包含着相同的内容和功能。EAR文件还可以包含JAR库,用于多种不同的目的。JAR文件可以包含在/META-INF/application.xml部署描述符中声明的EJB(EnterpriseJavaBeans),或者可用作EAR中两个或多个WAR模块的第三方库。
如同WAR文件一样,EAR也有着自己独有的类加载器架构。通常,需要在服务器类加载器和为每个模块分配的Web应用程序类加载器之间插入一个额外的类加载器。该类加载器用于将该企业级应用程序与其他企业级应用程序隔离开,但允许单个EAR中的多个模块之间共享通用库。Web应用程序类加载器可以是双亲委托优先模式(优先使用EAR库中的类)或子女委托优先模式(优先使用WAR文件中的类)。尽管了解EAR是非常有用的,它是完整Java EE规范的一部分,但是大多数只包含了Web容器的服务器(例如ApacheTomcat)并不支持它。