• SpringSpring上下文和容器


    转载:

    Spring上下文和容器

    https://blog.csdn.net/qq_40298351/article/details/100995402

    Core Container模块是Spring整个架构的根基,其核心概念是BeanFactory,也正是这个概念让Spring成为一个容器,帮助Spring管理Bean,并提供DI(依赖注入)功能来实现对Bean的依赖管理,使用配置方式来达到与业务代码及框架代码的分离。

    Context模块即Spring上下文模块(也叫Spring Context模块),是Core Container模块的子模块,它让Spring真正成为一个可执行框架。这个模块扩展实现了BeanFactory,为Spring的扩展和架构继承提供了非常多的可能,比如校验框架、调度框架、缓存框架、模版渲染框架 等等。

    一、Spring上下文的设计
    Spring Context模式是Spring Core Container模块中的子模块。下面说说核心抽象类的职责。

    (1)ApplicationContext是整个容器的基本功能定义类,继承了BeanFactory,这说明容器也是工厂的多态实现。其实它利用了代理的设计方法,内部持有一个BeanFactory实例,这个实例替它执行BeanFactory接口定义的功能。

    (2)AbstractApplicationContext是整个容器的核心处理类,是真正的Spring容器的执行者,在内部使用了模版方法,实现了高复用

    高扩展,实现了Spring的启动、停止、刷新、事件推送、BeanFactory方法的默认实现及虚拟机回调的注册等。

    (3)GenericApplicationContext是Spring Context模块中最容易构建Spring环境的实体类,涵盖了Spring Context的核心功能,在不需要特殊定制的场景下可以实现开箱即用。AnnotationConfigApplicationContext完美利用了GenericApplicationContext的封装性和对外简单性,如果想扩展适合自己业务的轻量级Spring容器,使用GenericApplicationContext这个基类则会非常容易上手。AnnotationConfigApplicationContext的构造方法先传入一个class数组,在创建一个可执行的上下文实例来构造一个可运行的Spring运行环境,使用起来非常简便。

        private final AnnotatedBeanDefinitionReader reader;
     
        private final ClassPathBeanDefinitionScanner scanner;
     
        public AnnotationConfigApplicationContext() {
            this.reader = new AnnotatedBeanDefinitionReader(this);
            this.scanner = new ClassPathBeanDefinitionScanner(this);
        }
     
        public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
            super(beanFactory);
            this.reader = new AnnotatedBeanDefinitionReader(this);
            this.scanner = new ClassPathBeanDefinitionScanner(this);
        }
     
        
        public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
            // 实例化注解Bean定义读取实例,并按照class路径扫描Bean实例
            this();
            // 注册当前这个class数组,解析并添加这个Bean的描述到BeanFactory中
            register(annotatedClasses);
            // 启动Spring容器
            refresh();
        }

    4)AbstractRefreshableApplicationContext是XmlWebApplicationContext的核心父类,如果当前上下文持有BeanFactory,则关闭当前BeanFactory,然后为上下文生命周期的下一个阶段初始化一个新的BeanFactory,并且在创建新容器时仍然保持对其父容器的引用。

    二、Spring容器BeanFactory的设计
    Spring的核心功能就是实现对Bean的管理,比如Bean的注册、注入、依赖等。而Spring容器提供了依赖注入这个特征,以实现Spring容器对Bean的管理,而且使用IoC实现了对Bean的配置与实际应用代码的隔离。其中,Core Container模块的核心概念就是BeanFactory,它是所有Spring应用的核心。因为Spring的核心模型就是Bean模型,所以需要在管理Spring Bean的基础上保证Spring应用的运行。

    BeanFactory接口是Bean容器设计中基本的职责定义接口,定义了按照名称、参数、类型等几个维度获取、判断Bean实例的职能。

    三、Spring父子上下文与容器
    从ApplicationContext中可以看出,Spring提供了为当前BeanFactory和ApplicationContext设置父子引用的功能方法,BeanFactory像一个单向链表节点一样支持Spring的多容器场景。



    /**
         * Return the parent context, or {@code null} if there is no parent
         * and this is the root of the context hierarchy.
         * @return the parent context, or {@code null} if there is no parent
         */
        @Nullable
        ApplicationContext getParent();

        ApplicationContext接口对外提供获取父上下文的方法,既然能对外获取父上下文,那么肯定有上下文属性的设置方法或者初始化方法。最常用的是用构造方法和set方法手工指定。

    /**
         * Create a new AbstractApplicationContext with the given parent context.
         * @param parent the parent context
         */
        public AbstractApplicationContext(@Nullable ApplicationContext parent) {
            this();
            setParent(parent);
        }

        在SpringMVC环境中存在Spring父子容器时,子容器可以复用父容器的Bean实例从而避免重复创建。

        在使用SpringMVC时,如下配置会出现在web.xml中。

        <context-param>
               <param-name>contextConfigLocation</param-name>
               <param-value>classpath:applicationContext.xml</param-value>
       </context-param>
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
     
    <servlet>
            <servlet-name>Hello</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-config.xml</param-value>
            </init-param>
            <!-- 服务器已启动就加载 -->
            <load-on-startup>1</load-on-startup>
        </servlet>

    由于在web.xml中<listener-class>标签的加载早于<servlet>标签的加载,所以ContextLoaderListener在启动后会先创建一个Spring容器,之后在Dispatcher启动时还会实例化一个容器。

    HttpServletBean是HttpServlet的子类,它重写了init方法,调用如下方法进行初始化。

     
        @Override
        protected final void initServletBean() throws ServletException {
            // ..省略..
            try {
                // 创建Spring Web容器
                this.webApplicationContext = initWebApplicationContext();
                initFrameworkServlet();
            }
            // ..省略..    
        }

    创建Spring Web容器:

    protected WebApplicationContext initWebApplicationContext() {
            // 从Servlet上下文中属性中获取Listener中的容器
            WebApplicationContext rootContext =
                    WebApplicationContextUtils.getWebApplicationContext(getServletContext());
            WebApplicationContext wac = null;
     
            if (this.webApplicationContext != null) {
                
                wac = this.webApplicationContext;
                if (wac instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                    /* 如果父容器是一个web容器并且没有启动,
    则此时运行当前容器并且设置它的父容器,
    也就是说在XML中配置了两个Spring Servlet,仍然可以互相引用bean */
                    if (!cwac.isActive()) {
                        
                        if (cwac.getParent() == null) {
                            
                            cwac.setParent(rootContext);
                        }
                        configureAndRefreshWebApplicationContext(cwac);
                    }
                }
            }
            if (wac == null) {
                
                wac = findWebApplicationContext();
            }
            if (wac == null) {
                // 创建当前Servlet创建的web容器,并且把父容器创建在其中
                wac = createWebApplicationContext(rootContext);
            }
     
            if (!this.refreshEventReceived) {
                
                synchronized (this.onRefreshMonitor) {
                    // 实例化当前容器
                    onRefresh(wac);
                }
            }
     
            if (this.publishContext) {
                
                String attrName = getServletContextAttributeName();
                getServletContext().setAttribute(attrName, wac);
            }
     
            return wac;
        }

        AbstractRefreshableApplicationContext是Spring Web容器的核心基类,在SpringAbstractAplicationContext启动时调用refreshBeanFactory方法。

    @Override
        protected final void refreshBeanFactory() throws BeansException {
            //如果当前已经存在工厂,则销毁工厂中的Bean,关闭当前BeanFactory
            if (hasBeanFactory()) {
                destroyBeans();
                closeBeanFactory();
            }
            try {
                //创建Bean工厂
                DefaultListableBeanFactory beanFactory = createBeanFactory();
                beanFactory.setSerializationId(getId());
                //设置当前工厂Bean是否允许Bean定义重写覆盖
                //设置当前BeanFactory是否允许Bean循环引用
                customizeBeanFactory(beanFactory);
                //按照指定的配置把Bean定义加载到Bean工厂中
                loadBeanDefinitions(beanFactory);
                synchronized (this.beanFactoryMonitor) {
                    this.beanFactory = beanFactory;
                }
            }
            catch (IOException ex) {
                throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
            }
        }
     
    protected DefaultListableBeanFactory createBeanFactory() {
            return new DefaultListableBeanFactory(getInternalParentBeanFactory());
        }
     
    @Nullable
        protected BeanFactory getInternalParentBeanFactory() {
            //把父容器中的工厂作为父工厂放在当前容器工厂中
            return (getParent() instanceof ConfigurableApplicationContext ?
                    ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent());
        }

    由于SpringMVC中的容器之间存在关联(也就是父子容器),所以容器之间可以互相访问,子容器也可以共用父容器的Bean。但父容器不能共用子容器的Bean,这是因为当父容器已经启动时,子容器还没有实例化启动,这时如果父容器引用子容器的Bean,则是不可能正常运行的。

  • 相关阅读:
    [LeetCode] Power of Three 判断3的次方数
    [LeetCode] 322. Coin Change 硬币找零
    [LeetCode] 321. Create Maximum Number 创建最大数
    ITK 3.20.1 VS2010 Configuration 配置
    VTK 5.10.1 VS2010 Configuration 配置
    FLTK 1.3.3 MinGW 4.9.1 Configuration 配置
    FLTK 1.1.10 VS2010 Configuration 配置
    Inheritance, Association, Aggregation, and Composition 类的继承,关联,聚合和组合的区别
    [LeetCode] Bulb Switcher 灯泡开关
    [LeetCode] Maximum Product of Word Lengths 单词长度的最大积
  • 原文地址:https://www.cnblogs.com/whoknows1/p/16358291.html
Copyright © 2020-2023  润新知