在应用程序中使用Spring启动嵌入式Jetty并让Web程序共享同一个Application Context(一)...

Jetty 的简单灵活特性使之很适合用于嵌入到应用程序当中。

 

比如开发一个多媒体资料管理库应用程序,你想用Web完成所有的用户界面,这时就可以让Jetty嵌入到你的应用程序作为Web容器。

 

在应用程序里启动 Jetty 的方法很简单,使用如下代码即可:

Server server = new Server();

SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(8080);
server.addConnector(connector);

WebAppContext context = new WebAppContext();

context.setContextPath("/");
context.setDescriptor("web/WEB-INF/web.xml");
context.setResourceBase("web");
context.setConfigurationDiscovered(true);

server.setHandler(context);
server.start();

这里有更详细的示例:http://wiki.eclipse.org/Jetty/Tutorial/Embedding_Jetty

 

通常我们的 Web 程序还会使用 Spring、 Struts、 Hibernate,为了让 Web 程序能使用 Spring ,我们只需在 web.xml 配置文件里加入如下代码即可:

 

 

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

 

这样就可以让 Spring 管理 Service、 Reposity、事务等。另外在 web.xml 加上如下代码即可启用 Struts2 的支持

 

<filter>
	<filter-name>struts2</filter-name>
	<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>

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

 

 

(注:你还需要 struts2-spring-plugin.jar 包才能让 Struts2 和 Spring 整合起来)

这样一个简单的嵌入式 Web 环境即搭建完成。

 

假如你在启动 Jetty 之前就已经创建和使用 Spring 的 Application Context 了,比如我在 Web 之外还要提供另外一个网络接口作为应用程序API的,而这个网络接口同时也依赖于 Spring 的 Application Context,这样你就会遇到一个问题:应用程序启动后会创建一个 context,当Jetty启动后又会创建一个 context,而刚好这两个 context 的内容其实是一样的。那么能不能让 Jetty 不创建新的 application context,而直接共享使用之前应用程序所创建的那个呢?

 

经过实践,得出其中的一个方法如下:

 

首先你要创建自己的 ContextLoaderListener 以替换掉 Spring 自带的那个,并把 web.xml 配置文件更改如下:

 

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value></param-value>
</context-param>

<listener>
	<listener-class>com.test.ShareContextLoaderListener</listener-class>
</listener>

 

需要注意的是 “contextConfigLocation” 这个配置不能省略,否则启动 Jetty 时会出现异常。 其中 com.test.ShareContextLoaderListener.java 就是自己写的,代码如下:

 

package com.test;

import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.ContextLoaderListener;

public class ShareContextLoaderListener extends ContextLoaderListener {
	
	@Override
	protected ContextLoader createContextLoader() {
		return new ShareContextLoader(); 
	}
}

 

 

实际上我只是继承Spring原有的 ContextLoaderListener 并将其中的 createContextLoader() 方法重写。上面代码中的 ShareContextLoader.java 的代码如下:

 

package com.test;

import javax.servlet.ServletContext;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.ContextLoader;

public class ShareContextLoader extends ContextLoader {
	@Override
	protected ApplicationContext loadParentContext(ServletContext servletContext)
			throws BeansException {
		return (ApplicationContext) servletContext
				.getAttribute("applicationContext");
	}
}

 

 

从上面的代码可见,其实应用程序所创建的 application context 是通过 servletContext 的 attribute 集合传递过来的。所以我们还要适当更改启动 Jetty 的代码:

 

package com.test;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;

public class JettyDaemon implements ApplicationContextAware{

	private Server server;
	private ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = applicationContext;
		
	}
	
	@Override
	public void start() throws Exception{
		server = new Server();

		SelectChannelConnector connector = new SelectChannelConnector();
		connector.setPort(8080);
		server.addConnector(connector);
		
		WebAppContext context = new WebAppContext();

		context.setContextPath("/");
		context.setDescriptor("web/WEB-INF/web.xml");
		context.setResourceBase("web");
		context.setConfigurationDiscovered(true);
		
		// 以下两行是关键所在 //
		context.setClassLoader(applicationContext.getClassLoader());
		context.setAttribute("applicationContext", applicationContext);
		
		server.setHandler(context);
		server.start();
	}
}

 

 

上面的这两行代码很关键:

context.setClassLoader(applicationContext.getClassLoader());
context.setAttribute("applicationContext", applicationContext);

 

第一行是为了让 web app 使用跟 application context 同一个 class loader。

第二行是为了将 application context 通过 servletContext 的 attribute 集合传递给 ShareContextLoader,这里的 applicationContext 对象是由于当前类继承了 ApplicationContextAware 接口而得来的。

 

这样我们就可以顺便在 Spring 的 applicationContext.xml 配置启动 Jetty 了,applicationContext.xml 文件的片段如下:

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" … … … … … …>
	<!-- daemons -->
	<bean id="webDaemon" class="com.test.JettyDaemon" init-method="start" />
	… … … … … …

 

 

现在应用程序的 main() 方法里面只需加载 Spring context 即可,其他的所有工作就让 Spring 来完成吧。

 

public static void main(String[] args){
	applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
	applicationContext.registerShutdownHook();
}

 

注:这篇文章的各个组件的版本如下:

Jetty:7.0.2

Spring:2.5.6

Struts 2.1.8

Hibernate 3.5.0

 

下一篇将会介绍另一种比较简单的方法:

在应用程序中使用Spring启动嵌入式Jetty并让Web程序共享同一个Application Context(二)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值