Spring与IoC
Spring与IoC
一、IoC概述
控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,其实现方式多种多样。当前比较流行的实现方式有两种:依赖注入和依赖查找。依赖注入方式应用更为广泛。
依赖查找:
Dependency Lookup,DL,容器提供回调接口和上下文环境给组件,程序代码则需要提供具体的查找方式。比较典型的是依赖于 JNDI 服务接口(Java Naming and
Directory Interface)的查找。依赖注入:
Dependency Injection,DI,程序代码不做定位查询,这些工作由容器自行完成。依赖注入 DI 是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。
Spring 的依赖注入对调用者与被调用者几乎没有任何要求,完全支持 POJO 之间依赖关系的管理。依赖注入是目前最优秀的解耦方式。依赖注入让 Spring 的 Bean 之间以配置文件的方式
组织在一起,而不是以硬编码的方式耦合在一起的。
二、Spring程序举例
- 定义接口与实体类
public interface IStudentService{
void some();
}
public class SatudentServiceImpl implements IStudentService{
public void some(){
System.out.println("执行some()方法");
}
}
- 创建Spring配置文件
bean标签 :用于定义一个实例对象。一个实例对应一个 bean 元素。
id:该属性是 Bean 实例的唯一标识,程序通过 id 属性访问 Bean,Bean 与 Bean 间的依赖关系也是通过 id 属性关联的。
class:指定该 Bean 所属的类,注意这里只能是类,不能是接口。 - 定义测试类
@Test
public void test03(){
//获取容器
ApplicationContext context=
new ClassPathXmlApplicationContext("application.xml");
//从容器获取对象
IStudentService Service=(IStudentService)context.getBean("studentservice");
service.some();
}
(1)ApplicationContext接口容器
ApplicationContext 用于加载 Spring 的配置文件,在程序中充当“容器”的角色。其实现类有两个。通过 Ctrl +T 查看:
A、配置文件在类路径下
- 若Spring配置文件存放在项目的类路径下,则使用 ClassPathXmlApplicationContext 实现类进行加载。
ApplicationContext context=
new ClassPathXmlApplicationContext("application.xml");
B、配置文件在本地目录中
- 若 Spring 配置文件存放在本地磁盘目录中,则使用 FileSystemXmlApplicationContext 实现类进行加载。
ApplicationContext context=
new FileSystemApplicationContext("d:/application.xml");
C、配置文件在项目根路径下
- 若 Spring 配置文件存放在项目的根路径下,同样使用 FileSystemXmlApplicationContext实现类进行加载。存放在项目根路径下的情况,该配置文件与 src 目录同级,而非在 src 中。
ApplicationContext context=
new FileSystemApplicationContext("application.xml");
(2)BeanFactory接口容器
- BeanFactory 接口对象也可作为 Spring 容器出现。BeanFactory 接口是 ApplicationContext
接口的父类 - 若要创建 BeanFactory 容器,需要使用其实现XmlBeanFactory(Ctrl+T 查看继承关系)。该类可以加载 Spring 配置文件。
- 而 Spring 配置文件以资源 Resouce 的形式出现在 XmlBeanFactory 类的构造器参数中。Resouce 是一个接口,其具有两个实现类:ClassPathResource:指定类路径下的资源文件、FileSystemResource:指定项目根路径或本地磁盘路径下的资源文件。
- 在创建了 BeanFactory 容器后,便可使用其重载的 getBean()方法,从容器中获取指定的Bean 对象
(3)两个接口容器的区别
- 虽然这两个接口容器所要加载的 Spring 配置文件是同一个文件,但在代码中的这两个容器对象却不是同一个对象,即不是同一个容器:它们对于容器内对象的装配(创建)时机是不同的。
- ApplicationContext 容器中对象的装配时机:
ApplicationContext 容器,会在容器对象初始化时,将其中的所有对象一次性全部装配好。以后代码中若要使用到这些对象,只需从内存中直接获取即可。执行效率较高。但占用内存。 - BeanFactory 容器中对象的装配时机:
BeanFactory 容器,对容器中对象的装配与加载采用延迟加载策略,即在第一次调用getBean()时,才真正装配该对象。
三、Bear的装配
- Bean 的装配,即 Bean 对象的创建。容器根据代码要求创建 Bean 对象后再传递给代码的过程,称为 Bean 的装配。
(1)默认装配方式
- 代码通过 getBean()方式从容器获取指定的 Bean 实例,容器首先会调用 Bean 类的无参构造器,创建空值的实例对象。
(2)动态工厂 Bean
- 有些时候,项目中需要通过工厂类来创建 Bean 实例,而不能像前面例子中似的,直接由 Spring 容器来装配 Bean 实例。使用工厂模式创建 Bean 实例,就会使工厂类与要创建的Bean 类耦合到一起。
(3)静态工厂 Bean
- 使用工厂模式中的静态工厂来创建实例 Bean。此时需要注意,静态工厂无需工厂实例,所以不再需要定义静态工厂bean。而对于工厂所要创建的 Bean,其不是由自己的类创建的,所以无需指定自己的类。但其是由工厂类创建的,所以需要指定所用工厂类。故 class 属性指定的是工厂类而非自己的类。当然,还需要通过 factory-method 属性指定工厂方法。
(4)容器中Bear的作用域
-
当通过 Spring 容器创建一个 Bean 实例时,不仅可以完成 Bean 的实例化,还可以通过scope 属性,为 Bean 指定特定的作用域。Spring 支持 5 种作用域。
- singleton:单态模式。即在整个 Spring 容器中,使用 singleton 定义的 Bean 将是单例的,只有一个实例。默认为单态的。
- prototype:原型模式。即每次使用 getBean 方法获取的同一个bean 的实例都是一个新的实例。
- request:对于每次 HTTP 请求,都将会产生一个不同的 Bean 实例。
- session:对于每个不同的 HTTP session,都将产生一个不同的 Bean 实例。
注意:
(1)对于 scope 的值 request、session 与 global session,只有在 Web 应用中使用 Spring 时,该作用域才有效。
(2)对于 scope 为 singleton 的单例模式,该 Bean 是在容器被创建时即被装配好了。
(3)对于 scope 为 prototype 的原型模式,Bean 实例是在代码中使用该 Bean 实例时才进行装配的。
(5)bean后处理器
- Bean 后处理器是一种特殊的 Bean,容器中所有的 Bean 在初始化时,均会自动执行该类的两个方法。由于该 Bean 是由其它 Bean 自动调用执行,不是程序员手工调用,故此 Bean无须 id 属性。
- 需要做的是,在 Bean 后处理器类方法中,只要对 Bean 类与 Bean 类中的方法进行判断,就可实现对指定的 Bean 的指定方法进行功能扩展与增强。方法返回的 Bean 对象,即是增过的对象。
- 代码中需要自定义 Bean 后处理器类。该类就是实现了接口 BeanPostProcessor 的类。该接口中包含两个方法,分别在目标 Bean 初始化完毕之前与之后执行。它们的返回值为:功能被扩展或增强后的 Bean 对象。
- Bean 初始化完毕有一个标志:一个方法将被执行。即当该方法被执行时,表示该 Bean被初始化完毕。所以 Bean 后处理器中两个方法的执行,是在这个方法之前之后执行。
public Object postProcessBeforeInitialization(Object bean, String beanId)
throws BeansException
该方法会在目标 Bean 初始化完毕之前由容器自动调用。
public Object postProcessAfterInitialization(Object bean, String beanId) throws BeansException
该方法会在目标 Bean 初始化完毕之后由容器自动调用。
- 它们的参数是:第一个参数是系统即将初始化的 Bean 实例,第二个参数是该 Bean 实例的 id 属性值。若 Bean 没有 id 就是 name 属性值。
(6)定制Bean的生命始末
- 可以为 Bean 定制初始化后的生命行为,也可以为 Bean 定制销毁前的生命行为。首先,这些方法需要在 Bean 类中事先定义好:是方法名随意的 public void 方法。
- 其次,在配置文件的标签中增加如下属性:
init-method:指定初始化方法的方法名
destroy-method:指定销毁方法的方法名
注意,若要看到 Bean 的 destroy-method 的执行结果,需要满足两个条件:
(1)Bean 为 singleton,即单例
(2)要确保容器关闭。接口 ApplicationContext 没有 close()方法,但其实现类有。所以,可以将 ApplicationContext 强转为其实现类对象,或直接创建的就是实现类对象。
(7)Bean的生命周期
Step1:调用无参构造器,创建实例对象。
Step2:调用参数的 setter,为属性注入值。
Step3:若 Bean 实现了 BeanNameAware 接口,则会执行接口方法 setBeanName(String beanId),使 Bean 类可以获取其在容器中的 id 名称。
Step4:若 Bean 实现了 BeanFactoryAware 接口,则执行接口方法 setBeanFactory(BeanFactoryfactory),使 Bean 类可以获取到 BeanFactory 对象。
Step5 : 若 定 义 并 注 册 了 Bean 后 处 理 器 BeanPostProcessor , 则 执 行 接 口 方 法postProcessBeforeInitialization()。
Step6:若 Bean 实现了 InitializingBean 接口,则执行接口方法 afterPropertiesSet ()。该方法在 Bean 的所有属性的 set 方法执行完毕后执行,是 Bean 初始化结束的标志,即 Bean 实例化结束。
Step7:若设置了 init-method 方法,则执行。
Step8 : 若 定 义 并 注 册 了 Bean 后 处 理 器 BeanPostProcessor , 则 执 行 接 口 方 法postProcessAfterInitialization()。
Step9:执行业务方法。
Step10:若 Bean 实现了 DisposableBean 接口,则执行接口方法 destroy()。
Step11:若设置了 destroy-method 方法,则执行。
四、基于XML的DI
(1)注入分类
- Bean 实例在调用无参构造器创建了空值对象后,就要对 Bean 对象的属性进行初始化。初始化是由容器自动完成的,称为注入。根据注入方式的不同,常用的有两类:设值注入、构造注入。还有另外一种,实现特定接口注入。由于这种方式采用侵入式编程,污染了代码,所以几乎不用。
1,设值注入
- 设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入中大量使用。
- 当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系。ref的值必须为某 bean 的 id 值。
- 对于其它 Bean 对象的引用,除了标签的 ref 属性外,还可以使用ref标签。
1,构造注入
- 构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即,使用构造器设置依赖关系。
constructor-arg 标签中用于指定参数的属性有:
name:指定参数名称。
index:指明该参数对应着构造器的第几个参数,从 0 开始。不过,该属性不要也行,但要注意,若参数类型相同,或之间有包含关系,则需要保证赋值顺序要与构造器中的参数顺序一致。
另外,type 属性可用于指定其类型。基本类型直接写类型关键字即可,非基本类型需要写全限定性类名。
(2)集合属性注入
* 集合:
public class MyController{
private String [] strs;
private List<Student> students;
private Set<String> myset;
private Map<String,Integer> myMap;
private Properties myPro;
}
1,为数组注入值
2,为 List 注入值
3,为 Set 注入值
4,为 Map 注入值
5,为 Properties 注入值
(3)对于域属性的自动注入
-
对于域属性的注入,也可不在配置文件中显示的注入。可以通过为bean标签设置autowire 属性值,为域属性进行隐式自动注入。根据自动注入判断标准的不同,可以分为两种:
byName:根据名称自动注入
byType:根据类型自动注入1,byName 方式自动注入:当配置文件中被调用者 Bean 的 id 值与代码中调用者 Bean 类的属性名相同时,可使用byName 方式,让容器自动将被调用者 Bean 注入给调用者 Bean。容器是通过调用者的 Bean类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。
2,byType 方式自动注入:使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用者 Bean 类的某域属性类型同源。即要么相同,要么有 is-a 关系(子类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。
(4)为应用指定多个 Spring 配置文件
- 将配置文件分解为地位平等的多个配置文件,并将所有配置文件的路径定义为一个String 数组,将其作为容器初始化参数出现。其将与可变参的容器构造器匹配。
1,平等关系的配置文件
* 将配置文件分解为地位平等的多个配置文件,并将所有配置文件的路径定义为一个String 数组,将其作为容器初始化参数出现。其将与可变参的容器构造器匹配。
2,包含关系的配置文件
* 各配置文件中有一个总文件,总配置文件将各其它子文件通过引入。在 Java代码中只需要使用总配置文件对容器进行初始化即可。
五、基于注解的DI
https://blog.csdn.net/king_cannon_fodder/article/details/80170665
上一篇: Ioc容器如何工作。