就是要你懂Spring-IOC

为何引入IOC?

class Programer {
    Computer computer = new Mac2015();
    private void work() {
        computer.help();
    }
}
复制代码

此时有一个问题就是computer和programer耦合在一块儿,这个programer不具有扩展性(它只会用mac2015),若是此时公司换了一批电脑Mac2016,那么须要从新建立一个新的程序员类,这显然是不合理的。
从设计的角度来说,类自己就是定义的一个模板亦或是一种抽象,若是抽象和具体的实现绑定或者耦合在一块儿,那么就不是纯粹的抽象了。 这也违背了设计模式中的don't call me法则。因此这个时候要把computer和programer解耦,解耦的方法很简单。 computer的具体实现由调用方指定就ok,而不是在类内部自行指定。那么类就须要暴露一些接口供外界实现注入功能。
常见的方法有三种java

  • 构造函数注入
  • set注入
  • 接口注入

这就是IOC的基本思路,而对于咱们web开发来说通常不存在调用方(其实也有不过在容器层面),因此如何保证类中属性的动态注入,这个就要由Spring登场了
接下来,选择何种注入方式以及须要注入哪些属性是怎么通知到Spring的呢? 常见的方法有两种
程序员

  • 注解
  • 配置文件(xml/properties)

这里多提一句,bean的实例化方式能够是普通的构造器构造也能够是工厂方法构造 下面这个是经过静态工厂注入的方式web

<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
复制代码

class指向的是包含工厂方法的类,并不是工厂方法返回的类 下面这个是经过普通工厂注入的方式spring

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
复制代码

IOC容器分类

  • BeanFactorysql

    默认lazy-load,因此启动较快 经常使用的一个实现类是DefaultListableBeanFactory,该类还实现了BeanDefinitionRegistry接口,该接口担当Bean注册管理的角色 registerBeanDefinition#BeanDefinitionRegistry bean会以beanDefinition的形式注册在BeanDefinitionRegistry中 BeanFactory接口中只定义了一些关于bean的查询方法,而真正的工做须要其子类实现的其余接口定义~apache

  • ApplicationContext设计模式

    构建于BeanFactory基础之上,非lazy,启动时间较长,除了BeanFactory的基础功能还提供了一些额外的功能(事件发布、国际化信息支持等)缓存

手动与自动

  • 基于配置文件的注入 咱们称他为手动app

    <bean> <name="propertyName" ref="otherBean">ide

  • 基于注解的注入 咱们称他为全自动

    @Conponet/@Autowire/@Resource + <componet-scan>

  • 还有注解+配置的半自动

    <bean> + @autowire/@resource

关于xml中的bean

通常最顶层是beans标签 beans标签有几个独特的属性,对包含的全部bean生效

  • default-lazy-init

    全部bean是否懒加载

  • default-autowire

    全部bean内部注入的方式no(默认)/byName/byType/constrctor/autodetect
    这里byName/byType ≈ @resource/@autowire

  • default-dependency-check

    是否进行依赖检查 none(默认)

  • default-init-method

    全部bean的初始化方法好比init

  • default-destroy-method

    全部bean的销毁方法好比destroy

基本上beans的全部属性均可以对应到bean中,这里提一下bean的scope属性 关于Singleton和Prototype singleton类型的bean的存活时间和容器同样长 prototype类型的bean的生命周期不禁容器维护,容器再也不拥有当前对象的引用,任由其自生自灭

想起当初解决的一个问题,持有对象的引用(new/reflect),可是这个对象未进行注入,如何利用Spring对其进行注入?

applicationContext.getAutowireCapableBeanFactory().
     autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_NO, true);

复制代码

Bean的生命周期

先放结论,后面有具体的代码论证(图文结合效果更佳)

实践是检验真理的惟一标准,举个例子

public class Car implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean {
    private String carName;
    private BeanFactory beanFactory;
    private String beanName;

    public Car() {
        System.out.println("bean的构造函数");
    }

    public void setCarName(String carName) {
        System.out.println("属性注入");
        this.carName = carName;
    }

    // 这是BeanFactoryAware接口方法
    public void setBeanFactory(BeanFactory arg0) throws BeansException {
        System.out
                .println("BeanFactoryAware.setBeanFactory()");
        this.beanFactory = arg0;
    }

    // 这是BeanNameAware接口方法
    public void setBeanName(String arg0) {
        System.out.println("BeanNameAware.setBeanName()");
        this.beanName = arg0;
    }

    // 这是InitializingBean接口方法
    public void afterPropertiesSet() throws Exception {
        System.out
                .println("InitializingBean.afterPropertiesSet()");
    }

    // 这是DiposibleBean接口方法
    public void destroy() throws Exception {
        System.out.println("DiposibleBean.destory()");
    }

    // 经过<bean>的init-method属性指定的初始化方法
    public void myInit() {
        System.out.println("<bean>的init-method属性指定的初始化方法");
    }

    // 经过<bean>的destroy-method属性指定的初始化方法
    public void myDestory() {
        System.out.println("<bean>的destroy-method属性指定的初始化方法");
    }
}
复制代码
public class MyBeanPostProcessor implements BeanPostProcessor {

    public MyBeanPostProcessor() {
        super();
        System.out.println("BeanPostProcessor构造函数");
    }

    public Object postProcessAfterInitialization(Object arg0, String arg1) throws BeansException {
        if (arg1.equals("car")) {
            System.out
                    .println("BeanPostProcessor.postProcessAfterInitialization()");
        }
        return arg0;
    }

    public Object postProcessBeforeInitialization(Object arg0, String arg1) throws BeansException {
        if (arg1.equals("car")) {
            System.out
                    .println("BeanPostProcessor.postProcessBeforeInitialization()");
        }
        return arg0;
    }
}
复制代码
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    public MyBeanFactoryPostProcessor() {
        super();
        System.out.println("BeanFactoryPostProcessor构造函数");
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
        System.out.println("BeanFactoryPostProcessor.postProcessBeanFactory()");
        System.out.println("我如今能够修改beanDefination可是我这里就不修改了");
    }
}
复制代码
public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {

    public MyInstantiationAwareBeanPostProcessor() {
        super();
        System.out
                .println("InstantiationAwareBeanPostProcessorAdapter构造函数");
    }

    // 接口方法、实例化Bean以前调用
    @Override
    public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
        if(beanName.equals("car")) {
            System.out
                    .println("InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()");
        }
        return null;
    }

    // 接口方法、实例化Bean以后调用
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if(beanName.equals("car")) {
            System.out
                    .println("InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()");
        }
        return true;
    }
    
    // 接口方法、设置某个属性时调用
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        if(beanName.equals("car")) {
            System.out
                    .println("InstantiationAwareBeanPostProcessor.postProcessPropertyValues()");
        }
        return pvs;
    }
}
复制代码
<bean id="beanPostProcessor" class="test.MyBeanPostProcessor"/>
<bean id="instantiationAwareBeanPostProcessor" class="test.MyInstantiationAwareBeanPostProcessor"/>
<bean id="beanFactoryPostProcessor" class="test.MyBeanFactoryPostProcessor"/>
<bean id="car" class="test.Car" init-method="myInit" destroy-method="myDestory" scope="singleton" p:carName="BMW"/>
复制代码

下面来见证下整个流程

public static void main(String[] args) {
        System.out.println("开始启动容器");
        ApplicationContext factory = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
        System.out.println("容器初始化成功");
        Car car = factory.getBean("car",Car.class);
        System.out.println("开始关闭容器");
        ((ClassPathXmlApplicationContext)factory).registerShutdownHook();
    }
复制代码

最终的console输出为:

开始启动容器
BeanFactoryPostProcessor构造函数
BeanFactoryPostProcessor.postProcessBeanFactory()
我如今能够修改beanDefination可是我这里就不修改了
InstantiationAwareBeanPostProcessorAdapter构造函数
BeanPostProcessor构造函数
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
bean的构造函数
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
InstantiationAwareBeanPostProcessor.postProcessPropertyValues()
属性注入
BeanNameAware.setBeanName()
BeanFactoryAware.setBeanFactory()
BeanPostProcessor.postProcessBeforeInitialization()
InitializingBean.afterPropertiesSet()
<bean>的init-method属性指定的初始化方法
BeanPostProcessor.postProcessAfterInitialization()
容器初始化成功
开始关闭容器
DiposibleBean.destory()
<bean>的destroy-method属性指定的初始化方法
复制代码

1)容器启动阶段
容器须要依赖某些工具类(BeanDefinitionReader)对加载的Configuration MetaData进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了bean定义必 要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry,这样容器启动工做就完成了。
BeanDefinitionReader
1)用来和配置文件打交道
2)负责从对应的配置文件中读取内容并将bean映射到BeanDefinition
3)而后将BeanDefinition注册到BeanDefinitionRegistry中
eg:PropertiesBeanDefinitionReader、XmlBeanDefinitionReader

XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanDefinitionRegistry);
reader.loadBeanDefinitions("classpath:xxx");
return (BeanFactory) beanDefinitionRegistry;
复制代码

惟一能插手容器启动阶段的大佬-----BeanFactoryPostProcessor 在容器实例化对象以前,对注册到容器的BeanDefinition信息进行修改
下面是几个比较典型的BeanFactoryPostProcessor

  • PropertyPlaceholderConfigurer

在实例化bean前,将bean配置中的占位符替换为实际值

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
复制代码
# jdbc.properties
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
复制代码

在运行时,占位符内的内容会被替换为属性文件中的匹配的value,这个工做就是由PlaceholderConfiguer完成的,不难看出这个类应该实现了BeanFactoryPostProcessor

  • CustomEditorConfigurer

即容器从XML格式的文件中读取的都是字符串形式,最终应用程序倒是由各类类型的对象所构成。 要想完成这种由字符串到具体对象的转换(无论这个转换工做最终由谁来作),都须要这种转换规则 相关的信息,而CustomEditorConfigurer就是帮助咱们传达相似信息的。
以下咱们须要将String类型的2018/07/27转为date

<bean id="dateFoo" class="...DateFoo">
    <property name="date">
        <value>2018/07/27</value>
    </property>
</bean>
复制代码

用户能够自定义CustomEditorConfigurer,来指定string到特定类型转换到逻辑

  • BeanFactoryPostProcessor的实际应用例子

以前项目中使用JUNIT进行单元测试,可是每次单元测试都要加载全部的bean,项目规模若是较大,那么单元测试启动须要很长时间,为了解决这个问题开发了一个简单的BeanFactoryPostProcessor,原理很简单就是将全部的bean都设置为懒加载模式,单元测试中用到哪一个实例化哪一个

public class LazyBeanFactoryProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        DefaultListableBeanFactory fac = (DefaultListableBeanFactory) beanFactory;
        Map<String, AbstractBeanDefinition> map = (Map<String, AbstractBeanDefinition>) ReflectionTestUtils.getField(fac, "beanDefinitionMap");
        for (Map.Entry<String, AbstractBeanDefinition> entry : map.entrySet()) {
            entry.getValue().setLazyInit(true);
        }
    }
}
复制代码

2)Bean实例化阶段 1.InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(Class beanClass,String beanName) 在执行Bean的构造器以前,若是有InstantiationAwareBeanPostProcessor那么会先执行它的postProcessBeforeInstantiation()若是此时返回的bean不为null,那么不会再继续执行后续的Bean流程,只执行postProcessAfterInitialization(),形成了"短路" 有一点须要注意的在提到InstantiationAwareBeanPostProcessor的方法的时候最好指定入参,由于它有好几个相同名字入参不一样的方法,容易发生混淆

2.对bean进行实例化 容器采用策略模式来决定采用何种方式初始化bean实例(反射 or CGLIB默认) 若是配置的bean有look-up or replace此时须要使用CGLIB建立代理类

3.InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(Class beanClass,String beanName)

4.InstantiationAwareBeanPostProcessor.postProcessPropertiesValues(PropertyValues pvs,PropertyDescriptor[] pds, Object bean, String beanName)

5.对bean进行依赖注入 实例化后的产物是BeanWrapper实例,它是对bean的包裹,接下来对这个包裹设置属性值 Ps借助Wrapper能够用统一的方式对对象属性进行注入,BeanWrapper会使用PropertyEditor进行属性的类型转换

6.检查是否实现Aware接口(BeanFactory独有) BeanNameAware:将BeanName设置到当前实例中 BeanClassLoaderAware:将加载当前bean的ClassLoader设置到当前实例中 BeanFactoryAware:将BeanFactory设置到当前实例中

7.BeanPostProcessor.postProcessBeforeInitialization 不少ApplicationContext独有的Aware接口也是经过BeanPostPorcessor实现属性注入的

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher 
(this.applicationContext); }
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
   ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); }
    return bean;
}
复制代码

8.InititalizingBean.afterPropertiesSet()

9.init-method

10.BeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName)

11.容器初始化成功,正常执行业务逻辑

12.DisposableBean.destroy()

13.destroy-method

循环依赖

Spring对于单例的bean之间的循环依赖会进行处理(Set注入),换言之对于单例构造器注入以及prototype类型的bean没法处理循环依赖。假设A依赖B,B又依赖于A,在实例化A的时候会将半成品的A放到缓存中,再去实例化所依赖的B,而在实例化B过程当中又须要去实例化A,此时直接将半成品的A填充到B中,就完成了循环依赖Bean的实例化。

关于BeanPostProcessor

  • BeanPostProcessor处理容器内全部符合条件的BeanDefinition
    Ps这里的符合条件与否在覆写的方法中自行判断if(beanName==xxx)
  • A bean post-processor typically checks for callback interfaces or may wrap a bean with a proxy. Some Spring AOP infrastructure classes are implemented as bean post-processors in order to provide proxy-wrapping logic.
    AOP就是经过BeanPostProcessor实现偷梁换柱的
  • Classes that implement the BeanPostProcessor interface are special and are treated differently by the container. All BeanPostProcessors and beans that they reference directly are instantiated on startup, as part of the special startup phase of the ApplicationContext实现该接口的bean是很特殊的,它以及它所依赖的bean须要优先启动,能够将其视为容器启动的一个阶段。并且实现了该接口的bean(或者被依赖的beans)没法对其进行动态代理,由于动态代理自己就是经过BeanPostProcessor实现的,你能够理解为"大力士举不起本身"
相关文章
相关标签/搜索