什么是IoC?
IoC(Inversion of Control),又叫控制反转,它是一种编程思想。
- 对于传统的程序而言,当一个类在依赖于另一个类的时候,通常会在自身的内部主动的去创建另外一个类的实例,从而导致类与类之间的紧耦合,使得程序变得僵硬,难以维护。
- 对于IoC而言,它会引用一个IoC容器。当一个类在依赖于另一个类的时候,这个类不会主动的去创建另一个类的实例,而是被动的去接收IoC容器中装载的实例,从而实现类与类之间的松耦合,使得程序变得更加灵活,便于维护。
- 因此IoC这种思想的含义就是:通过引入IoC容器,使得原本类与类间直接的控制,反转为间接的控制,从而实现类与类之间的解耦。
它是如何做的?
对于Spring来说,它引入了一个IoC容器,这个容器中装载着使用者所装配好的类。当使用者希望得到一个类的实例的时候,可以使用IoC容器,根据装配时留下的信息(id、类型、名称......等)来进行查询,从而得到想要的类的实例(默认是单例)。
如何装配类到IoC容器中?
- 在Spring中有三种装配方式。
- 装配 = 注册 + 依赖注入,注册使得类被装载到IoC容器中,而依赖注入使得被装载到IoC容器中的类,能够被初始化。
所需要的依赖包
最基本的两个:Spring-context和Spring-core
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
</dependencies>
1.纯xml装配
- 在使用xml进行装配时,需要编写待装配的类的bean标签来进行注册。
值得注意的是,bean标签与类之间并不是一一对应关系。允许存在一个类对应多个bean标签的情况,这就使得一个类的创建具有可以拥有多种形态。IoC容器会通过使用者传入的调用信息,找到具体bean标签。再根据bean标签的信息来创建和返回它所对应的类的实例。 - 在使用xml进行装配时,采用constructor-arg标签来对有参构造函数的参数进行注入。采用property标签来对具有set方法的字段进行注入。
对于基本类型采用value进行注入;
而对于类类型采用ref进行注入;
对于数组或容器采用array、map、list......等进行注入,具体参照官网。 - 参考代码如下:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
加入这两个配置,使得p命名空间与c命名空间可以使用
它们对应了标签property与constructor-arg
使得注入更加简洁
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
Class2相当于Class的别名,两者对应的Bean实际上是一样的
在没有设置scope属性为prototype的时候,通过IoC容器返回的对象默认为单例
-->
<bean id="Class" class="com.XiaoJie.Class" name="Class2" scope="prototype"/>
<bean id="Test" class="com.XiaoJie.Test"
c:constructorArg1="constructorArg1"
p:name="XiaoJie">
<constructor-arg name="constructorArg2" value="constructorArg2"/>
<property name="number" value="123456789"/>
<property name="arrary">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
<value>5</value>
<value>6</value>
</array>
</property>
<property name="myMap">
<map>
<entry key="A" value="1"/>
<entry key="B" value="2"/>
<entry key="C" value="3"/>
<entry key="D" value="4"/>
<entry key="E" value="5"/>
<entry key="F" value="6"/>
</map>
</property>
<property name="myClass" ref="Class2"/>
<!--
<property name="myClass" ref="Class1"/>
与<property name="myClass" ref="Class2"/>效果相同
-->
</bean>
</beans>
Java
public class Class {
}
public class Test {
public int getNumber() {
return Number;
}
public void setNumber(int number) {
Number = number;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public int[] getArrary() {
return Arrary;
}
public void setArrary(int[] arrary) {
Arrary = arrary;
}
public Class getMyClass() {
return MyClass;
}
public void setMyClass(Class myClass) {
MyClass = myClass;
}
public Map getMyMap() {
return MyMap;
}
public void setMyMap(Map myMap) {
MyMap = myMap;
}
@Override
public String toString() {
return "Test{" +
"ConstructorArg1='" + ConstructorArg1 + '\'' +
", ConstructorArg2='" + ConstructorArg2 + '\'' +
", Number=" + Number +
", Name='" + Name + '\'' +
", Arrary=" + Arrays.toString(Arrary) +
", MyClass=" + MyClass +
", MyMap=" + MyMap +
'}';
}
private String ConstructorArg1;
private String ConstructorArg2;
private int Number;
private String Name;
private int []Arrary;
private Class MyClass;
private Map MyMap;
public Test(String constructorArg1 ,String constructorArg2) {
ConstructorArg1=constructorArg1;
ConstructorArg2=constructorArg2;
}
}
public static void main(String []args)
{
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans");
Test test = context.getBean("Test", Test.class);
System.out.println(test.toString());
// 运行结果:Test{ConstructorArg1='constructorArg1', ConstructorArg2='constructorArg2', Number=123456789, Name='XiaoJie', Arrary=[1, 2, 3, 4, 5, 6], MyClass=com.XiaoJie.Class@647e447, MyMap={A=1, B=2, C=3, D=4, E=5, F=6}}
}
2.注解+xml装配
- 在面对大量的依赖注入的时候,xml中会编写大量需要注入的字段,同时还需要编写所需要注入字段的set方法。这会使得xml的装配方式变得非常繁琐,因此可以采用自动装配的方式。
- 自动装配主要采用注解的方式来进行的。它只需要在所希望注入的字段上方标明对应的注解,就可以实现字段的注入,从而无需对xml和set方法进行编写。
- 参考代码如下:
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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启对应包下的@Component注解-->
<context:component-scan base-package="com.XiaoJie"/>
<!--开启@Autowired、@Value等注解-->
<context:annotation-config/>
</beans>
Java
@Component
@Scope("prototype") //等价于<bean id="class" class="com.XiaoJie.Class" scope="prototype"/>,注意id首字母对应的是小写
public class Class {
}
/*
这里省去了上面例子中的数组和容器字段
因为注解主要是为了解决简单字段(或者说细粒度字段)的注入
对于数组和容器这种较复杂的字段还是应该通过xml的方式进行注入
*/
@Component
public class Test {
private String ConstructorArg1;
private String ConstructorArg2;
@Value("123456789") //等价于<property name="number" value="123456789"/>
private int Number;
@Value("XiaoJie")
private String Name;
@Autowired //等价于<property name="myClass" ref="Class2"/>
private Class MyClass;
public Test(@Value("constructorArg1") String constructorArg1,
@Value("constructorArg2") String constructorArg2) {
ConstructorArg1=constructorArg1;
ConstructorArg2=constructorArg2;
}
@Override
public String toString() {
return "Test{" +
"ConstructorArg1='" + ConstructorArg1 + '\'' +
", ConstructorArg2='" + ConstructorArg2 + '\'' +
", Number=" + Number +
", Name='" + Name + '\'' +
", MyClass=" + MyClass +
'}';
}
}
public static void main(String []args)
{
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans");
Test test = context.getBean("test", Test.class);
System.out.println(test.toString());
//运行结果:Test{ConstructorArg1='constructorArg1', ConstructorArg2='constructorArg2', Number=123456789, Name='XiaoJie', MyClass=com.XiaoJie.Class@4c178a76}
}
3.纯注解装配(纯Java装配)
- 在使用xml的方式进行依赖注入的时候,实际上存在一个问题。这个问题就是,我们实际上注入的都是事先已经确定好了的数据。而对于事先不确定,或者说需要一段处理后才能得到的数据,xml的方式就很难去完成,而采用纯Java装配的方式就能很好的解决这个问题。
- 参考代码如下:
Java:
public class Class {
}
@Configuration
public class MyConfig {
@Bean
public Class Class()
{
return new Class();
}
@Bean
public Test Test()
{
Test result=new Test("constructorArg1","constructorArg2");
int []array={1,2,3,4,5,6};
Map map=new HashMap();
map.put('A',1);
map.put('B',2);
map.put('C',3);
map.put('D',4);
map.put('E',5);
map.put('F',6);
result.setArrary(array);
result.setMyMap(map);
return result;
}
}
public static void main(String []args)
{
ApplicationContext context=
new AnnotationConfigApplicationContext(MyConfig.class);
Test test = context.getBean("Test", Test.class);
System.out.println(test.toString());
// 运行结果:Test{ConstructorArg1='constructorArg1', ConstructorArg2='constructorArg2', Number=123456789, Name='XiaoJie', Arrary=[1, 2, 3, 4, 5, 6], MyClass=com.XiaoJie.Class@6771beb3, MyMap={A=1, B=2, C=3, D=4, E=5, F=6}}
}
三种装配方式的总结
纯xml装配:xml与Java编写十分繁琐,但更利于维护。
注解+xml装配:xml与Java编写相对轻松,维护性相对较弱。
纯注解(纯Java)装配:不需要编写xml,可实现复杂的注入方式,但不利于维护。