1.spring的配置
<web-app>
<display-name>Web Application</display-name>
<!--全局变量配置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-*.xml</param-value>
</context-param>
<!--监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--解决乱码问题的filter-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--Restful前端控制器-->
<servlet>
<servlet-name>springMVC_rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springMVC_rest</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
spring的执行流程
.
从spring的启动流程可以看出,先启动web容器,再启动spring。这里我们以tomcat为例
tomcat启动时会先加载自身conf文件夹下的文件context.xml,catalina.properties,server.xml,web.xml,,之后加载找到spring的web.xml,执行其中的内容。
在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。
当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来处理。在 ServletContextListener 接口中定义了处理ServletContextEvent 事件的两个方法。
public interface ServletContextListener extends EventListener {
public void contextInitialized(ServletContextEvent sce);
public void contextDestroyed(ServletContextEvent sce);
}
}
从这里可以看出,想要通过tomcat启动springmvc,就需要sping实现这个监听,tomcat的启动时,就会自动执行接口中内容。在spring中,这个接口监听就是web.xml中的ContextLoaderListener监听,spring的ContextLoaderListener类,继承了spring的ContextLoader类,实现了ServletContextListener类。tomcat启动时,会执行contextInitialized方法,这个方法的实现就在ContextLoader类中。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
让我们看看启动的方法
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
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!");
}
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
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 ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}
在这个方法中,参数servletContext是tomcat传递过来的事件中的参数,方法中最主要的就是createWebApplicationContext方法,和configureAndRefreshWebApplicationContext方法,createWebApplicationContext方法创建了WebApplicationContext,configureAndRefreshWebApplicationContext方法配置并刷新WebApplicationContext。
我们主要看configureAndRefreshWebApplicationContext最后一行的刷新方法refresh() ,默认实现在类AbstractApplicationContext中,
@Override
public void refresh()抛出BeansException, IllegalStateException {
同步(this.startupShutdownMonitor){
//为刷新做好准备。
prepareRefresh();
//告诉子类刷新内部bean工厂。
ConfigurableListableBeanFactory = obtainFreshBeanFactory();
//准备在此上下文中使用的bean工厂。
prepareBeanFactory(beanFactory);
尝试{
//允许在上下文子类中对bean工厂进行后处理。
postProcessBeanFactory(beanFactory);
//调用在上下文中注册为bean的工厂处理器。
invokeBeanFactoryPostProcessors(beanFactory);
//注册拦截bean创建的bean处理器。
registerBeanPostProcessors(beanFactory);
//初始化此上下文的消息源。
initMessageSource();
//为此上下文初始化事件多播器。
initApplicationEventMulticaster();
//初始化特定上下文子类中的其他特殊bean。
onRefresh();
//检查侦听器bean并注册它们。
有关registerlistener();
//实例化所有剩余的(非延迟初始化)单例。
finishBeanFactoryInitialization(beanFactory);
//最后一步:发布相应的事件。
finishRefresh();
}
catch(BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
//销毁已经创建的单例,以避免挂起资源。
destroyBeans();
//重置“活动”标志。
cancelRefresh(ex);
//将异常传播给调用方。
throw ex;
}
finally{
//重置Spring内核中的常用自检缓存,因为我们
//可能再也不需要单例bean的元数据了……
resetCommonCaches();
}
}
}
这个方法是不是很熟悉,很多讲解spring启动过程的文章都会说道这个方法,所以具体内容请自行百度