DI简介
DI-Dependency Injection 即“依赖注入”,组件之间的依赖关系由容器在运行时决定,即由容器动态的为某个依赖冠以注入到组件中
关键点:谁依赖谁?为什么需要依赖?谁注入谁?注入了什么?
- 容器:IOC 组件(某一个特定的类:例如:UserService)
- 资源(组件依赖的内容:例如:mailService)
————————————————————————————
- 谁依赖于谁:应用程序依赖于IOC容器
- 为什么需要依赖:应用程序需要IOC容器提供组件需要的外部资源
- 谁注入谁:IOC容器注入应用程序需要的资源,组件依赖的资源
- 注入了什么:注入了某个对象所需要的外部资源(包括对象,常量数据,资源)
依赖注入的方式:
- 基于配置的形式注入依赖
- 基于注解的形式注入依赖
DI的注入方式
1、基于XML配置文件的注入
(1)基于有参构造函数注入依赖
给定对象User,并给定其有参构造函数:
public class User12 {
private Integer id;
private String name;
private String passwd;
private String address;
// 有参构造函数
public User12(Integer id, String name, String passwd, String address) {
this.id = id;
this.name = name;
this.passwd = passwd;
this.address = address;
}
}
配置中通过构造函数来注入依赖如下:
<!--基于XML形式的依赖注入:有参构造函数-->
<bean id="user" class="com.tulun.bean.User12">
<!--注入属性-->
<constructor-arg name="id" value="1"/>
<constructor-arg name="name" value="bob"/>
<constructor-arg name="passwd" value="passwd"/>
<constructor-arg name="address" value="xi'an"/>
</bean>
(2)基于set方法注入依赖
对应的user类提供set方法:
public class User12 {
private Integer id;
private String name;
private String passwd;
private String address;
public User12() {
}
public void setName(String name) {
this.name = name;
}
在配置文件中用到了property标签:
<!--基于XML形式的依赖注入:set方法-->
<bean id="user1" class="com.tulun.bean.User12">
<property name="name" value="luky"/>
<property name="person" ref="person"/>
</bean>
通过set方法注入依赖使用 < property >
标签,name
属性对应的是类中属性名 value
对应是赋予的值(只针对基本类型,作为String) 自定类型的注入使用的是ref
标签
注入数据处理基本类型,自定义类型,Spring中还支持List、map、set、array等类型的数据注入
例如:
<!--注入list类型-->
<property name="ls">
<list>
<value>12</value>
<value>13</value>
</list>
</property>
<!--注入map类型数据-->
<property name="ms">
<map>
<entry key=" " value=""/>
<entry key=" " value=""/>
</map>
</property>
2、基于注解的注入
@Value
:注入普通形式的依赖;@Resource
:注入对象类型;@Autowired
:注入对象类型;
使用注解方式注入依赖前提是Bean的装配支持注解扫描
<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/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解扫描: base-package(必填)指令包路径: 会扫描类方法、属性上是否有注解-->
<context:component-scan base-package="com.tulun.bean"></context:component-scan>
<!--扫描属性上面的注解(不建议使用)-->
<context:annotation-config></context:annotation-config>
</beans>
@Value注解
@Component(value = "user")
public class User {
@Value("1")
private String id;
@Value("张三")
private String name;
}
public class App{
public static void main( String[] args ){
//获取IOC的容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过容器来获取当前的对象
User user = (User) applicationContext.getBean("user");
System.out.println(user);
}
}
@Autowired注解
@Component(value = "person")
public class Person {
//自定义类型属性
//和@Resource功能一样
@Autowired
private User user;
}
public class App{
public static void main( String[] args ){
//获取IOC的容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过容器来获取当前的对象
Person person = (Person)applicationContext.getBean("person");
System.out.println(person);
}
}
高频问点:@Resource和@Autowired的区别?
@Resource
和@Autowired
都是用来做bean的注入时使用的,@Resource
和@Autowired
在有时是可以互相替换换使用的,但是有的时候是不可以的?
- 共同点:
@Resource
和@Autowired
都作为bean的注入使用的,在接口仅有一个实现类时,两个注解的修饰效果相同,可以相互替换 - 不同点:
@Resource
是Java自己的注解@Resource
注解有两个属性比较重要,一个是name
,一个是type
,Spring中使用name
属性解析为bean的名称,使用type
时解析为bean的类型,如果没有给定属性值,Spring的反射机制通过byName
来自动注入属性@Autowired
是Spring提供的注解,在Spring2.5版本后引入的只根据type
去进行注入,不用name
,如果涉及到type
无法识别注入对象时,仅使用@Autowired
是无法完成注入的,需要借助其他的注解一块才能完成
依赖的解析过程
在Spring的依赖的解析过程:
- ApplicationContext通过配置的元数据来创建和初始化,这些元数据描述了所有的bean,元数据的信息是可以通过注解,xml或者是Java代码来描述
- 对于每一个bean,他的依赖用属性、构造函数或者是静态工厂方法等形式来表达,bean被创建好之后这些依赖会提供给他
- 每一个属性或者构造方法都要被设置的值的实际定义,或者是对容器中的另一个bean的引用
- 每个属性或者构造方法的值的实际定义都会转化为当前实例bean的实际的值
在容器创建的过程中,Spring容器会验证每一个bean的配置,在实际创建bean之前,bean的属性不会被设置,单例和被设置为首先加载的bean会在容器初始化后就创建出来,其他的bean只会在需要的时候才会创建,创建bean过程可能会引起一系列的bean被创建,注意:此时依赖之间解析不匹配的问题有可能会出现,即会循环创建有影响的Bean。
循环依赖问题:
class User{
private Person person;
}
class Person{
private User user;
}
- 此时如果是通过构造方法的方式注入,就有可能造成无法解决的循环依赖。
- 例如:ClassA需要通过构造方法注入一个ClassB的实例,ClassB同样需要通过构造方法注入ClassA的实例,如果为ClassA和ClassB配置Bean并且相互注入,IOC容器在运行时会发现这个循环引用,并抛出异常:
BeanCurrentlyInCreationException
。
解决途径是:这些类可以通过setter注入,避免使用构造方法注入