依赖注入的概念
当一个对象要调用另一个对象时,一般是new一个被调用的对象,示例:
class A{
private B b=new B();
public void test(){
b.say();
}
}
A类的对象依赖于B类对象,如果没有B类对象,A类对象就不能正常工作,称A依赖于B。
以上方式会增加类A与类B的耦合性,不利于项目后期的升级(扩展)、维护。
在Spring中,B类的实例(被调用者),不再由A类(调用者)创建,而是由Spring容器创建,创建好以后,由Spring容器将B类的实例注入A类对象中,称为依赖注入(Dependency Injection,DI)。
原本是由A类主动创建B类对象(A类控制B类的创建),现在是Spring容器创建B类对象,注入A类对象中,A类被动接受Spring容器创建的B类实例,B类对象创建的控制权发生了反转,所以又叫做控制反转(Inversion of Control,IoC)。
依赖注入是一种优秀的解耦方式,由Spring容器负责控制类(Bean)之间的关系,降低了类之间的耦合。
因为Spring容器负责依赖注入(IoC),所以Spring容器也被称为Spring IoC容器。
依赖注入有2种实现方式:
- 设值注入
- 构造注入
设值注入
通过主调类的setter()方法注入被调类的实例。一个参数即一个依赖对象。
1、分别写2个接口
1 public interface Student { 2 public String getName(); 3 }
1 public interface Teacher { 2 public void say(); 3 }
2、分别写2个实现类
1 public class BadStudent implements Student { 2 private String name; 3 4 public BadStudent(String name){ 5 this.name=name; 6 } 7 8 @Override 9 public String getName(){ 10 return name; 11 } 12 }
1 public class MathTeacher implements Teacher{ 2 private Student student; 3 4 //主调者的setter()方法,接受被调者实例 5 public void setStudent(Student student){ 6 this.student=student; 7 } 8 9 @Override 10 public void say() { 11 System.out.println(student.getName()+",叫家长来一下。"); 12 } 13 }
类与接口耦合。
3、在applicationContext.xml文件中配置Bean
1 <!-- 初始化BadStudent类的实例lisi--> 2 <bean name="lisi" class="beans.BadStudent"> 3 <constructor-arg value="李四" /> <!--向BadStudent的构造函数传递”李四“--> 4 </bean> 5 6 <!--配置依赖--> 7 <bean name="mathTeacher" class="beans.MathTeacher"> 8 <property name="student" ref="lisi" /> <!--name指定setter方法的形参,ref或者value指定实参:某个Bean的name--> 9 </bean>
<constructor-arg>元素向该Bean的构造函数传递实参,一个<constructor-arg>传递一个实参,一个<bean>可配置多个<constructor-arg>,根据传递的实参个数来调用相应的构造函数。constructor-arg即构造器参数。
实参值通过value或ref属性来指定。value指定的是Java基础类型的参数,ref即reference(引用),指定依赖的其它Bean。
可通过属性index指定该value是第几个参数,默认从0自动递增。
<property>属性指定setter方法的实参。一个<property>指定一个参数,一个<bean>可带有多个<property>子元素。
name属性指定setter方法中的某个形参,实参由value或ref指定。
如果实参是某个Bean的实例,用ref指定,值是该Bean的name属性值。
如果实参是Java的基础类型(整型、浮点型、String等),用value指定(放在引号中),Spring容器会自动根据name的指定形参的类型,将实参转换为相应的类型进行赋值。
4、写一个测试类
1 public class Test { 2 public static void main(String[] args) { 3 ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); 4 MathTeacher mathTeacher=applicationContext.getBean("mathTeacher",MathTeacher.class); 5 mathTeacher.say(); 6 } 7 }
看到控制台已经打印出了“李四,叫家长来一下。”。
构造注入
通过构造函数注入。
1、上面的代码,将MathTeacher类修改如下
1 public class MathTeacher implements Teacher{ 2 private Student student; 3 4 //主调者的构造方法 5 public MathTeacher(Student student){ 6 this.student=student; 7 } 8 9 @Override 10 public void say() { 11 System.out.println(student.getName()+",叫家长来一下。"); 12 } 13 }
不使用setter方法注入依赖的对象,而是使用构造函数注入依赖的对象。
2、修改Spring容器的配置如下
1 <!-- 初始化BadStudent类的实例lisi--> 2 <bean name="lisi" class="beans.BadStudent"> 3 <constructor-arg value="李四" /> 4 </bean> 5 6 <!--配置依赖--> 7 <bean name="mathTeacher" class="beans.MathTeacher"> 8 <constructor-arg name="student" ref="lisi" /> 9 </bean>
3、运行,看到控制台打印出“李四,叫家长来一下。”。
说明
value是注入Java的基础类型,ref是注入依赖的Bean。当然也可以用<value>、<ref>子元素的形式配置。