Spring IOC:Spring IOC 的具体过程

上回展现了IOC的大体实现的原型,那么在Spring框架中具体是怎么实现这个容器根据metadata元信息配置加载POJO的过程的呢?在整个Spring IOC容器的工做过程当中有不少地方是设计地至关灵活的,供给使用者不少空间去完成本身的任务,而不是一味地只是完成容器的机械过程。spring

这是整个IOC容器工做过程的过程图:安全

clipboard.png

容器启动阶段

  • 加载配置文件信息app

  • 解析配置文件信息框架

  • 装配BeanDefinitionide

  • 后处理函数

首先配置文件或者注解等元信息和JavaBean的类信息被加载到IOC容器中,容器读取到xml格式的配置文件,这个配置文件是使用者声明的依赖关系和装配中须要特别关注的地方,是装配Bean的早期“外部图纸”,容器中的解析引擎能够把咱们写入的文本形式的字符元信息解析成容器内部能够识别的BeanDefinition,能够把BeanDefinition理解成为相似反射机制的类结构,这个经过对JavaBean和配置文件进行分析获得的BeanDefinition获取了组装一个符合要求的JavaBean的基本结构,若是须要除了BeanDefinition以后还要对这个BeanDefinition再作修改的话则执行这个后处理,后处理通常是经过Spring框架内的BeanFactoryPostProcessor处理的。post

咱们仍然使用上次使用过的例子来讲明这个BeanDefinition的运做原理:有三个bean,主模块MainModule和依赖模块DependModuleA,DependModuleB,前者依赖后面两个模块构成,在配置文件里咱们通常会这么进行依赖的声明:this

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="mainModule" class="com.rocking.demo.MainModule">
        <property name="moduleA">
            <ref bean="moduleA"/>
        </property>
        <property name="moduleB">
            <ref bean="moduleB"/>
        </property>
    </bean>
    <bean id="moduleA" class="com.rocking.demo.DependModuleAImpl"></bean>
    <bean id="moduleB" class="com.rocking.demo.DependModuleBImpl"></bean>
</beans>

这是咱们的程序演示一个标准的BeanFactory容器(Spring IOC容器的实现之一)对上面配置文件的装配:spa

class MainModule {
    private DependModuleA moduleA;
    private DependModuleB moduleB;

    public DependModuleA getModuleA() {
        return moduleA;
    }

    public void setModuleA(DependModuleA moduleA) {
        this.moduleA = moduleA;
    }

    public DependModuleB getModuleB() {
        return moduleB;
    }

    public void setModuleB(DependModuleB moduleB) {
        this.moduleB = moduleB;
    }

}

interface DependModuleA {
    public void funcFromModuleA();
}

interface DependModuleB {
    public void funcFromModuleB();
}

class DependModuleAImpl implements DependModuleA {

    @Override
    public void funcFromModuleA() {
        System.out.println("This is func from Module A");
    }

}

class DependModuleBImpl implements DependModuleB {

    @Override
    public void funcFromModuleB() {
        System.out.println("This is func from Module B");
    }

}

public class SimpleIOCDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions("Beans.xml");
        MainModule mainModule = (MainModule) beanFactory.getBean("mainModule");
        mainModule.getModuleA().funcFromModuleA();
        mainModule.getModuleB().funcFromModuleB();
    }
}

这里咱们的配置文件和JavaBean被加载读取并被解析,这里的BeanDefinition生成使用过程掩藏在其中,这是实际上在IOC内部发生的大体过程:设计

public class SimpleIOCDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        AbstractBeanDefinition mainModule = new RootBeanDefinition(MainModule.class);
        AbstractBeanDefinition moduleA = new RootBeanDefinition(DependModuleAImpl.class);
        AbstractBeanDefinition moduleB = new RootBeanDefinition(DependModuleBImpl.class);
        
        beanFactory.registerBeanDefinition("mainModule", mainModule);
        beanFactory.registerBeanDefinition("moduleA", moduleA);
        beanFactory.registerBeanDefinition("moduleB", moduleB);
        
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.add("moduleA", moduleA);
        propertyValues.add("moduleB", moduleB);
        mainModule.setPropertyValues(propertyValues);
        
        MainModule module = (MainModule) beanFactory.getBean("mainModule");
        module.getModuleA().funcFromModuleA();
        module.getModuleB().funcFromModuleB();
    }
}

对xml的元信息进行加载读取后,IOC解析引擎会将其中提到的模块依据其真实类型建立成BeanDefinition,这个BeanDefinition能够当作是一种反射或者代理的过程,目的是为了让IOC容器清楚之后要建立的实例对象的bean结构,而后将这些bean结构注册到BeanFactory中去,以后将主模块的依赖以setter注入的形式加入到主模块的属性中去,(这一点要看主模块提供的是setter方法仍是初始化方法),这个过程结束后注册完全部“图纸”上规定的bean的Definition后,BeanFactory就已经成型。以后只要调用getBean方法便可将符合要求的bean生产出来,这是下一阶段的过程,咱们以后再说。

在将BeanDefinition这一“图纸”上的信息注册到BeanFactory完毕后,咱们仍然能够对已经注册完的BeanDefinition进行改动的操做,这就是咱们前面提到的Spring为使用者设计的灵活的地方之一,不是说全部的过程不可控,而是在不少地方留了不少使用者能够发挥的余地。具体的办法是使用BeanFactory处理器BeanFactoryPostProcessor来介入对BeanFactory的处理以进一步改写咱们须要修改的BeanDefinition部分。这个过程对应流程里的“后处理”过程。
以常见的处理器之一:属性占位符配置处理器为例,就是在已经构建完成已注册完毕的BeanFactory以后再对它处理,以使得BeanDefinition相应属性里的内容修改成配置处理器指定配置文件里的信息:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions( new ClassPathResource( "Beans.xml"));
PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
configurer.setLocation( new ClassPathResource( "about.properties"));
configurer.postProcessBeanFactory( beanFactory);

BeanFactoryPostProcessor将对BeanFactory处理,处理的结果就是把BeanDefinition中定义的某些属性改为BeanFactoryPostProcessor定义位置处的某些信息。

Bean 实例化阶段

有了通过处理的BeanDefinition的“内部图纸”的指导下,容器能够进一步把BeanDefifnition经过反射或CGLIB动态字节码生产的方式化为存在于内存中的活化实例对象,再将BeanDefinition规定的依赖对象经过setter注入或者初始化注入的方式装配进新建立的实例对象中,这里是实实在在地将依赖对象的引用赋给须要依赖的对象属性中。
可是这里须要注意的是建立的实例不只仅是一个简单的bean定义的实例,而是一个通过Spring包装的BeanWrapper实例,这里为何要采用BeanWrapper的方式来包装bean呢?是由于BeanWrapper提供了统一访问bean属性的接口,在建立完了基本的bean的框架后要对其中的属性进行设置,每一个bean的setter方法都不同,因此若是直接用反射设置的话会很是复杂,因此spring提供这种包装来简化属性设置:

BeanWrapper beanWrapper = new BeanWrapperImpl(Class.forName("com.rocking.demo.MainModule"));
beanWrapper.setPropertyValue( "moduleA", Class.forName("com.rocking.demo.DepModuleAImpl").newInstance());
beanWrapper.setPropertyValue( "moduleB", Class.forName("com.rocking.demo.DepModuleBImpl").newInstance());
MainModule mainModule= (MainModule) beanWrapper.getWrappedInstance();
mainModule.getModuleA().funcFromA();
mainModule.getModuleB().funcFromB();

以上的过程展现了在Spring内部,经过获取类的反射容器了解未来包装的实例bean的结构并做出包装,使用统一的属性设置方法setPropertyValue来对这个包装的实例设置属性,最后获得的bean实例经过getWrappedInstance拿到,能够发现已经成功将其属性赋值。

这个时候的bean实例其实已经彻底可使用了,可是Spring一样在实例化阶段也为咱们准备了灵活的策略以完成使用者对这个阶段的介入,和容器启动阶段的BeanFactoryPostProcessor控制BeanDefinition相似,在实例化阶段,Spring提供了BeanPostProcessor处理器来对已经装配好的实例进行操做,以完成可能须要的改动:、

这里举个例子来讲明,定义一个BeanPostProcessor的实现类,实现其中的方法postProcessAfterInitialization和postProcessBeforeInitialization来定义对在bean实例装配以后和以前分别进行的操做,在BeanFactory添加了这个处理器后就会在每次调用getBean方法装配实例的时候,都会传入根据“图纸”装配出的bean实例(包括装配过程当中建立的依赖实例bean)调用这两个方法,这些方法能够对这些bean实例实施修改。

下面是一个这样的例子(MainModule及其依赖关系和本文以前的例子相同):

class ModuleC {
    private String x;

    public String getX() {
        return x;
    }

    public void setX(String x) {
        this.x = x;
    }
    
}

class ModulePostProcessor implements BeanPostProcessor{

    @Override
    public Object postProcessAfterInitialization(Object object, String string)
            throws BeansException {
        System.out.println(string);
        if(object instanceof ModuleC){
            System.out.println(string);
            ((ModuleC)object).setX("after");
        }
        return object;
    }

    @Override
    public Object postProcessBeforeInitialization(Object object, String string)
            throws BeansException {
        if(object instanceof ModuleC){
            ((ModuleC)object).setX("before");
        }
        return object;
    }
    
}

public class VerySimpleIOCKernal {
    public static void main(String[] args) throws ClassNotFoundException, BeansException, InstantiationException, IllegalAccessException {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions(new ClassPathResource("Beans.xml"));
        ModulePostProcessor postProcessor = new ModulePostProcessor();
        beanFactory.addBeanPostProcessor(postProcessor);
        MainModule module = (MainModule) beanFactory.getBean("mainModule");
        ModuleC moduleC = (ModuleC) beanFactory.getBean("moduleC");    
        System.out.println(moduleC.getX());
    }
}

这是bean的依赖关系配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="mainModule" class="com.rocking.demo.MainModule">
        <property name="moduleA">
            <ref bean="moduleA"/>
        </property>
        <property name="moduleB">
            <ref bean="moduleB"/>
        </property>
    </bean>
    <bean id="moduleA" class="com.rocking.demo.DepModuleAImpl">
        <property name="infoA">
            <value>${moduleA.infoA}</value>
        </property>
    </bean>
    <bean id="moduleB" class="com.rocking.demo.DepModuleBImpl">
        <property name="infoB">
            <value>info of moduleB</value>
        </property>
    </bean>
    <bean id="moduleC" class="com.rocking.demo.ModuleC">
    </bean>
</beans>

从最终的结果咱们能够看出,每次调用getBean方法获得的bean实例(包括因依赖关系生成的)都将被BeanPostProcessor获取进行前置和后置处理。

除了相似上面的BeanPostProcessor的办法对装配好的bean再作处理外,Spring还能够经过配置init-method和destroy-method来对bean的初始化和销毁过程设置回调函数,这些回调函数也还能够灵活地提供更改bean实例的机会。

整个Spring IOC的过程其实整体来讲和咱们本身写的IOC原型在本质上是同样的,只不过经过复杂的设计使得IOC的过程可以更灵活有效地提供给使用者更多的发挥空间,除此以外,Spring的IOC也在安全性、容器的稳定性、metadata到bean转换的高效性上作到了精美的设计,使得IOC这一Spring容器的基础得以稳固。

相关文章
相关标签/搜索