赞
踩
Spring 是Java 程序猿的必经之路,但对于 Spring 的学习,相信大多人停留在会使用的阶段,那你了解 Spring 加载时都做了哪些工作?Spring 上下文是如何加载的?Spring 是怎么实现灵活扩展的?只有弄明白了这些,才能更好的明白 Spring 的实现细节,才能更好的驾驭 Spring !
容器的概念很多人可能经常放嘴边,毕竟,容器概念的热度很高。那什么是容器?Linux Container?Docker?一个简单的Map?答案是都对,这都是容器,我们喝水的杯子也是容器,海边码头的集装箱也是容器,容器的作用就是“装”——“装”东西!容器的概念是很宽泛的,重点是这种隔离性的容器思想。
相信大多数 DevOps 都经历过类似这种的事情,开发好的应用程序要进行部署,于是,安装操作系统和各种依赖环境,部署好了以后,公司现在要清理服务器,需要将部署的应用程序移植到其他的服务器。那我们是不是需要重新部署?这时,聪明的前辈们就在想,有没有办法能让部署好的服务可以轻便的移植到其他地方,可以不用在安装一套操作系统和依赖环境。这就像集装箱运载一样,把货物,一辆兰博基尼(好比开发好的应用程序),打包到一个集装箱里,通过货轮从上海码头(CentOS7环境)运输到别的码头(Ubuntu14环境),而且运输期间,我的兰博基尼(应用程序)没有收到任何损坏(文件丢失),在另一个码头卸货后,依然可以极速飞驰。(启动正常)。当然,这个问题现在已经得到很好的解决了!这就是容器技术的应用~
Spring 最核心的就是容器,那 Spring 容器是Map吗?
Spring 容器不仅仅是 Map,因为 Spring 容器不仅仅是存放Bean,最主要的功能是“管理Bean的声明周期和依赖”。对象的创建,销毁…
组件A依赖组件B和C,C依赖D,D依赖B…现在我要得到组件A,就需要相应的一层层的初始化其他组件,其他组件之间可能存在相互依赖的情况,这种层层依赖的管理如果人工管理就会非常麻烦~容器会帮助我们管理组件,让开发人员专注于程序的开发。
所以,管理Bean是Spring 容器核心要解决的事情!
想明白 Spring 的加载机制,就必须先清楚 Spring ApplicationContext 到底是什么,以及与 BeanFactory 的关系。
Spring ApplicationContext 是围绕 Spring 整体来设计的,从类型上看它是 BeanFactory 类型,因为它是 BeanFactory 的实现类,功能比 BeanFactory 丰富,可以理解为 ApplicatonContext 扩展了 BeanFactory,是 Spring ApplicationContext 框架的核心设计。
通过看类图得知,AbstractApplicationContext 是 ApplicationContext 的实现类,也是最重要的核心类。其中核心方法 refresh就是AbstractApplicationContext 类提供的。
看看AbstractApplicationContext 的 refresh() 方法做了什么?
@Override public void refresh() throws BeansException, IllegalStateException { // 互斥锁,防止重复调用,保证上下文的对象状态 synchronized(this.startupShutdownMonitor) { // 启动前准备参数 this.prepareRefresh(); // 获得子类创建的 BeanFactory 实例,如已经存在,销毁重新创建 ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); // 给新创建的 BeanFactory实例,准备上下文环境--类加载器,bean表达式解析器,bean后置处理器等 this.prepareBeanFactory(beanFactory); try { // 在标准初始化后,修改应用上下文的内部beanFactory,通过重写此方法加载所有bean,但是不实例化bean。 // 调用 BeanFactory 实例化后置处理器 this.postProcessBeanFactory(beanFactory); // 调用 工厂处理器 注册 bean this.invokeBeanFactoryPostProcessors(beanFactory); // 在 Bean 工厂中注册 Bean 的后置处理器 this.registerBeanPostProcessors(beanFactory); // 初始化消息源,并且设置父消息源来自父容器的配置 this.initMessageSource(); // 初始化 消息推送器,注册一个默认的单例 Bean this.initApplicationEventMulticaster(); // 调用子类重写的方法初始化其他 bean,模板方法设计模式的体现! this.onRefresh(); // 注册 bean 监听器 this.registerListeners(); // 初始化所有剩余的单例 bean(非延迟加载的)并设置冻结标志位,防止重新实例化 Bean 浪费资源 this.finishBeanFactoryInitialization(beanFactory); // 注册、启动 LifecycleProcessor,并且发送启动完成事件 this.finishRefresh(); } catch (BeansException var9) { if (this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } // 销毁已经创建的单例 bean ,避免悬挂资源 this.destroyBeans(); // 释放标志位,标识其可以重新启动 this.cancelRefresh(var9); throw var9; } finally { // 清除与反射相关的缓存 this.resetCommonCaches(); } } }
通读以上源码,可得知,初始化 bean 的时候使用了模板方法设计模式,让子类去实现自己的实例加载。下面我们来看看子类是如何通过 AbstractApplicationContext 基类实现灵活扩展实例的。
我们来看看 AbstractApplicationContext 的子类 XmlWebApplicationContext 是如何通过基类的模板方法实现的扩展适应 Web 场景的上下文实例。
@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 创建一个读取XML文件的实例对象,并将读取到的 Bean 描述定义信息加载到 BeanFactory 中 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 配置读取 Bean 的上下文环境 beanDefinitionReader.setEnvironment(getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 使用当前 reader 读取指定的 XML 文件中的 Bean 描述信息到工厂中 initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); } protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { String[] configLocations = getConfigLocations(); if (configLocations != null) { // 读取 XML 配置文件 for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation); } } } // 子类 StaticWebApplicationContext 重写父类 AbstractApplicationContext 的方法,使用 BeanFactory 实例化后置处理器 @Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 在当前的 Bean 工厂中添加 Bean 后置处理器 beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig)); beanFactory.ignoreDependencyInterface(ServletContextAware.class); beanFactory.ignoreDependencyInterface(ServletConfigAware.class); // 注册 Web 应用的 Bean 作用范围、WebRequest、Session,详情见下方! WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext); // 注册 上下文环境和配置参数,并注册两个 Bean 用于存放这些参数 WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig); }
WebApplicationContextUtils 的 registerWebApplicationScopes 方法!
// 注册 web 应用的 Bean 作用域范围 public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, @Nullable ServletContext sc) { beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope()); beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope()); if (sc != null) { ServletContextScope appScope = new ServletContextScope(sc); beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope); // 注册 Servlet 上下文属性 sc.setAttribute(ServletContextScope.class.getName(), appScope); } beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()); beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory()); beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory()); beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory()); if (jsfPresent) { // 避免 jsf 依赖 FacesDependencyRegistrar.registerFacesDependencies(beanFactory); } }
通读以上源码,可以看出 XmlWebApplicationContext 是如何利用基类 AbstractApplicationContext ,灵活扩展适应 web 场景的上下文实例的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。