SpringMVC原始碼分析--容器初始化(四)FrameworkServlet
一下SpringMVC配置檔案的地址contextConfigLocation的配置屬性,然後其呼叫的子類FrameworkServlet的initServletBean方法。
其實FrameworkServlet是springMVC初始化IOC容器的核心,通過讀取配置的contextConfigLocation配置的springMVC配置檔案的地址來讀取各種初始化資訊和Bean注入資訊,來完成springMVC IOC 容器的初始化。接下來我們通過原始碼一步一步進行分析。
首先我們看initServletBean函式方法,其主要的操作就是this.webApplicationContext = initWebApplicationContext()完成webApplicationContext的初始化,webApplicationContext 即為springMVC的IOC容器,其實現類是XMLWebApplicationContext,
這樣我們發現,初始化工作在initWebApplicationContext函式方法中。
initWebApplicationContext函式方法是整個springMVC IOC實現的實現過程,函式程式碼段如下,//建立springMVC的IOC容器 @Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { //建立springMVC的IOC容器 this.webApplicationContext = initWebApplicationContext(); //沒有任何具體實現 initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }
WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext())是從ServletContext中獲取IOC容器,因為我們知道spring web在初始化時會將初始化的webApplicationContex儲存到ServletContext中,這樣我們就可以獲取到spring web容器了,並且把它作為父容器。
if (this.webApplicationContext != null) 當容器已經存在時,我們就不需要重新建立,只需要重新整理一下,將springMVC配置檔案中的屬性載入到webApplicationContext中就可以了,當不存在時我們就需要一步一步的建立webApplicationContext了。
wac = findWebApplicationContext()在ServletContext中查詢WebApplicationContext,說不定它已經被存放到ServletContext中去了。
如果沒有我們就需要建立wac = createWebApplicationContext(rootContext),這個步驟我們會接下來分析,建立完之後我們呼叫onRefresh(wac),這個方法在子類DispatcherServlet中實現,去初始化一些其他操作,現在已經完成了webApplicationContext的初始化工作了接下來就需要把webApplicationContext存放到ServletContext中去了getServletContext().setAttribute(attrName, wac),最後呼叫return wac完成springMVC IOC容器的初始化。
/**
*初始化和釋出web應用上下文,封裝了建立Spring容器上下文的整個過程
*
*/
protected WebApplicationContext initWebApplicationContext() {
//獲取由ContextLoaderListener初始化並註冊在ServletContext中的根上下文,一般是Spring的IOC容器記為rootContext
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//如果webApplicationContext已經不為空,表示這個Servlet類是通過程式設計式註冊到容器中的(Servlet 3.0 +中的ServletContext.addServlet()),
//上下文也由程式設計式傳入。若這個傳入的上下文還沒有被初始化,將rootContext上下文設定為它的父上下文,然後將其初始化,否則直接使用。
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
//判斷wac是否為空判斷是否已經完成上面的上下文的設定
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
//如果為空,說明該Servlet不是由程式設計式註冊到容器中的,在ServletContext中查詢上下文,查詢得到
//說明上下文已經以別的方式初始化並註冊在contextAttribute下,直接使用
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
//此時還為空,呼叫以下方法建立一個全新的以rootContext為父上下文的上下文,作為SpringMVC配置元素的上下文
//大多數情況下我們所使用的上下文就是這個新建的上下文
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
//在DispatcherServlet中複寫,會初始化一系列屬性
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
//將這個上下文釋出到ServletContext中,也就是將上下文以一個和Servlet類在web.xml中註冊名字有關的值為鍵
//設定為ServletContext的一個屬性
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
上面我們介紹了springMVC IOC容器的初始化操作,但很多具體操作是在其他函式中完成的,接下來我們一一分析,
findWebApplicationContext函式是從ServletContext中查詢webApplicationContext,springMVC初始化完成之後我們會看到它會將webApplicationContext儲存到ServletContext中,這樣我們可以在ServletContext中獲取到它。
//從當前容器中查詢SpringMVC的IOC容器
protected WebApplicationContext findWebApplicationContext() {
String attrName = getContextAttribute();
if (attrName == null) {
return null;
}
WebApplicationContext wac =
WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
}
return wac;
}
在上面介紹中我們並沒有真正介紹到webApplicationContext初始化的完整過程,其完整實現是在createWebApplicationContext函式中,其首先會例項化一個IOC容器,這只是一個空容器,並沒有相關屬性wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
接下來的操作就是對wac設定相關屬性
wac.setEnvironment(getEnvironment())設定環境
wac.setParent(parent)設定parent,這樣就把springMVC和Spring的兩個IOC容器連線了在一起
wac.setConfigLocation(getContextConfigLocation())設定web.xml中的springMVC的配置檔案,各種bean的注入
configureAndRefreshWebApplicationContext(wac),這個函式是關鍵,是解析springMVC配置檔案完成初始化的過程。
return wac返回已經初始化的webApplicationContext
//建立springMVC的IOC容器,
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
//獲取類XMLWebApplicationContext
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
//例項化一個IOC容器
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//設定IOC容器相關的屬性,這樣springMVC的IOC容器就建立好了
wac.setEnvironment(getEnvironment());
//設定parent,這樣就把springMVC和Spring的兩個IOC容器連線了在一起
wac.setParent(parent);
//設定web.xml中的springMVC的配置檔案,各種bean的注入
wac.setConfigLocation(getContextConfigLocation());
//初始化springMVC各種的相關配置,各種注入的Controller,配置檔案等
configureAndRefreshWebApplicationContext(wac);
return wac;
}
上面我們介紹到configureAndRefreshWebApplicationContext函式是完成springMVC配置檔案初始化過程的函式,其實它也是比較簡單的,也是設定springMVC IOC容器的相關屬性,
最終呼叫wac.refresh(),通過AbstractApplicationContext來完成對springMVC xml配置檔案的解析初始化操作,這邊我們就不再深入探究,在spring IOC容器初始化時我們再詳細介紹。
/*配置或者重新整理應用上下文*/
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
}
}
//設定springMVC IOC容器的相關屬性
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
//初始化springMVC各種的相關配置,各種注入的Controller,配置檔案等
wac.refresh();
}
通過以上函式的操作就完成了springMVC IOC容器的初始化操作,並且把spring web初始化的IOC容器作為父容器,這樣springMVC就可以獲得父容器中注入的各種Bean,同時初始化的IOC容器webApplicationContext也會被存放到ServletContex中,這樣在整個的web容器中都可以得到並使用IOC容器中的各種屬性。
FrameworkServlet完整原始碼解析:
//FrameworkServlet類的設計目的就是用來建立一個和Servlet關聯的Spring容器上下文
//並將其註冊到ServletContext中,跳脫開SpringMVC體系,我們也能通過繼承FrameworkServlet類
//得到與Spring容器整合的好處,FrameworkServlet和HttpServletBean一樣,是一個可以獨立使用的類
@SuppressWarnings("serial")
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
//servlet名稱空間字首
public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";
//FrameworkServlet類的上下文
public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";
private static final String INIT_PARAM_DELIMITERS = ",; \t\n";
private static final boolean responseGetStatusAvailable =
ClassUtils.hasMethod(HttpServletResponse.class, "getStatus");
private String contextAttribute;
private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
private String contextId;
private String namespace;
// web.xml中配置的contextConfigLocation屬性,就是springMVC的配置檔案
private String contextConfigLocation;
/** Actual ApplicationContextInitializer instances to apply to the context */
private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
/** Comma-delimited ApplicationContextInitializer class names set through init param */
private String contextInitializerClasses;
/** Should we publish the context as a ServletContext attribute? */
private boolean publishContext = true;
/** Should we publish a ServletRequestHandledEvent at the end of each request? */
private boolean publishEvents = true;
/** Expose LocaleContext and RequestAttributes as inheritable for child threads? */
private boolean threadContextInheritable = false;
/** Should we dispatch an HTTP OPTIONS request to {@link #doService}? */
private boolean dispatchOptionsRequest = false;
/** Should we dispatch an HTTP TRACE request to {@link #doService}? */
private boolean dispatchTraceRequest = false;
/** WebApplicationContext for this servlet */
private WebApplicationContext webApplicationContext;
/** If the WebApplicationContext was injected via {@link #setApplicationContext} */
private boolean webApplicationContextInjected = false;
/** Flag used to detect whether onRefresh has already been called */
private boolean refreshEventReceived = false;
public FrameworkServlet() {
}
public FrameworkServlet(WebApplicationContext webApplicationContext) {
this.webApplicationContext = webApplicationContext;
}
public void setContextAttribute(String contextAttribute) {
this.contextAttribute = contextAttribute;
}
public String getContextAttribute() {
return this.contextAttribute;
}
public void setContextClass(Class<?> contextClass) {
this.contextClass = contextClass;
}
public Class<?> getContextClass() {
return this.contextClass;
}
public void setContextId(String contextId) {
this.contextId = contextId;
}
public String getContextId() {
return this.contextId;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public String getNamespace() {
return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX);
}
//web.xml中配置檔案的地址
public void setContextConfigLocation(String contextConfigLocation) {
this.contextConfigLocation = contextConfigLocation;
}
public String getContextConfigLocation() {
return this.contextConfigLocation;
}
@SuppressWarnings("unchecked")
public void setContextInitializers(ApplicationContextInitializer<?>... initializers) {
if (initializers != null) {
for (ApplicationContextInitializer<?> initializer : initializers) {
this.contextInitializers.add((ApplicationContextInitializer<ConfigurableApplicationContext>) initializer);
}
}
}
public void setContextInitializerClasses(String contextInitializerClasses) {
this.contextInitializerClasses = contextInitializerClasses;
}
public void setPublishContext(boolean publishContext) {
this.publishContext = publishContext;
}
public void setPublishEvents(boolean publishEvents) {
this.publishEvents = publishEvents;
}
public void setThreadContextInheritable(boolean threadContextInheritable) {
this.threadContextInheritable = threadContextInheritable;
}
public void setDispatchOptionsRequest(boolean dispatchOptionsRequest) {
this.dispatchOptionsRequest = dispatchOptionsRequest;
}
public void setDispatchTraceRequest(boolean dispatchTraceRequest) {
this.dispatchTraceRequest = dispatchTraceRequest;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) {
this.webApplicationContext = (WebApplicationContext) applicationContext;
this.webApplicationContextInjected = true;
}
}
//建立springMVC的IOC容器
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
//建立springMVC的IOC容器
this.webApplicationContext = initWebApplicationContext();
//沒有任何具體實現
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
/**
*初始化和釋出web應用上下文,封裝了建立Spring容器上下文的整個過程
*
*/
protected WebApplicationContext initWebApplicationContext() {
//獲取由ContextLoaderListener初始化並註冊在ServletContext中的根上下文,一般是Spring的IOC容器記為rootContext
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//如果webApplicationContext已經不為空,表示這個Servlet類是通過程式設計式註冊到容器中的(Servlet 3.0 +中的ServletContext.addServlet()),
//上下文也由程式設計式傳入。若這個傳入的上下文還沒有被初始化,將rootContext上下文設定為它的父上下文,然後將其初始化,否則直接使用。
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
//判斷wac是否為空判斷是否已經完成上面的上下文的設定
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
//如果為空,說明該Servlet不是由程式設計式註冊到容器中的,在ServletContext中查詢上下文,查詢得到
//說明上下文已經以別的方式初始化並註冊在contextAttribute下,直接使用
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
//此時還為空,呼叫以下方法建立一個全新的以rootContext為父上下文的上下文,作為SpringMVC配置元素的上下文
//大多數情況下我們所使用的上下文就是這個新建的上下文
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
//在DispatcherServlet中複寫,會初始化一系列屬性
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
//將這個上下文釋出到ServletContext中,也就是將上下文以一個和Servlet類在web.xml中註冊名字有關的值為鍵
//設定為ServletContext的一個屬性
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
//從當前容器中查詢SpringMVC的IOC容器
protected WebApplicationContext findWebApplicationContext() {
String attrName = getContextAttribute();
if (attrName == null) {
return null;
}
WebApplicationContext wac =
WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
}
return wac;
}
//建立springMVC的IOC容器,
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
//獲取類XMLWebApplicationContext
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
//例項化一個IOC容器
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//設定IOC容器相關的屬性,這樣springMVC的IOC容器就建立好了
wac.setEnvironment(getEnvironment());
//設定parent,這樣就把springMVC和Spring的兩個IOC容器連線了在一起
wac.setParent(parent);
//設定web.xml中的springMVC的配置檔案,各種bean的注入
wac.setConfigLocation(getContextConfigLocation());
//初始化springMVC各種的相關配置,各種注入的Controller,配置檔案等
configureAndRefreshWebApplicationContext(wac);
return wac;
}
/*配置或者重新整理應用上下文*/
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
}
}
//設定springMVC IOC容器的相關屬性
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
//初始化springMVC各種的相關配置,各種注入的Controller,配置檔案等
wac.refresh();
}
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) {
}
protected void applyInitializers(ConfigurableApplicationContext wac) {
String globalClassNames = getServletContext().getInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM);
if (globalClassNames != null) {
for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
this.contextInitializers.add(loadInitializer(className, wac));
}
}
if (this.contextInitializerClasses != null) {
for (String className : StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS)) {
this.contextInitializers.add(loadInitializer(className, wac));
}
}
AnnotationAwareOrderComparator.sort(this.contextInitializers);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
initializer.initialize(wac);
}
}
@SuppressWarnings("unchecked")
private ApplicationContextInitializer<ConfigurableApplicationContext> loadInitializer(
String className, ConfigurableApplicationContext wac) {
try {
Class<?> initializerClass = ClassUtils.forName(className, wac.getClassLoader());
Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
if (initializerContextClass != null) {
Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
"Could not add context initializer [%s] since its generic parameter [%s] " +
"is not assignable from the type of application context used by this " +
"framework servlet [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
wac.getClass().getName()));
}
return BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class);
}
catch (Exception ex) {
throw new IllegalArgumentException(String.format("Could not instantiate class [%s] specified " +
"via 'contextInitializerClasses' init-param", className), ex);
}
}
public String getServletContextAttributeName() {
return SERVLET_CONTEXT_PREFIX + getServletName();
}
/**
* Return this servlet's WebApplicationContext.
*/
public final WebApplicationContext getWebApplicationContext() {
return this.webApplicationContext;
}
protected void initFrameworkServlet() throws ServletException {
}
public void refresh() {
WebApplicationContext wac = getWebApplicationContext();
if (!(wac instanceof ConfigurableApplicationContext)) {
throw new IllegalStateException("WebApplicationContext does not support refresh: " + wac);
}
((ConfigurableApplicationContext) wac).refresh();
}
//WebApplicationContext重新整理通知
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
onRefresh(event.getApplicationContext());
}
protected void onRefresh(ApplicationContext context) {
// For subclasses: do nothing by default.
}
/**
* Close the WebApplicationContext of this servlet.
* @see org.springframework.context.ConfigurableApplicationContext#close()
*/
@Override
public void destroy() {
getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
// Only call close() on WebApplicationContext if locally managed...
if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {
((ConfigurableApplicationContext) this.webApplicationContext).close();
}
}
/**
* Override the parent class implementation in order to intercept PATCH
* requests.
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String method = request.getMethod();
if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
//get請求
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
//post請求
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
//put請求
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
//delete請求
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
processRequest(request, response);
if (response.containsHeader("Allow")) {
// Proper OPTIONS response coming from a handler - we're done.
return;
}
}
// Use response wrapper for Servlet 2.5 compatibility where
// the getHeader() method does not exist
super.doOptions(request, new HttpServletResponseWrapper(response) {
@Override
public void setHeader(String name, String value) {
if ("Allow".equals(name)) {
value = (StringUtils.hasLength(value) ? value + ", " : "") + RequestMethod.PATCH.name();
}
super.setHeader(name, value);
}
});
}
@Override
protected void doTrace(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (this.dispatchTraceRequest) {
processRequest(request, response);
if ("message/http".equals(response.getContentType())) {
// Proper TRACE response coming from a handler - we're done.
return;
}
}
super.doTrace(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
protected LocaleContext buildLocaleContext(HttpServletRequest request) {
return new SimpleLocaleContext(request.getLocale());
}
protected ServletRequestAttributes buildRequestAttributes(
HttpServletRequest request, HttpServletResponse response, RequestAttributes previousAttributes) {
if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
return new ServletRequestAttributes(request, response);
}
else {
return null; // preserve the pre-bound RequestAttributes instance
}
}
private void initContextHolders(
HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {
if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
}
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
if (logger.isTraceEnabled()) {
logger.trace("Bound request context to thread: " + request);
}
}
private void resetContextHolders(HttpServletRequest request,
LocaleContext prevLocaleContext, RequestAttributes previousAttributes) {
LocaleContextHolder.setLocaleContext(prevLocaleContext, this.threadContextInheritable);
RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable);
if (logger.isTraceEnabled()) {
logger.trace("Cleared thread-bound request context: " + request);
}
}
private void publishRequestHandledEvent(
HttpServletRequest request, HttpServletResponse response, long startTime, Throwable failureCause) {
if (this.publishEvents) {
// Whether or not we succeeded, publish an event.
long processingTime = System.currentTimeMillis() - startTime;
int statusCode = (responseGetStatusAvailable ? response.getStatus() : -1);
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause, statusCode));
}
}
protected String getUsernameForRequest(HttpServletRequest request) {
Principal userPrincipal = request.getUserPrincipal();
return (userPrincipal != null ? userPrincipal.getName() : null);
}
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
//監聽WebApplicationContext的重新整理情況
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
private class RequestBindingInterceptor extends CallableProcessingInterceptorAdapter {
@Override
public <T> void preProcess(NativeWebRequest webRequest, Callable<T> task) {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
HttpServletResponse response = webRequest.getNativeRequest(HttpServletResponse.class);
initContextHolders(request, buildLocaleContext(request), buildRequestAttributes(request, response, null));
}
}
@Override
public <T> void postProcess(NativeWebRequest webRequest, Callable<T> task, Object concurrentResult) {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
resetContextHolders(request, null, null);
}
}
}
}