使用 SpringSource Slices 构建模块化 Web 应用

工程 | Rob Harrop | 2009 年 6 月 22 日 | ...

更新:增加了 Git 子模块说明。

我过去曾谈到为真正的模块化应用提供支持,现在很高兴宣布您可以访问 SpringSource Slices 的早期原型代码了。

构建与安装

您可以从我们的 Git 仓库访问源代码

git clone git://git.springsource.org/slices/slices.git
git submodule init
git submodule update

要构建 Slices 的打包版本,只需在 build-slices 目录中运行 ant clean jar package

cd slices/build-slices
ant clean jar package

这将在 target/artifacts 中生成一个 zip 文件,其中包含 Slices 子系统,然后可以将其安装到 dm Server 2.0 上

安装 Slices 只需要将新子系统添加到 dm Server,然后更新 dm Server 的配置文件以启动新子系统即可。Slices 应该兼容任何近期的 dm Server 2.0 快照版本。我这里使用的是 2.0.0.CI-R326-B274,我已经下载并解压到我的桌面上了

 unzip target/artifacts/springsource-slices-BUILD-20090622083953.zip -d ~/Desktop/springsource-dm-server-2.0.0.CI-R326-B274

接下来,必须更新 dm Server 的 kernel.properties 配置,以包含新的 slices 子系统。打开你的 dm Server 安装目录下的 config/kernel.properties 文件,编辑 Profile Configuration 部分,列出 slices 子系统并给配置文件一个合适的名称(我称之为 slices)

#######################
# Profile Configuration
#######################
profile.name=slices
profile.subsystems=	com.springsource.server.web,com.springsource.osgi.slices
profile.optionalSubsystems=

保存更新的文件,然后就可以尝试 Slices 示例应用了。

试用示例应用

Slices 仓库包含一个 Spring 3 PetClinic 示例的版本,随着我们为 Slices 添加新功能,我们不断对其进行增强和改进。

在使用示例之前,必须将其依赖项添加到你的 dm Server 安装目录中。依赖项列在示例的 dependencies.txt 文件中。将每个列出的依赖项下载到安装目录的 repository/bundles/usr 目录。然后,使用 -clean 启动选项启动 dm Server

./bin/startup.sh -clean

现在是构建和部署示例的时候了。进入 slices/samples/slices-petclinic/com.springsource.slices.petclinic.host 目录并运行 ant clean jar

cd samples/slices-petclinic/com.springsource.slices.petclinic.host
ant clean jar

然后可以通过将生成的 war 包复制到 dm Server 的 pickup 目录来部署 host

 cp target/artifacts/com.springsource.slices.petclinic.host.war ~/Desktop/springsource-dm-server-2.0.0.CI-R326-B274/pickup/

现在可以通过 http://localhost:8080/petclinic 访问 host 了

petclinic-no-slices

接下来,进入 slices/samples/slices-petclinic/com.springsource.slices.petclinic.appointments 目录并运行 ant clean jar 来构建 appointments slice

cd samples/slices-petclinic/com.springsource.slices.petclinic.appointments
ant clean jar

现在可以通过将生成的 war 包复制到 dm Server 的 pickup 目录来部署 slice 了

cp target/artifacts/com.springsource.slices.petclinic.appointments.war  ~/Desktop/springsource-dm-server-2.0.0.CI-R326-B274/pickup/

刷新 http://localhost:8080/petclinic,它现在将反映 appointments slice 的存在,并带有新的 Appointments 链接

petclinic-appointments-slice

如果您愿意,现在可以从 pickup 目录中删除 appointments war 包,以再次看到 slice 消失。

Slices 应用的结构

使用 Slices,您可以从多个 OSGi bundle 构建一个 Web 应用,每个 bundle 为您的应用 URL 空间中的一个独立子部分提供内容。Slices 应用采用父子结构排列,每个应用最多有一个父级,称为 host,以及零个或多个子级,称为 slices。我们正在开发一个简单的 Slices 示例,它看起来像这样

slice-anatomy

Host 内部

petclinic.host bundle 包含所有共享内容,如图片和 CSS,以及用于显示首页的控制器和 JSPs。host bundle 只是一个符合 RFC66 标准的 web bundle,其 web.xml 中配置了 SliceHostFilter。下面的代码片段展示了 petclinic.host bundle 中的 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	
    <filter>
    	<filter-name>host-filter</filter-name>
    	<filter-class>com.springsource.osgi.slices.core.SliceHostFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>host-filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

	<!-- Enables clean URLs with JSP views e.g. /welcome instead of /app/welcome -->
	<filter>
		<filter-name>UrlRewriteFilter</filter-name>
		<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>UrlRewriteFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
		
	<!-- Handles all requests into the application -->
	<servlet>
		<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>
				/WEB-INF/spring/*.xml
			</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
		
	<!-- Maps all /app requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
		<url-pattern>/app/*</url-pattern>
	</servlet-mapping>

  	<!-- Serves static resource content from the webapp root & .jar files such as spring-js.jar -->
	<servlet>
		<servlet-name>Resources Servlet</servlet-name>
		<servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class>
		<load-on-startup>0</load-on-startup>
	</servlet>
		
	<!-- Map all /resources requests to the Resource Servlet for handling -->
	<servlet-mapping>
		<servlet-name>Resources Servlet</servlet-name>
		<url-pattern>/resources/*</url-pattern>
	</servlet-mapping>	
	
</web-app>

如您所见,host 可以有任何正常的 servlet 和 filter 映射,但它需要 SliceHostFilter 才能将请求路由到其 slices。

Slice 内部

Slice 是一个 bundle,其外观和功能类似于标准的 RFC66 web bundle,但它没有自己的 ServletContext。相反,slice 运行在其 host 的 ServletContext 下。

Slices 的主要设计目标之一是确保 slice 开发与标准 web 应用开发尽可能相似。为此,您创建 slice web 内容的方式与为任何应用创建 web 内容的方式完全相同,即使用 web.xml。这是 petclinic.appointments bundle 中的 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- Handles all requests into the application -->
	<servlet>
		<servlet-name>appointments</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>		
		<load-on-startup>1</load-on-startup>
	</servlet>
		
	<!-- Maps all /app requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>appointments</servlet-name>
		<url-pattern>/app/*</url-pattern>
	</servlet-mapping>
	
</web-app>

appointments slice 只是委托给了 Spring DispatcherServlet。可以在 slice 中正常使用 Spring MVC。

slice 可以通过引用自身不存在的资源名称来访问 host bundle 中的资源。如果 slice 想要访问 host 中的资源,即使它自身也有同名资源,也可以使用 host: 前缀来实现

ServletContext context =  getServletContext();
context.getResource("host:/WEB-INF/some.config.xml");

slice 需要定义它想要附加到哪个 host,这通过 Slice-Host manifest header 实现。 Slice-ContextPath header 定义了 slice 处理的 URL 部分

Bundle-SymbolicName: petclininc.appointments
Slice-Host: petclinic.host;version="[1.0, 2.0)"
Slice-ContextPath: /appointments

Slice 生命周期

host 可以在没有附加任何相应的 slice 的情况下运行。当一个 slice 安装到与某个 host 匹配的服务器中时,该 slice 将附加到该 host。此时,host URL 部分中与新附加的 slice 的 Slice-ContextPath 匹配的子部分将被路由到该 slice。

当 slice 被卸载时,曾经路由到该 slice 的 URL 空间部分现在直接路由回 host。通过这种方式,您可以在 host 中创建内容来处理丢失的 slices。

今天 Slice 可以做些什么?

截至撰写本文时,Slices 代码库在 Slice 中支持以下功能

  • Servlets 和 servlet 映射
  • JSPs
  • Spring MVC
  • Slice 局部会话
  • 自动回退到应用范围会话
  • 通过 ServletContext 进行 Slice 局部资源查找
  • 自动回退到 host 中的资源查找
  • 使用 host: 进行显式 host 资源查找

接下来是什么?

我们正在为 Slices 开发许多有趣的新功能,包括

  • slices 中的 Filters 和 filter 映射
  • slices 中的 Listeners
  • UI 组合框架。您可以在 Petclinic 示例中看到手动实现方法
  • Tiles 2 集成
  • 完整的示例应用
  • SpringSource Tool Suite 支持

如果您有任何功能建议或发现错误,请随时在我们的 JIRA 上提交问题。

如果您想密切关注进展,可以关注我们的 Git 仓库和 #dmserver Twitter 标签。

订阅 Spring 电子报

订阅 Spring 电子报,保持联系

订阅

领先一步

VMware 提供培训和认证,助您快速提升。

了解更多

获取支持

Tanzu Spring 通过一个简单的订阅提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看全部