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 的属性值以及通过
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 名称中的一个进行匹配和连线。
本文来自博客园,作者:白芷呀,转载请注明原文链接:https://www.cnblogs.com/angelica-duhurica/p/11188085.html