SSM的加载顺序
一直采用的是Tomcat,所以这里也以Tomcat加载SSM项目时配置文件的处理顺序作为例子.....
Tomcat加载顺序
Tomcat启动后,首先会先加载你项目下的web.xml,ssm在项目运行时,首先会加载web.xml,然后按序寻找如下的节点:
- context-param
- listener
- filter
- servlet
- interceptor
如下,是我从Tomcat的一次启动过程中按日志打印顺序截取出来的信息
信息: Initializing Spring root WebApplicationContext
信息: Initializing log4j from [classpath:log4j.properties]
信息: Initializing Spring FrameworkServlet 'spring-dispatcher'
而我的web.xml如下,首先是context-param:
<!--指定Spring配置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<!--指定log4j配置-->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:log4j.properties</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>600000</param-value>
</context-param>
接着是,监听器
<!-- Spring 容器加载 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
然后再是过滤器
<!--解决中文乱码问题 -->
<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>
最后是servlet和拦截器,SpringMVC其实是对servlet的封装来着,如果用过原生的servlet,我们都知道它需要在web配置好多的servlet和servlet-mapping节点。而通过<load-on-startup>
节点,我们可以指定Tomcat启动时初始化servlet的优先级,至于拦截器,其实本质上也是一个servlet来着~如登录判读,缓存判断.
<!-- SpringMVC的前端控制器 -->
<servlet>
<servlet-name>spring-dispatcher</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>
<!-- 何时启动 大于0的值表示容器启动时初始化此servlet,正值越小优先级越高 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Spring MVC配置文件结束 -->
<!-- SpringMVC拦截设置 -->
<servlet-mapping>
<servlet-name>spring-dispatcher</servlet-name>
<!-- 由SpringMVC拦截所有请求 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- SpringMVC拦截设置结束 -->
Spring加载顺序
Spring在初始化容器时,会先解析和加载所有的Bean (如果没有设置为延迟加载的话,即bean标签中的lazy-init没有设置为true),如果符合要求则通过Class生成BeanDefinition,存入BeanFactory中,在加载完所有Bean Class后,开始有序的通过BeanDefinition实例化Bean。同一级别的bean一般按照文件中的顺序进行初始化。
补充两点点,(1)如果一个bean的scope属性为scope="pototype"多例时,lazy-init的属性值即使设置为false,容器启动时也不会实例化bean,而是等到需要时,再进行实例化。(2)如果一个beanA依赖于另一个beanB,而你需要将beanA设置为单例,beanB设置为多例时,不能简单的将将beanB嵌套在beanA中,因为那样设置的话,由于外围的beanA在启动的时候就已经设置为单例了,而beanA又依赖于beanB,所以连带着beanB也被实例化,此时会将beanB也实例化为单例的。如下图所示:
地址都一样的:
解决方法时不用注入的方式,并将外围bean实现BeanFactoryAware 接口,如下:
public class TestBeans implements BeanFactoryAware {
private TestSingleton tool;
private BeanFactory factory;
public TestBeans(){
System.out.println("单例初始化");
}
public TestSingleton getTool() {
return tool;
}
public void init(){
System.out.println("多例初始化");
this.tool = (TestSingleton) factory.getBean("tool");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
factory = beanFactory;
}
}