spring(四):IoC

IoC—Inversion of Control,即控制反转

IoC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

理解IoC的关键:“谁控制谁,控制什么,为何是反转,哪些方面反转了”:

谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由IoC容器来控制对象的创建;

谁控制谁?IoC 容器控制对象;

控制什么?控制外部资源获取。

为何是反转,哪些方面反转了:传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象;而IoC则是由容器来帮忙创建及注入依赖对象;

为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;

哪些方面反转了?依赖对象的获取被反转了。

IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”

DI—Dependency Injection,即“依赖注入”

是组件之间依赖关系由容器在运行期决定,即由容器动态的将某个依赖关系注入到组件之中。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”:

谁依赖于谁:应用程序依赖于IoC容器;Bean依赖IoC容器;依赖指的是Bean之间的依赖关系

为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;Bean需要IoC容器来创建

谁注入谁:IoC容器注入应用程序某个对象,应用程序依赖的对象;

●注入了什么:注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

Spring IoC容器如何知道哪些是它管理的对象呢?

Spring IoC容器通过读取配置文件中的配置元数据(BeanDefinition),通过元数据对应用中的各个对象进行实例化及装配。

Spring与配置文件完全解耦,可以使用任何可能的方式进行配置元数据,比如注解、基于xml的、基于java文件的、基于属性文件的配置。

由IoC容器管理的那些组成应用程序的对象我们就叫它Bean。

在Spring中BeanFactory是IoC容器的实际代表者。ApplicationContext 接口继承了BeanFactory。简单说, BeanFactory提供了IoC容器最基本功能,而 ApplicationContext 则增加了更多支持企业级功能支持。

public static void main(String[] args) {
    // 1、读取配置文件实例化一个IoC容器
    ApplicationContext context = new ClassPathXmlApplicationContext("hello.xml");
    // 2、从容器中获取Bean,注意此处完全“面向接口编程,而不是面向实现”
    HelloApi helloApi = context.getBean("hello", HelloApi.class);
    // 3、执行业务逻辑
    helloApi.sayHello();
 }

Bean?

本质就是一个POJO类,但具有以下限制:

  • 该类必须要有公共的无参构造器
  • 属性为private访问级别,不建议public,如private String message
  • 属性必要时通过一组setter和getter方法来访问

作用域

Spring 框架支持以下五个作用域,分别为singleton、prototype、request、session和global session。

作用域 描述
singleton Spring会缓存单例对象,在IoC容器仅存在一个Bean实例,默认值
prototype Spring不会缓存原型对象,每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()
request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境
global-session 一般用于Portlet应用环境,该运用域仅适用于WebApplicationContext环境
<bean id="..." class="..." scope="singleton"></bean>

自定义作用域:org.springframework.beans.factory.config.Scope

生命周期

Bean的生命周期可以表达为:Bean的定义——Bean的初始化——Bean的使用——Bean的销毁

初始化回调

一、org.springframework.beans.factory.InitializingBean 接口指定一个单一的方法:

void afterPropertiesSet() throws Exception;

二、配置元数据,使用 init-method 属性来指定带有 void 无参数方法的名称:

<bean id="exampleBean" class="examples.ExampleBean" init-method="init"/>

销毁回调

一、org.springframework.beans.factory.DisposableBean 接口指定一个单一的方法:

void destroy() throws Exception;

二、配置元数据,你使用 destroy-method 属性来指定带有 void 无参数方法的名称:

<bean id="exampleBean" class="examples.ExampleBean" destroy-method="destroy"/>

后置处理器

允许在调用初始化方法前后对 Bean 进行额外的处理。

是一个监听器,它可以监听容器触发的事件。将它向IoC容器注册后,容器中管理的Bean具备了接收IoC容器事件回调的能力。

在populateBean之后的initializeBean中被调用。在Bean的初始化之前提供postProcessBeforeInitialization回调入口。在Bean的初始化之后提供postProcessAfterInitialization回调入口。

public class InitBean implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeforeInitialization : " + beanName);
        return bean;  // you can return any other object as well
    }
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("AfterInitialization : " + beanName);
        return bean;  // you can return any other object as well
    }
}

Bean自身的方法:如调用 Bean 构造函数实例化 Bean,调用 Setter 设置 Bean 的属性值以及通过的 init-method 和 destroy-method 所指定的方法;

Bean级生命周期接口方法:如 BeanNameAware、 BeanFactoryAware、 InitializingBean 和 DisposableBean,这些接口方法由 Bean 类直接实现;

容器级生命周期接口方法:在上图中带“★” 的步骤是由 InstantiationAwareBean PostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“ 后处理器” 。 后处理器接口一般不由 Bean 本身实现,它们独立于 Bean,实现类以容器附加装置的形式注册到 Spring 容器中并通过接口反射为 Spring 容器预先识别。当Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用,所以这些后处理器的影响是全局性的。当然,用户可以通过合理地编写后处理器,让其仅对感兴趣Bean 进行加工处理

Spring IoC容器如何实例化Bean呢?

IoC容器需要根据Bean定义里的配置元数据使用反射机制来创建Bean。

一、使用构造器实例化Bean

指定必须的class属性

<bean id="hello" class="hello.HelloApiImpl"></bean>

二、使用静态工厂方式实例化Bean

指定必须的class属性

指定factory-method属性来指定实例化Bean的方法

<bean id="hello" class="hello.HelloApiStaticFactory" factory-method="newInstance"></bean>

三、使用实例工厂方法实例化Bean

不能指定class属性

使用factory-bean属性指定工厂Bean

使用factory-method属性指定实例化Bean的方法

<!—定义实例工厂Bean -->  
<bean id="beanInstanceFactory" class="hello.HelloApiInstanceFactory"/>  
<!—使用实例工厂Bean创建Bean -->  
<bean id="hello" factory-bean="beanInstanceFactory" factory-method="newInstance"></bean>

Spring IoC容器如何注入Bean的依赖资源?

在基于构造函数注入中,我们使用的是〈bean〉标签中的〈constructor-arg〉元素;

而在基于设值函数的注入中,我们使用的是〈bean〉标签中的〈property〉元素。

构造器注入:

实例化Bean时,通过在Bean定义中指定构造器参数进行注入依赖,包括实例工厂方法参数注入依赖,但静态工厂方法参数不允许注入依赖;

  • 根据参数索引注入
<constructor-arg index="0" value="Hello World!"/>

构造函数的参数在 bean 定义中的顺序就是把这些参数提供给适当的构造函数的顺序。

最好的传递构造函数参数的方式,是使用 index 属性来显式的指定构造函数参数的索引。

  • 根据参数类型注入
<constructor-arg type="java.lang.String" value="Hello World!"/>
  • 根据参数名注入:在构造器上使用@ConstructorProperties注解来指定参数名。只对构造器实例化Bean方式起作用,而对工厂不起作用。
<constructor-arg name="message" value="Hello World!"/>

setter注入

实例化Bean后,通过调用Bean类的setter方法进行注入依赖;

  • 注入常量:
<property name="message" value="Hello World!"/>
<property name="message"><value>Hello World!</value></property>
  • 注入Bean ID:
<property name="id"><idref bean="bean1"/></property>
  • 注入集合(Collection、Set、List):
<property name="values">
    <list value-type="可选" merge="可选">
        <value>1</value>
        <value>2</value>
        <value>3</value>
    </list>
</property>  
<property name="values"><set value-type="同上">...</set></property>
  • 注入数组:

一维:

<property name="id">
    <array>...</array>
</property>  

二维:

<property name="id">
    <array>
        <array>...</array>
        <array>...</array>
    </array>
</property>  
  • 注入字典:
<property name="id">
    <map>
        <entry key="" value=""/>
        <entry key="" value=""/>
    </map>
</property>  
  • 注入Properties:
<property name="id">
    <props value-type="无用,就是String">
        <prop key="">value</prop>
        <prop key="">value</prop>
    </map>
</property>  
  • 注入依赖Bean:

如果你想要向一个对象传递一个引用,你需要使用标签的 ref 属性,如果你想要直接传递值,那么你应该使用如上所示的 value 属性。

一般:

<constructor-arg index="0" ref="bean1"/>
<property name="helloApi" ref="bean1"/>

高级:

<ref local=""/>
<ref parent=""/>
<!-- sources/chapter3/parentBeanInject.xml表示父容器配置-->
<!--注意此处可能子容器也定义一个该Bean-->
<bean id="helloApi" class="cn.HelloImpl4">  
    <property name="message" value="Hello Parent!"/>
</bean>

<!-- sources/chapter3/localBeanInject.xml表示当前容器配置-->
<!-- 注意父容器中也定义了id 为 helloApi的Bean -->
<bean id="helloApi" class="cn.HelloImpl4">
    <property name="message" value="Hello Local!"/>  
</bean>  
<!-- 通过local注入 -->
<bean id="bean1" class="cn.bean.HelloApiDecorator">
    <constructor-arg index="0"><ref local="helloApi"/></constructor-arg>
</bean>
<!-- 通过parent注入 -->
<bean id="bean2" class="cn.bean.HelloApiDecorator">
    <property name="helloApi"><ref parent="helloApi"/></property>
</bean>
  • 注入内部Bean:
<bean id="bean" class="cn.bean.HelloApiDecorator">
    <property name="helloApi">
        <bean id="即便指定也没有用,是匿名的,对外部不可见" class="cn.hello.HelloImpl"/>
    </property>  
</bean>
  • 处理null值:
<property name=""><null/></property>

Spring Bean自动装配?(不建议用

在 XML 配置文件中 beans 的 auto-wire 属性设置为 byName。然后,它尝试将它的属性与配置文件中定义为相同名称的 beans 进行匹配和连接。

在 XML 配置文件中 beans 的 autowire 属性设置为 byType。然后,如果它的 type 恰好与配置文件中 beans 名称中的一个相匹配,它将尝试匹配和连接它的属性。

在 XML 配置文件中 beans 的 autowire 属性设置为 constructor。然后,它尝试把它的构造函数的参数与配置文件中 beans 名称中的一个进行匹配和连线。

posted @ 2019-07-14 21:52  白芷呀  阅读(300)  评论(0编辑  收藏  举报