在前面的spring ioc源码分析中,我们从配置文件以及注解两方面简单分析了beanDefinition的创建以及bean的实例化过程。而目前企业开发中大都是基于web环境的。那么在web环境下,spring的ioc是在什么时候创建的?它又在什么地方起作用了?
1、从简单的ssm环境来看web环境下ioc容器的创建流程
这里环境搭建过程就省略了 将项目部署在tomcat下,然后启动tomcat
首先我们需要知道,springmvc也是基于servlet来进行web开发的。我们在web.xml中配置的DispatcherServlet就是我们请求的入口。
而对于一个servlet来说,必然会涉及他的生命周期。
init() service() destory()
很显然,springmvc肯定是在init的时候,去来初始化的ioc容器。所以我们就从init入手去看他的一个简单的流程
先来看一下DispatcherServlet的继承关系
直接父类就是FeameworkServlet了
DospatcherServlet的初始化
HttpServletBean 48 line
public final void init() throws ServletException {
try {
//一些配置的设置 略过
PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException var4) {
if (this.logger.isErrorEnabled()) {
this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
}
throw var4;
}
//初始化ioc容器
this.initServletBean();
}
初始化ioc容器 这里只是很简单的分析下 因为重点是springmvc,并且ioc在前面spring着重分析过了 这里大概走一遍就ok 具体的细节也不会去打断点去调试的
FrameworkServlet 186 line
protected final void initServletBean() throws ServletException {
//这里面都是打的日志 关键代码就是下面一行
try {
//初始化webApplicationContext
this.webApplicationContext = this.initWebApplicationContext();
//空方法
this.initFrameworkServlet();
}
......
}
上面只有一行代码是关键代码 我们先看一下WebApplicationContext的接口定义
继承ApplicationContext 多了一个获取ServletContext的方法的定义
public interface WebApplicationContext extends ApplicationContext {
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
String SCOPE_REQUEST = "request";
String SCOPE_SESSION = "session";
String SCOPE_GLOBAL_SESSION = "globalSession";
String SCOPE_APPLICATION = "application";
String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
ServletContext getServletContext();
}
继续往下走,初始化webApplicationContext
FrameworkServlet 212 line
protected WebApplicationContext initWebApplicationContext() {
//首先根据servletContext去获取一下WebApplicationContext
//可以看到这里用了rootContext 也就说明可能会有父子容器
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
//初始化ioc容器
this.configureAndRefreshWebApplicationContext(cwac);
}
}
}
//如果WebApplicationContext为null,在这里会根据servletContext的一些属性去获取 略过吧
if (wac == null) {
wac = this.findWebApplicationContext();
}
//如果还没有WebApplicationContext 那么就去初始化一个
if (wac == null) {
//创建一个WebApplicationContext
wac = this.createWebApplicationContext(rootContext);
}
......
return wac;
}
创建WebApplicationContext
FrameworkServlet 266 line
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
Class<?> contextClass = this.getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
} else {
//创建一个ConfigurableWebApplicationContext
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(this.getEnvironment());
wac.setParent(parent);
//如果设置了springmvc.xml文件位置,则会在这里设置 没设置后面就用默认的
wac.setConfigLocation(this.getContextConfigLocation());
//初始化ioc容器
this.configureAndRefreshWebApplicationContext(wac);
return wac;
}
}
初始化ioc容器
FrameworkServlet 284 line
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
//设置容器的id 全类名+dispatcherServlet
//org.springframework.web.context.WebApplicationContext:/dispatcherServlet
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
if (this.contextId != null) {
wac.setId(this.contextId);
} else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + '/' + this.getServletName());
}
}
//ioc持有servletContext 那么这个web资源都可以从这里获取
wac.setServletContext(this.getServletContext());
wac.setServletConfig(this.getServletConfig());
//dispatcherServlet-servlet 这个就是默认springmvc.xml的名字
wac.setNamespace(this.getNamespace());
//添加了一个监听器
wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener()));
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());
}
this.postProcessWebApplicationContext(wac);
this.applyInitializers(wac);
//初始化ioc容器 可以不用往下看了 这一块就是springioc的refresh方法了
wac.refresh();
}
简单说一下配置文件是怎么获取的
我们可以看到上面设置了wac.setNamespace(this.getNamespace()); 如果我们自己有在web.xml里面设置springmvc.xml文件的位置,就用我们自己设置的 如果没有,那么就会使用默认的
获取加载bd的xml文件的路径
this.getConfigLocations();
使用默认的路径 观察下面代码可以看到
默认的为 /WEB-INF/dispatcherServlet-servlet.xml
protected String[] getDefaultConfigLocations() {
return this.getNamespace() != null ? new String[]{"/WEB-INF/" + this.getNamespace() + ".xml"} : new String[]{"/WEB-INF/applicationContext.xml"};
}
那么如果nameSpace也为null,就是取/WEB-INF/applicationContext.xml
那么什么时候设置的namespace呢,通过查看源码,我们发现 其实他会取默认的servletName再拼接-servlet
FrameworkServlet 129 line
public String getNamespace() {
return this.namespace != null ? this.namespace : this.getServletName() + "-servlet";
}
到了这里,我们的基于springmvc的一部分bean已经加载完了,并且我们也得知了springmvc.xml需要放置的位置,以及他的命名的一些要求
还有一部分bean的加载是在哪里呢? 别忘记我们还有一个applicationContext.xml文件,那么他又是如何被加载的呢?
附带一下项目结构吧