Spring 源码阅读起始web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!---->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
<!--加载spring配置-->
classpath:config/spring/applicationContext-core.xml
</param-value>
</context-param>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>ServicePlatform.root</param-value>
</context-param>
<context-param>
<param-name>globalInitializerClasses</param-name>
<param-value>com.zhuguang.jack.applicationContextInitializer.MyApplicationContextInitializer</param-value>
</context-param>
<!--<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>600000</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:config/log/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>-->
<context-param>
<param-name>allowBeanDefinitionOverriding</param-name>
<param-value>true</param-value>
</context-param>
<listener>
<!-- 当加载servlet 的时候此监听器执行-->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--
<filter>
<filter-name>sessionFilter</filter-name>
<filter-class>com.zhuguang.jack.filter.SessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sessionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>-->
<servlet>
<servlet-name>spring-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--springmvc的配置文件-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/spring/spring-dispatcher.xml</param-value>
</init-param>
<init-param>
<param-name>detectAllHandlerMappings</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.zhuguang.jack.applicationContextInitializer.MyApplicationContextInitializer</param-value>
</init-param>
<init-param>
<param-name>allowBeanDefinitionOverriding</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
spring 与springmvc 的区别
spring
有时候不是web项目但是想用spring的 aop 和ioc 怎么办
new ClassPathXmlApplicationContext(“application.xml”);
springmvc
包括spring aop ioc的功能
mvc 表示 model view controller
可能有些读者就要问了,web.xml 里面即加载了spring的ioc 和aop ,springmvc 里面又加载了aop 和ioc 那么不重复了么?
重复也不重复。何解? spring 容器将先加载,然后springmvc 的容器将拷贝一份放入自己的容器中,初始化容器的方法不在同一个类中。所以是没有冲突的
tomcat会先调用spring的Servlet 于是配置的监听器(ContextLoaderListener)将会执行
/**
* Initialize the root web application context.
* 这是spring 自己的注释 很明显的会调用这个方法初始化
*/
@Override
public void contextInitialized(ServletContextEvent event) {
//参数是 servelt容器
initWebApplicationContext(event.getServletContext());
}
进入initWebApplicationContext方法
刚开始debug 发现context 一定为空所以会走createWebApplicationContext 方法
创建WebApplicationContext
/**
* Return the WebApplicationContext implementation class to use, either the
* default XmlWebApplicationContext or a custom context class if specified.
* @param servletContext current servlet context
* @return the WebApplicationContext implementation class to use
* @see #CONTEXT_CLASS_PARAM
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
//注释上写着返回WebApplicationContext 类型为默认的xnlWebApplicationContext 或者配置的类
protected Class<?> determineContextClass(ServletContext servletContext) {
//CONTEXT_CLASS_PARAM很明显如果配置则取的这个值, 我进去看了一下
//public static final String CONTEXT_CLASS_PARAM = "contextClass";
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
//很明显不配置为空走这里 ,contextClassName 和defaultStrategies有关
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
//反射出类然后返回, 完美
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
// 寻找defaultStrategies 发现了这个静态代码块 defaultStrategies 是Properties类型
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
private static final Properties defaultStrategies;
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
//那么真相只有一个 这边 和DEFAULT_STRATEGIES_PATH 有关,所以去ClassPath下寻找ContextLoader.properties
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
找到了 在这呢:
内容如上图,之所以默认是XmlWebApplicationContext 的原因也找到了
回到createWebApplicationContext()方法
if (this.context == null) {
//这边其实按照我的理解 就是xml 里面配置了就用xml 里面的 ,没有的话就用默认的
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
//如果没有parent属性就去servlet 里面找找看
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);
}
}
//servlet 也放了一份spring的
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
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
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
//contextid没有设置所以设为默认的
else {
// Generate default id...
//参数如上 类名加冒号+项目名称
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
//将servlet容器放入了spring容器中
wac.setServletContext(sc);
//查看web.xml里面有没有配置contextConfigLocation,有就放进来,很明显这个是spring 写死的名称
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
//实际上调到AbstractRefreshableWebApplicationContext类的方法里面了
wac.setConfigLocation(configLocationParam);
}
// 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(sc, null);
}
//当我们需要对spring的容器做点事情的时候 有时会用到这里
customizeContext(sc, wac);
wac.refresh();
}
如何在spring容器初始化之前做点事情呢?
首先调用了wac.refresh就是对容器初始化了
//传入sc 和spring容器
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
//重点在这个方法里面
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(sc);
if (initializerClasses.isEmpty()) {
// no ApplicationContextInitializers have been declared -> nothing to do
return;
}
ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
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 " +
"context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
wac.getClass().getName()));
}
initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
}
//对所用继承ApplicationContextInitializer的类排序
AnnotationAwareOrderComparator.sort(initializerInstances);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
//循环调用initialize方法
initializer.initialize(wac);
}
}
determineContextInitializerClasses()方法
public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";
public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
//只要在web.xml配置了globalInitializerClasses,contextInitializerClasses就会加载进来
protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
determineContextInitializerClasses(ServletContext servletContext) {
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();
String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
if (globalClassNames != null) {
for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
classes.add(loadInitializerClass(className));
}
}
String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
if (localClassNames != null) {
for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
classes.add(loadInitializerClass(className));
}
}
return classes;
}
至于如何排序则需要继承Ordered类
public class MyApplicationContextInitializer1 implements ApplicationContextInitializer,PriorityOrdered {
//在spring容器初始化之前对spring的上下文做一些修改
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("===========MyApplicationContextInitializer");
}
@Override
//返回值越大越先执行上面的方法
public int getOrder() {
return 1;
}
}
我们再来看看springmvc 是如何做的,看springmvc 的话,根据web.xml 中的指示我们应该去看DispatcherServlet这个类
//DispatcherServlet 这个类继承了FrameworkServlet
public class DispatcherServlet extends FrameworkServlet
//FrameworkServlet 继承了HttpServletBean 和实现了ApplicationContextAware(ApplicationContextAware可以获取spring上下文)
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
//HttpServletBean 继承了HttpServlet 所以 DispatcherServlet 这东西就是一个servlet
public abstract class HttpServletBean extends HttpServlet
implements EnvironmentCapable, EnvironmentAware
HttpServletBean 无脑看init 方法
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
// Let subclasses do whatever initialization they like.
//初始化bean
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
进入initServletBean()方法 又看见如下代码
@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 {
//这边也是初始化webApplicationContext 方法
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}