首页 > 编程语言 > 详细

Spring IOC容器初体验(二)

时间:2019-07-20 19:30:53      阅读:87      评论:0      收藏:0      [点我收藏+]

通过上篇文章(Spring IOC容器初体验(一)),我们可以对 Spring 框架体系有了一个基本的宏观了解。这篇文章开始体验IOC。

Web IOC容器初体验

 我们从大家最熟悉的DespatcherServlet开始,DespatcherServlet的父类HttpServletBean中有一个init()方法,如下:

技术分享图片
 1 /**
 2      * Map config parameters onto bean properties of this servlet, and
 3      * invoke subclass initialization.
 4      * @throws ServletException if bean properties are invalid (or required
 5      * properties are missing), or if subclass initialization fails.
 6      */
 7     @Override
 8     public final void init() throws ServletException {
 9         if (logger.isDebugEnabled()) {
10             logger.debug("Initializing servlet ‘" + getServletName() + "‘");
11         }
12 
13         // Set bean properties from init parameters.
14         PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
15         if (!pvs.isEmpty()) {
16             try {
17                 //定位资源
18                 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
19                 //加载配置信息
20                 ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
21                 bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
22                 initBeanWrapper(bw);
23                 bw.setPropertyValues(pvs, true);
24             }
25             catch (BeansException ex) {
26                 if (logger.isErrorEnabled()) {
27                     logger.error("Failed to set bean properties on servlet ‘" + getServletName() + "‘", ex);
28                 }
29                 throw ex;
30             }
31         }
32 
33         // Let subclasses do whatever initialization they like.
34         initServletBean();
35 
36         if (logger.isDebugEnabled()) {
37             logger.debug("Servlet ‘" + getServletName() + "‘ configured successfully");
38         }
39     }
View Code

 

在init方法中,真正完成初始化容器动作的逻辑其实是在initServletBean()方法中,我们继续跟进initServletBean()中的代码在FrameworkServlet类中:

技术分享图片
 1 /**
 2      * Overridden method of {@link HttpServletBean}, invoked after any bean properties
 3      * have been set. Creates this servlet‘s WebApplicationContext.
 4      */
 5     @Override
 6     protected final void initServletBean() throws ServletException {
 7         getServletContext().log("Initializing Spring FrameworkServlet ‘" + getServletName() + "‘");
 8         if (this.logger.isInfoEnabled()) {
 9             this.logger.info("FrameworkServlet ‘" + getServletName() + "‘: initialization started");
10         }
11         long startTime = System.currentTimeMillis();
12 
13         try {
14 
15             this.webApplicationContext = initWebApplicationContext();
16             initFrameworkServlet();
17         }
18         catch (ServletException ex) {
19             this.logger.error("Context initialization failed", ex);
20             throw ex;
21         }
22         catch (RuntimeException ex) {
23             this.logger.error("Context initialization failed", ex);
24             throw ex;
25         }
26 
27         if (this.logger.isInfoEnabled()) {
28             long elapsedTime = System.currentTimeMillis() - startTime;
29             this.logger.info("FrameworkServlet ‘" + getServletName() + "‘: initialization completed in " +
30                     elapsedTime + " ms");
31         }
32     }
View Code

在上面的代码中看到了initWebApplicationContext(),继续跟进:

技术分享图片
  1 /**
  2      * Initialize and publish the WebApplicationContext for this servlet.
  3      * <p>Delegates to {@link #createWebApplicationContext} for actual creation
  4      * of the context. Can be overridden in subclasses.
  5      * @return the WebApplicationContext instance
  6      * @see #FrameworkServlet(WebApplicationContext)
  7      * @see #setContextClass
  8      * @see #setContextConfigLocation
  9      */
 10     protected WebApplicationContext initWebApplicationContext() {
 11 
 12         //先从ServletContext中获得父容器WebAppliationContext
 13         WebApplicationContext rootContext =
 14                 WebApplicationContextUtils.getWebApplicationContext(getServletContext());
 15         //声明子容器
 16         WebApplicationContext wac = null;
 17 
 18         //建立父、子容器之间的关联关系
 19         if (this.webApplicationContext != null) {
 20             // A context instance was injected at construction time -> use it
 21             wac = this.webApplicationContext;
 22             if (wac instanceof ConfigurableWebApplicationContext) {
 23                 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
 24                 if (!cwac.isActive()) {
 25                     // The context has not yet been refreshed -> provide services such as
 26                     // setting the parent context, setting the application context id, etc
 27                     if (cwac.getParent() == null) {
 28                         // The context instance was injected without an explicit parent -> set
 29                         // the root application context (if any; may be null) as the parent
 30                         cwac.setParent(rootContext);
 31                     }
 32                     //这个方法里面调用了AbatractApplication的refresh()方法
 33                     //模板方法,规定IOC初始化基本流程
 34                     configureAndRefreshWebApplicationContext(cwac);
 35                 }
 36             }
 37         }
 38         //先去ServletContext中查找Web容器的引用是否存在,并创建好默认的空IOC容器
 39         if (wac == null) {
 40             // No context instance was injected at construction time -> see if one
 41             // has been registered in the servlet context. If one exists, it is assumed
 42             // that the parent context (if any) has already been set and that the
 43             // user has performed any initialization such as setting the context id
 44             wac = findWebApplicationContext();
 45         }
 46         //给上一步创建好的IOC容器赋值
 47         if (wac == null) {
 48             // No context instance is defined for this servlet -> create a local one
 49             wac = createWebApplicationContext(rootContext);
 50         }
 51 
 52         //触发onRefresh方法
 53         if (!this.refreshEventReceived) {
 54             // Either the context is not a ConfigurableApplicationContext with refresh
 55             // support or the context injected at construction time had already been
 56             // refreshed -> trigger initial onRefresh manually here.
 57             onRefresh(wac);
 58         }
 59 
 60         if (this.publishContext) {
 61             // Publish the context as a servlet context attribute.
 62             String attrName = getServletContextAttributeName();
 63             getServletContext().setAttribute(attrName, wac);
 64             if (this.logger.isDebugEnabled()) {
 65                 this.logger.debug("Published WebApplicationContext of servlet ‘" + getServletName() +
 66                         "‘ as ServletContext attribute with name [" + attrName + "]");
 67             }
 68         }
 69 
 70         return wac;
 71     }
 72 
 73     /**
 74      * Retrieve a {@code WebApplicationContext} from the {@code ServletContext}
 75      * attribute with the {@link #setContextAttribute configured name}. The
 76      * {@code WebApplicationContext} must have already been loaded and stored in the
 77      * {@code ServletContext} before this servlet gets initialized (or invoked).
 78      * <p>Subclasses may override this method to provide a different
 79      * {@code WebApplicationContext} retrieval strategy.
 80      * @return the WebApplicationContext for this servlet, or {@code null} if not found
 81      * @see #getContextAttribute()
 82      */
 83     @Nullable
 84     protected WebApplicationContext findWebApplicationContext() {
 85         String attrName = getContextAttribute();
 86         if (attrName == null) {
 87             return null;
 88         }
 89         WebApplicationContext wac =
 90                 WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
 91         if (wac == null) {
 92             throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
 93         }
 94         return wac;
 95     }
 96 
 97     /**
 98      * Instantiate the WebApplicationContext for this servlet, either a default
 99      * {@link org.springframework.web.context.support.XmlWebApplicationContext}
100      * or a {@link #setContextClass custom context class}, if set.
101      * <p>This implementation expects custom contexts to implement the
102      * {@link org.springframework.web.context.ConfigurableWebApplicationContext}
103      * interface. Can be overridden in subclasses.
104      * <p>Do not forget to register this servlet instance as application listener on the
105      * created context (for triggering its {@link #onRefresh callback}, and to call
106      * {@link org.springframework.context.ConfigurableApplicationContext#refresh()}
107      * before returning the context instance.
108      * @param parent the parent ApplicationContext to use, or {@code null} if none
109      * @return the WebApplicationContext for this servlet
110      * @see org.springframework.web.context.support.XmlWebApplicationContext
111      */
112     protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
113         Class<?> contextClass = getContextClass();
114         if (this.logger.isDebugEnabled()) {
115             this.logger.debug("Servlet with name ‘" + getServletName() +
116                     "‘ will try to create custom WebApplicationContext context of class ‘" +
117                     contextClass.getName() + "‘" + ", using parent context [" + parent + "]");
118         }
119         if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
120             throw new ApplicationContextException(
121                     "Fatal initialization error in servlet with name ‘" + getServletName() +
122                     "‘: custom WebApplicationContext class [" + contextClass.getName() +
123                     "] is not of type ConfigurableWebApplicationContext");
124         }
125         ConfigurableWebApplicationContext wac =
126                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
127 
128         wac.setEnvironment(getEnvironment());
129         wac.setParent(parent);
130         String configLocation = getContextConfigLocation();
131         if (configLocation != null) {
132             wac.setConfigLocation(configLocation);
133         }
134         configureAndRefreshWebApplicationContext(wac);
135 
136         return wac;
137     }
138 
139     protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
140         if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
141             // The application context id is still set to its original default value
142             // -> assign a more useful id based on available information
143             if (this.contextId != null) {
144                 wac.setId(this.contextId);
145             }
146             else {
147                 // Generate default id...
148                 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
149                         ObjectUtils.getDisplayString(getServletContext().getContextPath()) + ‘/‘ + getServletName());
150             }
151         }
152 
153         wac.setServletContext(getServletContext());
154         wac.setServletConfig(getServletConfig());
155         wac.setNamespace(getNamespace());
156         wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
157 
158         // The wac environment‘s #initPropertySources will be called in any case when the context
159         // is refreshed; do it eagerly here to ensure servlet property sources are in place for
160         // use in any post-processing or initialization that occurs below prior to #refresh
161         ConfigurableEnvironment env = wac.getEnvironment();
162         if (env instanceof ConfigurableWebEnvironment) {
163             ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
164         }
165 
166         postProcessWebApplicationContext(wac);
167         applyInitializers(wac);
168         wac.refresh();
169     }
View Code

从上面的代码可以看出,在configAndRefreshWebApplicationContext()方法中,调用了refresh()方法,这个是真正启动IOC容器的入口,后面会详细介绍。

IOC容器初始化以后,最后调用了DispatcherServlet的onRefresh()方法,在onRefresh()方法中,又直接调用了initStrategies()方法初始化SpringMVC的九大组件:

技术分享图片
 1 /**
 2      * This implementation calls {@link #initStrategies}.
 3      */
 4     @Override
 5     protected void onRefresh(ApplicationContext context) {
 6         initStrategies(context);
 7     }
 8 
 9     /**
10      * Initialize the strategy objects that this servlet uses.
11      * <p>May be overridden in subclasses in order to initialize further strategy objects.
12      */
13     //初始化策略
14     protected void initStrategies(ApplicationContext context) {
15         //多文件上传的组件
16         initMultipartResolver(context);
17         //初始化本地语言环境
18         initLocaleResolver(context);
19         //初始化模板处理器
20         initThemeResolver(context);
21         //handlerMapping
22         initHandlerMappings(context);
23         //初始化参数适配器
24         initHandlerAdapters(context);
25         //初始化异常拦截器
26         initHandlerExceptionResolvers(context);
27         //初始化视图预处理器
28         initRequestToViewNameTranslator(context);
29         //初始化视图转换器
30         initViewResolvers(context);
31         //
32         initFlashMapManager(context);
33     }
View Code

 

Spring IOC容器初体验(二)

原文:https://www.cnblogs.com/yybinger/p/11218802.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!