欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

第二节 Spring框架之二IOC容器的继续研究

程序员文章站 2022-03-19 15:02:16
...

一. IOC容器的探索

  1. 从容器的创建来探索
    用ClassPathXMLApplicationContext来创建,在这个类中,有很多个重载的构造方法,其中目前主要用以下这四个构造器,在内部实现上,这四个构造器调用的还是同一个构造方法。真正实现容器的刷新并且启动和创建bean组件的构造方法
    第二节 Spring框架之二IOC容器的继续研究

在这个构造器中最重要的方法就是refresh()方法,由它来负责刷新IOC容器,启动和创建bean。这个方法的实现如下,重要的注释已经在代码中注释

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();
            // Tell the subclass to refresh the internal bean factory.加载好了配置文件
            //工厂中在解析XML的时候,解析完成后会保存每一个要创建的bean信息,但是并没有创建
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);
            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);
                // Invoke factory processors registered as beans in the context.                invokeBeanFactoryPostProcessors(beanFactory);
                // Register bean processors that intercept bean creation.   
                                              registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.给子类实现的接口
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                **finishBeanFactoryInitialization(beanFactory);//初始化所有的单例类**

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }
        }
    }

在这个方法中起决定作用的是finishBeanFactoryInitialization(beanFactory)这个方法,这个方法的具体实现如下

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // Initialize conversion service for this context.和类型转换有关的
        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
        }

        // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        for (String weaverAwareName : weaverAwareNames) {
            getBean(weaverAwareName);
        }

        // Stop using the temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(null);

        // Allow for caching all bean definition metadata, not expecting further changes.冻结配置,bean的配置信息都在beanfactory的一个map中保存着,先把这个map冻结,防止其他的操作来对其进行修改,为了多线程安全问题
        beanFactory.freezeConfiguration();

        // Instantiate all remaining (non-lazy-init) singletons.初始化所以的懒加载的bean
        beanFactory.preInstantiateSingletons();
    }

这个方法中起决定作用的是preInstantiateSingletons()这个方法,实现是:

public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Pre-instantiating singletons in " + this);
        }
        List<String> beanNames;
//beanDefinitionMap保存了每个bean的详细的配置信息
        synchronized (this.beanDefinitionMap) {
            // Iterate over a copy to allow for init methods which in turn register new bean definitions.
            // While this may not be part of the regular factory bootstrap, it does otherwise work fine.beanDefinitionNames保存了所有的bean的id
            beanNames = new ArrayList<String>(this.beanDefinitionNames);
        }
开始循环创建,按照配置顺序进行创建的
        for (String beanName : beanNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);//获取当前要创建的bean的详细的配置信息

//判断要创建的bean的配置信息中,是否是不是抽象的,是否是单列的,是否不是懒加载的
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//判断这个bean是否是工厂bean
                if (isFactoryBean(beanName)) {
                    final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                            @Override
                            public Boolean run() {
                                return ((SmartFactoryBean<?>) factory).isEagerInit();
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
                else {
//是一个多功能方法,如果这个bean没有被创建,则这个方法就去创建bean,如果这个bean创建了,则就去获取这个bean,是在AbstractBeanFactory这个工厂类中
                    getBean(beanName);
                }
            }
        }
    }

在这个方法中实现起功能的是getBean这个方法,这个方法是在AbstractBeanFactory工厂类中,在这个工厂类中有多个这个方法的重载方法如下:

public Object getBean(String name) throws BeansException {
        return doGetBean(name, null, null, false);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return doGetBean(name, requiredType, null, false);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return doGetBean(name, null, args, false);
    }

    public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
        return doGetBean(name, requiredType, args, false);
    }

在这几个重载的方法中都调用了doGetBean方法,其实现为:

protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {

        final String beanName = transformedBeanName(name);
        Object bean;

        // Eagerly check singleton cache for manually registered singletons.获取单实例bean,紧接着如果bean为null,则开始创建,方法往下走
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            if (logger.isDebugEnabled()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                            "' that is not fully initialized yet - a consequence of a circular reference");
                }
                else {
                    logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

        else {
            // Fail if we're already creating this bean instance:
            // We're assumably within a circular reference.
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            // Check if bean definition exists in this factory.
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                String nameToLookup = originalBeanName(name);
                if (args != null) {
                    // Delegation to parent with explicit args.
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else {
                    // No args -> delegate to standard getBean method.
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
            }
没创建之前先标记一下bean已经被创建了,防止多个线程同时要创建这个bean
            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);\\[ protected void markBeanAsCreated(String beanName) {
        this.alreadyCreated.add(beanName);
    }]
            }
创建bean
            try {
//拿到bean的定义信息
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // Guarantee initialization of beans that the current bean depends on.、
//优先创建有依赖的bean
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dependsOnBean : dependsOn) {
                        if (isDependent(beanName, dependsOnBean)) {
                            throw new BeanCreationException("Circular depends-on relationship between '" +
                                    beanName + "' and '" + dependsOnBean + "'");
                        }
                        registerDependentBean(dependsOnBean, beanName);
                        getBean(dependsOnBean);
                    }
                }

                // Create bean instance.
                if (mbd.isSingleton()) {

                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            catch (BeansException ex) {
                                // Explicitly remove instance from singleton cache: It might have been put there
                                // eagerly by the creation process, to allow for circular reference resolution.
                                // Also remove any beans that received a temporary reference to the bean.
                                destroySingleton(beanName);
                                throw ex;
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

                else if (mbd.isPrototype()) {
                    // It's a prototype -> create a new instance.
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }

                else {
                    String scopeName = mbd.getScope();
                    final Scope scope = this.scopes.get(scopeName);
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
                    }
                    try {
                        Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                            @Override
                            public Object getObject() throws BeansException {
                                beforePrototypeCreation(beanName);
                                try {
                                    return createBean(beanName, mbd, args);
                                }
                                finally {
                                    afterPrototypeCreation(beanName);
                                }
                            }
                        });
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    }
                    catch (IllegalStateException ex) {
                        throw new BeanCreationException(beanName,
                                "Scope '" + scopeName + "' is not active for the current thread; " +
                                "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                ex);
                    }
                }
            }
            catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }

        // Check if required type matches the type of the actual bean instance.
        if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
            try {
                return getTypeConverter().convertIfNecessary(bean, requiredType);
            }
            catch (TypeMismatchException ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Failed to convert bean '" + name + "' to required type [" +
                            ClassUtils.getQualifiedName(requiredType) + "]", ex);
                }
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        }
        return (T) bean;
    }

接下来就是getSingleton这个方法,其实现为

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "'beanName' must not be null");
//singletonObjects IOC容器创建的所有单实例都在这个map里
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                if (this.singletonsCurrentlyInDestruction) {
                    throw new BeanCreationNotAllowedException(beanName,
                            "Singleton bean creation not allowed while the singletons of this factory are in destruction " +
                            "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
                }
                beforeSingletonCreation(beanName);
                boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet<Exception>();
                }
                try {
//创建了bean
                    singletonObject = singletonFactory.getObject();
                }
                catch (BeanCreationException ex) {
                    if (recordSuppressedExceptions) {
                        for (Exception suppressedException : this.suppressedExceptions) {
                            ex.addRelatedCause(suppressedException);
                        }
                    }
                    throw ex;
                }
                finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    afterSingletonCreation(beanName);
                }
                addSingleton(beanName, singletonObject);
            }
            return (singletonObject != NULL_OBJECT ? singletonObject : null);
        }
    }

将创建好的bean组件放到singletonObjects中

    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

总结一下,IOC容器最底层的实现就是一个Map集合
这些核心方法的底层调用顺序为:
第二节 Spring框架之二IOC容器的继续研究

  1. 继续研究起IOC容器的用法
    1) 给bean的级联属性赋值,一个类的属性是另一个类的对象,即就是这个作为属性的类的对象的属性的值
        <property name="bookName" value="红楼梦"></property>
        <property name="price" value="998"></property>
    </bean>

    <bean id="person01" class="com.fgy.bean.Person" p:lastName="王五" p:gender="女" p:salary="19999">
        <property name="myBook" ref="book01"></property>
        <!-- 会改掉外部全局的book bean的属性值,因为ref是一个严格的引用 -->
        <property name="myBook.price" value="1111"></property>
        <!-- 不想改外部的bean,可以自己在内部创建bean -->
    </bean>

2)静态工厂和实例工厂
静态工厂:调用工厂方法,获取对象。不需要创建工厂对象,即就是创建一个静态方法
实例工厂:调用工厂方法,获取对象。需要创建工厂对象

<!--class 静态工厂的全类名
        factory-method 静态工厂的工厂方法
        constructor-arg标签设置工厂方法的参数
      -->
    <bean id="myBookStaticFactory01" class="com.fgy.factory.MyBookStaticFactory" factory-method="getBook" >
        <constructor-arg value="发恼天空"></constructor-arg>

    </bean>

    <!--
        1.先把实例工厂的bean创建进来
      -->
    <bean id="myBookInstanceFactory" class="com.fgy.factory.MyBookInstanceFactory" ></bean>
    <!--2.创建book的bean,告诉bean用的是那个工厂,调用的是哪个工厂方法  -->
    <bean id="book03" class="com.fgy.bean.Book" factory-bean="myBookInstanceFactory" factory-method="getBook">
        <constructor-arg value="孙子兵法"></constructor-arg>
    </bean>

3)配置factoryBean
实现factoryBean的实现类

package com.fgy.factory;
import org.springframework.beans.factory.FactoryBean;
import com.fgy.bean.Book;
public class MyBookImplFactoryBean implements FactoryBean<Book> {
    //获取对象,工厂用来创建对象的方法
    public Book getObject() throws Exception {
        Book book = new Book();
        book.setBookName("国志");
        book.setAuthor("佚名");
        book.setPrice(99.8);        
        return book;
    }
//工厂返回创建对象的类型
    public Class<?> getObjectType() {
        return Book.class;
    }
//创建的对象是否是单例对象
    public boolean isSingleton() {
        return false;
    }
}
<!--1.创建实现了factoryBean接口的类
        1).实现了factoryBean接口的所类,都被Spring认识的工厂类
        2).这个接口就是Spring配置的
        3).Spring创建什么对象都是照这个类指定的
        4).这个工厂是一个单例的
        2.让Spring知道这样的一个工厂类,即注册
         id是用来获取创建对象的
        3.正常来说IOC容器启动时会创建所的单实例对象,但是对于实现factoryBean接口的单实例类,却不会在IOC容器启动时,创建和获取
      -->
      <bean id="myBookImplFactoryBean" class="com.fgy.factory.MyBookImplFactoryBean"></bean>

4.<!--通过继承实现bean配置信息的重用 -->
      <bean id="book04" class="com.fgy.bean.Book" p:bookName="亮剑" p:author="小海" p:price="998"></bean>       
      <bean id="book05" class="com.fgy.bean.Book" p:price="9999" parent="book04"></bean>

4)通过abstract属性创建一个模板bean,当abstract属性被赋值为true时,这个bean组件只能被继承,不能获取对象

<bean id="book04" class="com.fgy.bean.Book" p:bookName="亮剑" p:author="小海" p:price="998" abstract="true"></bean>     
      <bean id="book05" class="com.fgy.bean.Book" p:price="9999" parent="book04"></bean>

5)bean之间的依赖关系,作用:决定bean执行的顺序

bean默认的创建顺序,按照配置优先的原则如
      <bean id="dog01" class="com.fgy.bean.Dog"></bean>
      <bean id="cat01" class="com.fgy.bean.Cat"></bean>
顺序:先创建了dog,然后创建了cat
自定义创建顺序依赖于depends-on这个属性
      <bean id="dog01" class="com.fgy.bean.Dog" depends-on="cat01"></bean>
      <bean id="cat01" class="com.fgy.bean.Cat"></bean>

6)测试bean的作用域,分别创建单实例和多实例的bean
作用域:指的就是bean是否是单实例,作用域默认是单实例的,整个容器中,只有一个bean,
修改:scope属性
scope属性值:prototype:多实例:容器启动时不会创建对象,每次获取的时候,采取创建对象,我们可以在获取的时候指定值
singleton:默认的就是,单实例bean
request:在Web环境下,同一个request创建一个对象
session:在Web环境下,同一个session创建一个对象
7)创建带有生命周期方法的bean
生命周期:bean的生命周期是受IOC容器管理的,IOC容器决定了Bean的生命周期
单实例:
创建:容器启动时
初始化:指定初始化方法后,在容器启动后,就初始化了
销毁:容器销毁的时候调用,ConfigurableApplicationContext接口有close()方法
我们可以自己去定义一个方法,告诉Spring,哪些方法是用来初始化,哪些用来做销毁方法,这些方法不能有参数,bean的属性都设置好了,单实例情况下
多实例:
创建:获取的时候,创建对象
初始化:对象创建完成后,初始化
销毁:不会被调用。因为容器底层不会保存创建多实例的对象,容器关闭时只会销毁保存的对象,即容器启动时创建的单实例对象
8)bean的后置处理器
作用:就是在bean初始化前后可以进行一些预处理
实际上bean的后置处理器就是Spring的一个接口BeanPostProcessor,在这个接口中有两个方法
/**
* bean 通过构造器刚创建好的对象,也是我们要初始化的对象
* beanName bean的名字,就是在XML配置中的bean的id值
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
在初始化之前做一些操作
初始化方法
/**
* bean 通过初始化后的对象,对象的内容可能已经发生了改变
* beanName bean的名字,就是在XML配置中的bean的id值
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
在初始化之后做一些操作
9)引用外部属性文件
①这是在Spring配置文件的XML文件中直接写的,这样写的缺点就是把数据都写死了,没有体现动态性

<!--举例数据库连接池,用Spring来管理数据库连接池  -->
    <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    </bean>
测试举例:
    DataSource dataSource = (DataSource) ioc.getBean("comboPooledDataSource");

②所以我们要引用外部属性文件,体现数据的动态性
首先我们要用到context命名空间

<!--context命名空间,注册外部的properties文件,并设置访问路径,classpath是类目录下  -->
    <context:property-placeholder location="classpath:JDBCConfig.properties"/>

    <!--举例数据库连接池,用Spring来管理数据库连接池  -->
    <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${password}"></property>
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
        <property name="driverClass" value="${driverClass}"></property>

注意点:为了区分配置文件中的值和Spring自己属性的值,一般建议给配置文件中的每个属性都加一个前缀
10)基于XML的自动装配
装配:解释过来就是给属性赋值的过程
①手动装配:在容器启动时,每个组件的属性值都为null,所以要为每个属性手动的在配置文件中配置值

<bean id="bookService" class="com.fgy.service.BookService">
        <property name="bookDao" ref="bookDao"></property>
    </bean>
    <bean id="bookDao" class="com.fgy.dao.BookDao"></bean>

②自动装配:自动装配对基本数据类型无作用

autowire="default"、autowire="no":都是设置为不自动装配
autowire="byName":按照属性名作为bean的id,去容器中查找,并且赋值
        注意:bean的id是按什么确定的呢?不是按照类中的属性名,而是按照类中属性的setter方法中set后面的单词,当找到的时候,对容器组件属性赋值,否则赋值为null
    <bean id="bookService" class="com.fgy.service.BookService" autowire="byName"></bean>
    <bean id="bookDao" class="com.fgy.dao.BookDao"></bean>
autowire="byType":按照属性的类型去容器中找到这个类型对于的bean,然后赋值。如果容器中有多个这个类型的bean,则这种方法就不能进行自动装配了

    <bean id="bookService" class="com.fgy.service.BookService" autowire="byType"></bean>
    <bean id="bookDao" class="com.fgy.dao.BookDao"></bean>
autowire="constructor":按照构造器进行装配
                1.先去看构造器中参数的类型,按照类型进行装配
                2.如果这个参数类型有多个组件,参数的变量名就会作为id继续查找
                3.都找不到,则装配null

    <bean id="bookService" class="com.fgy.service.BookService" autowire="constructor"></bean>
    <bean id="bookDao" class="com.fgy.dao.BookDao"></bean>

11)SpEL

<bean id="person" class="com.atguigu.beans.Person">
    <!--[SpEL测试I]在SpEL中使用字面量  -->
    <property name="lastName" value="#{'张三'}"></property>
    <!--[SpEL测试VI]在SpEL中使用运算符 -->
    <property name="salary" value="#{9879.98*14}"></property>
    <!--[SpEL测试II]在SpEL中引用其他bean #{对象的id}获取这个对象 -->
    <property name="myBook" value="#{book01}"></property>
    <!--[SpEL测试III]在SpEL中引用其他bean的某个属性值  -->
    <property name="email" value="#{book01.author}"></property>
    <!--[SpEL测试IV]在SpEL中调用非静态方法  -->
    <property name="myMap" value="#{helloMap.getMap('key01')}"></property>
    <!--[SpEL测试V]在SpEL中调用静态方法  
    #{T(全类名).方法名(参数列表)}
    -->
    <property name="list" value="#{T(com.atguigu.beans.HelloMap).getList(3)}"></property>
  </bean>
  <bean id="helloMap" class="com.atguigu.beans.HelloMap"></bean>
  <bean id="book01" class="com.atguigu.beans.Book">
    <property name="bookName" value="西游记"></property>
    <property name="author" value="吴承恩@atguigu.com"></property>
  </bean>
相关标签: ioc