IoC(Inversion of Control,控制反转)是Spring框架的核心内容,关于IoC,在Spring的官网中是这样介绍的:
IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes or a mechanism such as the Service Locator pattern.
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.
对于这两段话,个人的理解是:
既然IoC容器是存放bean的地方,那就必然会有一些手段,可以将bean放入容器中。Spring中提供了以下几种bean的装配方式:
这里将主要介绍基于XML装配bean的方式。
假设现在由一个包(com.ju.pojo),在包下面有三个类,如下所示:
每个类的代码如下所示:
package com.ju.pojo;
public class Cat {
public void shout() { System.out.println("miao~"); }
}
package com.ju.pojo;
public class Dog {
public void shout() { System.out.println("wang~"); }
}
package com.ju.pojo;
public class Person {
private String name;
private Dog dog;
private Cat cat;
public Person() {
this(null, null, null);
}
public Person(String name, Dog dog, Cat cat) {
this.name = name;
this.dog = dog;
this.cat = cat;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Dog getDog() { return dog; }
public void setDog(Dog dog) { this.dog = dog; }
public Cat getCat() { return cat; }
public void setCat(Cat cat) { this.cat = cat; }
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", dog=" + dog +
", cat=" + cat +
'}';
}}
接下来创建一个测试类,用于程序的调试,在一般的程序中,测试类似乎应该是这样的:
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Person person = new Person("ju", dog, cat);
person.getDog().shout();
person.getCat().shout();
}
}
/*
* 运行程序,得到以下结果:
* wang~
* miao~
*/
在上面的代码中,对象的创建与依赖关系完全由程序自己控制,这也是大家所熟悉的传统的方式。接下来,我们尝试将控制权交给第三方。
首先,因为是通过XML装配bean,所以需要创建applicationContext.xml文件,并在里面输入以下内容(Spring官网中给出):
<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">
beans>
在导入了支持之后,就可以在xml中装配bean了(通俗的说,就是将对象放到Spring IoC容器中),可以通过
标签实现bean的装配。
<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="dog" class="com.ju.pojo.Dog"/>
<bean id="cat" class="com.ju.pojo.Cat"/>
<alias name="cat" alias="catAlias"/>
<bean id="person" class="com.ju.pojo.Person" name="personAlias">
<constructor-arg name="name" value="ju"/>
<constructor-arg name="dog" ref="dog"/>
<constructor-arg name="cat" ref="cat"/>
<property name="name" value="ju"/>
<property name="dog" ref="dog"/>
<property name="cat" ref="catAlias"/>
bean>
beans>
至此,bean的配置以及完成,所有的bean都被交由Spring容器管理,当我们需要用到bean的时候,直接从容器中获取即可,即:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
// The ApplicationContext lets you read bean definitions and access them
// 因为我们是用XML装配的bean,所有要用 ClassPathXmlApplicationContext。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取bean
Person person = context.getBean("person", Person.class);
person.getDog().shout();
person.getCat().shout();
}
}
/*
* 运行程序,得到以下结果:
* wang~
* miao~
*/
基于IoC的思想进行编程,我们的程序中不再需要创建对象,也不需要关心对象之间的依赖关系(都被交由Spring容器管理),程序模块间的耦合被大大降低。
通常,在实现一类事物之前,我们会先定义其规范,即先定义接口,这样就可以用同一个接口类型引用不同的实现类。这样做的好处是当我们需要换一种具体实现时,只需在调用时传入一个新的实现类对象即可,无需修改方法的具体实现。
而使用Spring,由于所有的bean被交由Spring容器管理,我们无需修改自己的业务代码,只需要在配置文件中(这里是applicationContext.xml)修改注入的对象即可。
在3.2中,给出了两种依赖注入的方式:值注入和引用注入。除此之外,Spring还提供了许多其它的注入方式,以支持如数组、List等类型的注入。
为了更好的说明,这里给出了一个包括大多数Java常用类型的类,用于演示不同类型的XML注入方式:
public class Student {
private String name;
private Dog dog;
private String[] books;
private List<String> hobbies;
private Map<Integer, String> cards;
private Set<String> games;
private String wife;
private Properties info;
// 以下是get、set方法以及一个toString方法,可以不用细看
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
public String[] getBooks() { return books; }
public void setBooks(String[] books) { this.books = books; }
public List<String> getHobbies() { return hobbies; }
public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; }
public Map<Integer, String> getCards() { return cards; }
public void setCards(Map<Integer, String> cards) { this.cards = cards; }
public Set<String> getGames() { return games; }
public void setGames(Set<String> games) { this.games = games; }
public String getWife() { return wife; }
public void setWife(String wife) { this.wife = wife; }
public Properties getInfo() { return info; }
public void setInfo(Properties info) { this.info = info; }
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobbies=" + hobbies +
", cards=" + cards +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
各种类型的注入方式与如下:
<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="dog" class="com.ju.pojo.Dog"/>
<bean id="student" class="com.ju.pojo.Student">
<property name="name" value="琚"/>
<property name="dog" ref="dog"/>
<property name="books">
<array>
<value>水浒传value>
<value>三国演义value>
array>
property>
<property name="hobbies">
<list>
<value>吃饭value>
<value>睡觉value>
list>
property>
<property name="cards">
<map>
<entry key="1" value="农行"/>
<entry key="2" value="交行"/>
map>
property>
<property name="games">
<set>
<value>LOLvalue>
<value>CFvalue>
set>
property>
<property name="wife">
<null/>
property>
<property name="info">
<props>
<prop key="driver">nullprop>
<prop key="url">ju.comprop>
<prop key="username">Juprop>
<prop key="password">123456prop>
props>
property>
bean>
beans>
利用
标签,可以将多个xml配置文件,合并为一个。继续以3.2中的Cat、Dog、Person为例子:
首先为Cat和Dog分别创建了xml配置文件:
<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="cat" class="com.ju.pojo.Cat"/>
beans>
<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="dog" class="com.ju.pojo.Dog"/>
beans>
接下来我们在applicationContext.xml中导入它们,就可以和原来一样装配Person对象了:
<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">
<import resource="cat.xml"/>
<import resource="dog.xml"/>
<bean id="person" class="com.ju.pojo.Person" name="personAlias">
<property name="name" value="ju"/>
<property name="dog" ref="dog"/>
<property name="cat" ref="cat"/>
bean>
beans>
通过
标签中的scope属性,设置bean的作用域,下表是Spring官网中给出的scope的属性的说明:
后面的四个属性值,只有在web开发时才会用到,这里主要对前两个进行讲解
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = context.getBean("person", Person.class);
Person person1 = context.getBean("person", Person.class);
// 单例模式下,测试是否是同一个bean
System.out.println(person == person1); // 结果为true
}
}
<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="dog" class="com.ju.pojo.Dog"/>
<bean id="cat" class="com.ju.pojo.Cat"/>
<bean id="person" class="com.ju.pojo.Person" scope="prototype">
<property name="name" value="ju"/>
<property name="dog" ref="dog"/>
<property name="cat" ref="cat"/>
bean>
beans>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = context.getBean("person", Person.class);
Person person1 = context.getBean("person", Person.class);
// 原型模式下,测试是否是同一个bean
System.out.println(person == person1); // 结果为false,即得到的不是同一个对象
}
}
官方参考文档:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html