Spring-Spring上下文和容器

 

转载:

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,则是不可能正常运行的。

 

posted @ 2022-06-09 10:08  WhoKnows1  阅读(425)  评论(0编辑  收藏  举报