当前位置: 代码迷 >> Web前端 >> Web开发中获取Spring的ApplicationContext的三种模式
  详细解决方案

Web开发中获取Spring的ApplicationContext的三种模式

热度:166   发布时间:2012-09-27 11:11:17.0
Web开发中获取Spring的ApplicationContext的三种方式

前两天在写这编文章的时候出了N次错,今天还是决定重新把它简单的记录一下。

?

在 WEB 开发中,可能会很少需要显示的获得 ApplicationContext 来得到由 Spring 进行管理的某些 Bean, 今天我就遇到了,在这里和大家分享一下, WEB 开发中,怎么获取 ApplicationContext

一?????? 要想怎么获取 ApplicationContext, 首先必须明白 Spring 内部 ApplicationContext 是怎样存储的。下面我们来跟踪一下源码

首先:从大家最熟悉的地方开始?

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

?上面这一段,大家很熟悉吧。好,让我们看一看它到底实现了些啥。

?

?

public class ContextLoaderListener implements ServletContextListener {

	private ContextLoader contextLoader;

	/**
	 * Initialize the root web application context.
	 */
	public void contextInitialized(ServletContextEvent event) {
		this.contextLoader = createContextLoader();
		this.contextLoader.initWebApplicationContext(event.getServletContext());
	}//下面的略

?
显然,ContextLoaderListener实现了ServeletContextListenet,在ServletContext初始化的时候,会进行Spring的初始化,大家肯定会想,Spring的初始化应该与ServletContext有一定关系吧?有关系吗?接下来让我们进入

ContextLoader.initWebApplicationContext方法

?

?

public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
			throws IllegalStateException, BeansException {

		//从ServletContext中查找,是否存在以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为Key的值
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}

				
		try {
			// Determine parent for root web application context, if any.
			ApplicationContext parent = loadParentContext(servletContext);

			// it is available on ServletContext shutdown.
			this.context = createWebApplicationContext(servletContext, parent);
			//将ApplicationContext放入ServletContext中,其key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
			//将ApplicationContext放入ContextLoader的全局静态常量Map中,其中key为:Thread.currentThread().getContextClassLoader()即当前线程类加载器
			currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);

			return this.context;
		}
	}

?从上面的代码大家应该明白了Spring初始化之后,将ApplicationContext存到在了两个地方,那么是不是意味着我们可以通过两种方式取得ApplicationContext?

?

第一种获取方式:


?注意:WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

即为 "org.springframework.web.context.WebApplicationContext.ROOT"

?

那么咱们是不是可以这样获得ApplicationContext:

??

request.getSession().getServletContext().getAttribute("org.springframework.web.context.WebApplicationContext.ROOT")

?确实可以,而且我们想到这种方法的时候,Spring早就提供给我们接口了:

?

public abstract class WebApplicationContextUtils {
	
		 
public static WebApplicationContext getRequiredWebApplicationContext(ServletContext sc)
	    throws IllegalStateException {

		WebApplicationContext wac = getWebApplicationContext(sc);
		if (wac == null) {
			throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
		}
		return wac;
	}

?getWebApplicationContext方法如下:

?

?

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
		return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
	}

?哈哈,明白了吧,它和我们自己实现的方法是一样的。

?

现在看一下第二种方法:

?

前面说到Spring初始化的时候,将ApplicationContext还存了一份到ContextLoader的Map里面,那么我们是不是可以通过Map.get(key)? ???很不幸的是,这个Map是私有的。LOOK:

?

private static final Map currentContextPerThread = CollectionFactory.createConcurrentMapIfPossible(1);

?

不过,不用担心Spring为我们提供了方法:再LOOK:

?

public static WebApplicationContext getCurrentWebApplicationContext() {
		return (WebApplicationContext) currentContextPerThread.get(Thread.currentThread().getContextClassLoader());
	}

?

这下我们放心了吧!哈哈,第二种方法也搞定了。第二种方法与第一种方法相比有什么好的地方呢?就是它不需要参数,只要在Web容器中,当Spring初始化之后,你不需要传入任何参数,就可以获得ApplicationContext为咱们服务。是不是很好?不过作者在用这个方法的时候,发现在Spring2.52版本中是不存在的,但是在2.5.5版本中提供了!!

?

其实第二种获取方法看上去简单,但他的原理还是有一定难度的,他与类加载器的线程上下文相关,有不知道大家有没有听说过,这个线程上下文在咱们常用的Mysql驱动中有用到,下次可以和大家一起分享一下。

?

第三种方式:借用ApplicationContextAware,ApplicationContext的帮助类能够自动装载ApplicationContext,只要你将某个类实现这个接口,并将这个实现类在Spring配置文件中进行配置,Spring会自动帮你进行注

入?ApplicationContext.ApplicationContextAware的代码结构如下:

?

?

public interface ApplicationContextAware {
	
		void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

?

就这一个接口。可以这样简单的实现一个ApplicationContextHelper类:

?

?

?

?

public class ApplicationHelper implements ApplicationContextAware {

	
	private ApplicationContext applicationContext;
	
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
			this.applicationContext = applicationContext;
	}

	
	public  ApplicationContext getApplicationContext(){
		return this.applicationContext;
	}
}

?

通过ApplicationHelper我们就可以获得咱们想要的AppilcationContext类了

?

?

1 楼 dxm1986 2010-09-19  
我晕,某些标红的地方,又变成<SPAN style="COLOR: #ff0000">****</SPAN>了,javaeye的编辑器有问题呀。
2 楼 zywang 2010-09-20  
你找找用Struts集成Spring的时候wac被保存到什么地方去的吧,貌似不是org.springframework.web.context.WebApplicationContext.ROOT这个地址,我讲的是spring2.5.5版本的
3 楼 dxm1986 2010-09-20  
zywang 写道
你找找用Struts集成Spring的时候wac被保存到什么地方去的吧,貌似不是org.springframework.web.context.WebApplicationContext.ROOT这个地址,我讲的是spring2.5.5版本的

我试了是可以的。

ApplicationContext application = (ApplicationContext)req.getSession().getServletContext().getAttribute("org.springframework.web.context.WebApplicationContext.ROOT");

这样我取得了我想要的ApplicationContext

我不知你初始化Spring是用的哪个,如果用org.springframework.web.servlet.DispatcherServlet进行初始化,在DispatcherServlet extends FrameworkServlet
FrameworkServlet.initWebApplicationContext()方法中有
getServletContext().setAttribute(attrName, wac);

debug时发现attrName="org.springframework.web.servlet.FrameworkServlet.CONTEXT.dis" 不知道你是不是说的这种情况。
4 楼 dxm1986 2010-09-20  
PS:
org.springframework.web.servlet.FrameworkServlet.CONTEXT.dis 其中dis为DispatcherServlet在web.xml中的配置名字。

String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);

public String getServletContextAttributeName() {
    return SERVLET_CONTEXT_PREFIX + getServletName();
}

public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";

public final String getServletName() {
return (getServletConfig() != null ? getServletConfig().getServletName() : null);
}
  相关解决方案