目录
1、spring IoC的基本概念
1.1、什么是IoC(控制反转)
在通常的做法中,我们会在需要的时候直接用new关键字创建一个对象,此时对象的创建是由我们调用者直接控制并管理。而控制反转(ioc)则是把对象实例的创建及管理交付给第三方,对象实例的控制权不再属于调用者,当调用者需要调用某一实例时,需向第三方请求,由第三方将实例交付给调用者。
1.2、什么是Di(依赖注入)
控制反转是一个大的概念,而依赖注入(DI)则是ioc的实现方式,依赖是个名词,是指被依赖的对象,即调用者请求的对象实例,注入则是把这个对象实例(依赖)给注入到调用者所声明的变量中。
例如这个简图,在传统的做法中,当我们需要调用B类时,是通过new关键字直接创建实例,但在这种做法中我们必须自己选择B类的实现类(如果是多实现类),这种直接创建实例的方式会使A类和B类的耦合度非常高,降低A的可重用性。而在使用依赖注入时,我们是在与第三方容器打交道,通过它来获取实例,此时我们并不知道此实例来自哪里,但我们并不用关心,我们只知道这个实例能够提供我们所需的功能即可,这种做法极大的降低了依赖者(A类)和被依赖者(B类)的耦合度,更有利于程序的可扩展性。这个模式就是依赖注入,也即是控制反转。
1.3、对于依赖注入方式的一个小栗子:
1.3.1、依赖注入器
import java.util.HashMap;
import java.util.Map;
public class DependencyInjector {
private final Map<Class ,Object> singleton = new HashMap<>(); //这样实现单例是不妥的,我只是方便使用
private boolean isSingleton = true; //默认单例模式
public DependencyInjector(boolean isSingleton) {
this.isSingleton = isSingleton;
}
public Object getBean(Class clazz) throws Exception {
if (clazz == A.class)
return createA();
else if(clazz == B.class)
return createB();
else if( clazz == C.class)
return createC();
else{
throw new Exception("只支持这三种类型");
}
}
//这里应该是bean工厂,例子简单实现
private B createB(){
if (isSingleton){ //如果是单例模式
if (singleton.containsKey(B.class)){ //如果已存在实例,注意这不是线程安全的
return (B)singleton.get(B.class);
} else {
B b = new B();
b.setC(createC());
singleton.put(B.class,b);
return b;
}
} else {
B b = new B();
b.setC(createC());
return b;
}
}
private A createA(){
if (isSingleton){
if (singleton.containsKey(A.class)){
return (A)singleton.get(A.class);
} else {
A a = new A();
a.setB(createB());
singleton.put(A.class,a);
return a;
}
} else {
A a = new A();
a.setB(createB());
return a;
}
}
private C createC(){
if (isSingleton){
if (singleton.containsKey(C.class)){
return (C)singleton.get(C.class);
} else {
C c = new C();
singleton.put(B.class,c);
return c;
}
} else {
C c = new C();
return c;
}
}
}
1.3.2、三个测试对象 、粗略一看即可
public class A {
private B b;
public void setB(B b) {
this.b = b;
}
@Override
public String toString() {
return "A{这是A,依赖于B " +
"b=" + b +
'}';
}
}
public class B {
private C c;
public void setC(C c) {
this.c = c;
}
@Override
public String toString() {
return "B{这是B,依赖于C " +
"c=" + c +
'}';
}
}
public class C {
@Override
public String toString() {
return "C{这是C:没有依赖}";
}
}
1.3.3、测试类
public class Test {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
DependencyInjector di = new DependencyInjector(true);
A a = (A) di.getBean(A.class);
A a1 = (A) di.getBean(A.class);
System.out.println(a == a1);
System.out.println(a);
B b = (B) di.getBean(B.class);
System.out.println(b);
C c = (C) di.getBean(C.class);
System.out.println(c);
}
}
1.3.4、结果
是否来自同一个实例:true
A{这是A,依赖于B b=B{这是B,依赖于C c=C{这是C:没有依赖}}}
B{这是B,依赖于C c=C{这是C:没有依赖}}
C{这是C:没有依赖}
1.3.5、小结
通过依赖注入器DependencyInjector 类 我们可以不再自己创建A、B、C类,而是从依赖注入器中获取,从而降低了调用者与A、B、C类的耦合度。同时DependencyInjector(及所有依赖注入框架)的魅力在于,他所返回的对象也和依赖一同被注入了,如果某一个依赖中还有其他的依赖,那么这个依赖也会随之一起被注入
2、 Spring IoC 容器 及其 依赖注入的类型
在spring中,ioc的思想贯穿整个框架,而实现ioc的是spring ioc容器,实现的方式就是依赖注入。
在spring中ioc服务的主要有 org.springframework.beans.factory.BeanFaction和org.springframework.context.ApplicationContext 这两个接口提供的
2.1 BeanFactory 容器
这是一个最简单的容器,它主要的功能是为依赖注入 (DI) 提供支持,这个容器接口在 org.springframework.beans.factory.BeanFactory 中被定义。
在 Spring 中,有大量对 BeanFactory 接口的实现。其中,最常被使用的是 XmlBeanFactory 类。这个容器从一个 XML 文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。
在资源宝贵的移动设备或者基于 applet 的应用当中, BeanFactory 会被优先选择。否则,一般使用的是 ApplicationContext,除非你有更好的理由选择 BeanFactory。
2.2 ApplicationContext 容器
Application Context 是 BeanFactory 的子接口,也被成为 Spring 上下文。 Application Context 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。
ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationContext 会更加优秀。
最常被使用的 ApplicationContext 接口实现:
• FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。
• ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
• WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。
2.3 依赖注入的类型
依赖注入的类型有:setter注入,集合注入,构造注入,接口注入
2.4、栗子:bean容器的简单使用及各种注入方式的示例
2.4.1、 xml配置
<?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="person" class="ioc.testBeanFactory.Person">
<property name="x" value="1"></property> <!-- setter 注入 -->
<property name="list" > <!-- 集合注入 -->
<list>
<value>1</value>
<value>1</value>
</list>
</property>
<property name="map">
<map>
<entry key="ioc" value="nice"></entry>
</map>
</property>
<property name="set">
<set>
<value>ssd</value>
<value>kjkj</value>
</set>
</property>
<!-- 注意这个是引用 -->
<property name="cat" ref="catId"></property>
</bean>
<!-- -->
<bean name="catId" class="ioc.testBeanFactory.Cat">
<!-- 构造注入 index为构造构造方法参数的索引 注意,bean默认注入无参构造方法,若类中无提供无参构造方法,则必须注入有参构造方法 -->
<constructor-arg index="0" value="2"/>
</bean>
</beans>
2.4.2、 测试类及结果
package ioc.testBeanFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
//这种方式并不常用
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource("D:\\program\\IdeaProjects\\dongruan\\springIoc\\src\\application.xml"));
Person person = (Person) beanFactory.getBean("person");
person.print();
ApplicationContext context;
context = new FileSystemXmlApplicationContext("D:\\program\\IdeaProjects\\dongruan\\springIoc\\src\\application.xml");
person = (Person) context.getBean("person");
person.print();
// context = new ClassPathXmlApplicationContext("file:D:\\program\\IdeaProjects\\dongruan\\springIoc\\src\\application.xml");
// context = new ClassPathXmlApplicationContext("classpath:application.xml");
context = new ClassPathXmlApplicationContext("application.xml");
person = (Person) context.getBean("person");
person.print();
}
}
//----- 结果
spring ioc test
Person{x=1, list=[1, 1], set=[ssd, kjkj], map={ioc=nice}, cat=Cat{cat=2}}
spring ioc test
Person{x=1, list=[1, 1], set=[ssd, kjkj], map={ioc=nice}, cat=Cat{cat=2}}
spring ioc test
Person{x=1, list=[1, 1], set=[ssd, kjkj], map={ioc=nice}, cat=Cat{cat=2}}
2.4.3、 简单类
package ioc.testBeanFactory;
public class Cat {
private int cat;
public Cat(int cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Cat{" +
"cat=" + cat +
'}';
}
}
package ioc.testBeanFactory;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Person {
private int x;
private List<Integer> list;
private Set<String> set;
private Map map;
private Cat cat;
void print(){
System.out.println("spring ioc test");
System.out.println(this.toString());
}
//-------省略setter方法-----------
@Override
public String toString() {
return "Person{" +
"x=" + x +
", list=" + list +
", set=" + set +
", map=" + map +
", cat=" + cat +
'}';
}
}
2.4.4、小结
采用绝对路径的加载方式将导致程序的灵活性变差,一般不推荐使用。因此, 通常在Spring 的Java 应用中采取通过ClassPathXmlApplicationContext 类来实例化ApplicationContext 容器的方式,而在Web 应用中, ApplicationContext 容器的实例化工作将交给Web 服务器完成,使用WebXmlApplicationContext类。
在使用各种注入方式时;使用setter注入需要实现类提供setter方法,但此实现类必须提供无参构造方法,当实现类没有提供无参构造方法,则必须使用构造注入方式,使用构造注入时,最好添加索引,即使用index来显示此依赖是注入到构造方法的第几个参数上。第一个参数的index为0。
3、参考资料
https://blog.csdn.net/bestone0213/article/details/47424255
https://www.w3cschool.cn/wkspring/yqdx1mm5.html
《servlet和jsp学习指南》.((加)Budi Kurniawan)