IOC:也就是控制反转(创建对象实例的控制权反转),说的是一个类A要调用另一个类B,本来应该在类A里面创建B的实例的,控制权在A手里。现在用了Spring了,有了IOC,控制权就不在A手里了,而是交到Spring的IOC容器了,A要用到B,那Spring就把B分给A了。
下面通过具体的例子来说明:
package com.spring.demo.entity; /** * 一个伟大的程序员类 * */ public class Programmer { private String name; private String sex; public void coding(){ //要用到computer对象,调用computer的coding方法, //在这里new computer对象,控制权在Programmer手里 Computer computer = new Computer(); computer.coding(); } }
package com.spring.demo.entity; public class Computer { private String brand; private String color; private double size; public void coding() { System.out.println("Computer is coding!!!"); } }
上面,就是用传统方式创建依赖对象。接下来,看看Spring IOC是怎样反转控制权来创建对象的。
有三种方式:属性注入,构造器注入,自动装配。
方式一:属性注入(setter注入)
package com.spring.demo02.entity;
public class Programmer {
private String name;
private String sex;
// 在这里定义要依赖的computer属性,加上set方法
private Computer computer;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Computer getComputer() {
return computer;
}
/**
* 加上Setter方法
* */
public void setComputer(Computer computer) {
this.computer = computer;
}
}
package com.spring.demo02.entity;
public class Computer {
private String brand;
private String color;
private String size;
public void coding() {
System.out.println("Computer is coding!!!");
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
}
看上面的代码,可以发现,Programmer类里面,有3个属性,name,sex,computer,并且都有对应的getter、setter方法;Computer类里面也有三个属性,分别是品牌、颜色、尺寸,也都有对应的getter、setter方法。这只是第一步,在类里面声明属性并且实现set方法。
接下来,要写一个spring的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-3.0.xsd">
<bean id="programmer" class="com.spring.demo2.entity.Programmer">
<property name="name" value="小李"></property>
<property name="sex" value="男"></property>
<property name="computer" ref="computer"></property>
</bean>
<bean id="computer" class="com.spring.demo2.entity.Computer">
<property name="brand" value="hp"></property>
<property name="color" value="黑"></property>
<property name="size" value="14"></property>
</bean>
</beans>
解读一下这个xml文件:
1.声明一个bean,可以理解为实例化了一个对象。那这里实例化了两个对象(programmer和computer),各个属性都已经赋值上去。
2.id为programmer的bean,其实就是Programmer类;通过给property赋值,Spring就会通过Programmer类的各个属性的set方法,逐一给Programmer的属性赋值。
3.在programmer里面,有一个属性是computer的,可以看到它属性值是 ref=”computer”,这就说明computer这个属性是个引用,这里ref后面的值其实就是指向另一个bean的id值,所以这里引用的是id为computer的bean。
以上,就是属性注入了。关键的是在类里面声明属性,写set方法,然后在xml里面配置bean和property的值。
方式二、构造器注入
构造器注入,顾名思义,就是在构造器里面注入依赖对象。那是怎么实现的呢?其实跟属性注入差不多,定义一个有参构造器,然后配置xml文件就行了。看代码:
package com.spring.demo03.entity;
import com.spring.demo02.entity.Computer;
public class Programmer {
private Computer computer;
public Programmer(Computer computer){
this.computer = computer;
}
}
package com.spring.demo03.entity;
public class Computer {
private String brand;
private String color;
private String size;
public Computer(String brand, String color, String size) {
this.brand = brand;
this.color = color;
this.size = size;
}
}
上面两个类都有一个有参的构造器,接下来,在xml里面配置这两个bean,然后再配置构造器的参数值就可以了
<?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-3.0.xsd">
<bean id="programmer" class="com.spring.demo3.entity.Programmer">
<constructor-arg ref="computer"></constructor-arg>
</bean>
<!-- 构造器里面没有name字段,只有value,是根据构造器的方法参数顺序来定义的 -->
<bean id="computer" class="com.spring.demo3.entity.Computer">
<constructor-arg value="联想"></constructor-arg>
<constructor-arg value="红色"></constructor-arg>
<constructor-arg value="15.6寸"></constructor-arg>
</bean>
</beans>
方式三:自动装配
package com.spring.demo04.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Programmer {
@Autowired
Computer computer;
}
package com.spring.demo04.entity;
import org.springframework.stereotype.Component;
@Component
public class Computer {
private String brand;
private String color;
private String size;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
}
<?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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<context:component-scan base-pakage="com.spring.demo04">
</beans>
关键点:在类前面加注解:@Component,在需要注入的类里面加注解:@Autowired,这样xml里面的自动扫描就会扫描到这些加了注解的类和属性,在实例化bean的时候,Spring容器会把加了@Component的类实例化;在实际运行时,会给加了@Autowired的属性注入对应的实例。
Spring中的自动装配有哪些限制?
- 如果使用了构造器注入或者setter注入,那么将覆盖自动装配的依赖关系。
- 基本数据类型的值、字符串字面量、类字面量无法使用自动装配来注入。
- 有先考虑使用显式的装配来进行更精确的依赖注入而不是使用自动装配。
和自动装配相关的注解有哪些?
- @Required:该依赖关系必须装配(手动或自动装配),否则将抛出BeanInitializationException异常。
- @Autowired:自动装配,默认按类型进行自动装配。
- @Qualifier:如果按类型自动装配时有不止一个匹配的类型,那么可以使用该注解指定名字来消除歧义。