对象间存在依赖关系。假如A组件调用了B组件的方法,就可以称A组件依赖于B组件。通过使用依赖注入,Java EE应用中的各种组件不需要以硬编码方式耦合在一起,甚至无需使用工厂模式。当某个Java 实例需要其他Java 实例时,系统自动提供所需要的实例,无需程序显示获取,这种自动提供java实例称之为依赖注入,也可以称之为控制反转(Inversion of Control IoC)。
其实不管是控制反转还是依赖注入,都可以这样理解:当某个Java实例(调用者)需要另一个Java实例(被调用者)时,在传统的程序设计过程中,通常有调用者来创建被调用者的实例。但是在依赖注入/控制反转模式下,创建被调用者的工作不再是有调用者来完成,而是由Spring容器来完成,然后注入调用者。
Bean的实例化方式:
默认情况下调用new ClassPathXmlApplicationContext("beans.xml")即容器启动时初始化bean,之后只有singleton。可在bean节点设置lazy-init="true",或在beans节点中设置default-lazy-init="true",对全部Bean都在getBean()时才初始化。
1.类构造器
<bean id="personServiceBean" class="com.zero.service.impl.PersonServiceBean">
2.静态工厂方法
public class PersonServiceBeanStaticFactory {
public static PersonServiceBean create() {
return new PersonServiceBean();
}
}
<bean id="factory" class="com.zero.service.impl.PersonServiceBeanStaticFactory" factory-method="create"></bean>
3.实例工厂方法
public class PersonServiceBeanFactory {
public PersonServiceBean create() {
return new PersonServiceBean();
}
}
<bean id="factoryBean" class="com.zero.service.impl.PersonServiceBeanFactory" ></bean>
<bean id="factory" factory-bean="factoryBean" factory-method="create"></bean>
Bean的init-method与destory-method
可以让Spring容器在初始化Bean后执行init-method,在Spring容器关闭时执行destory-method方法
<bean id="personServiceBean" class="com.zero.service.impl.PersonServiceBean" init-method="init" destroy-method="close"></bean>
注意:使用BeanFactory的子类AbstractApplicationContext来完成close()方法,ApplicationContext无此方法。
依赖注入:
1、设置注入:IoC容器使用属性的setter方法来注入被依赖的实例。
2、构造注入:IoC容器使用构造器来注入被依赖的实例。
3、filed注入:注解方式
一、设置注入
设置注入是指IoC容器使用属性的setter方法来注入被依赖的实例。这种注入方式比较简单、直观。
public class Chinese implements Person {
private Axe axe;
private String name;
// 设值注入所需的setter方法
public void setAxe(Axe axe) {
this.axe = axe;
}
public void setName(String name) {
this.name = name;
}
// 实现Person接口的userAxe方法
public void useAxe() {
// 调用axe的chop方法,表明Person对象依赖于Axe对象
System.out.println("我是"+name+"用"+axe.chop());
}
}
Spring采用了XML文件作为配置文件。对于本应用的XML配置文件如下:
<!-- 配置Chinese实例,其实现类是Chinese -->
<bean id="chinese" class="com.spring.service.impl.Chinese">
<!-- 将StoneAxe注入给axe属性 -->
<property name="axe" ref="stoneAxe" />
<property name="name" value="孙悟空"/>
</bean>
<!-- 配置stoneAxe实例 -->
<bean id="stoneAxe" class="com.spring.service.impl.StoneAxe" />
Spring会自动接管每个<bean>定义里的<property>元素定义,Spring会在调用无参数的构造器、创建默认的Bean实例后,调用相应的setter方法为程序注入属性值。<property>定义的属性值将不再有该Bean来主动设置、管理,而是接受Spring的注入。
!!!注意<property>识别的是setter方法,即setAxe和setName。不需要匹配字段axe、name。!!!
程序没有为Person实例传入Axe实例,Axe实例由Spring在运行期间注入。Bean与Bean之间的依赖关系由Spring管理,Spring采用setter方法为目标Bean注入所依赖的Bean,这种方式被称之为设值注入。
从上面的实例可以看出,依赖注入以配置文件管理Bean实例之间的耦合,让Bean实例之间的耦合从代码层次分离出来。
二、构造注入
构造注入就是利用构造函数来设置依赖关系的方式。
public class Japanese implements Person{
private Axe axe;
//默认构造器
public Japanese(){
}
//构造注入所需的带参数构造器
public Japanese(Axe axe){
this.axe = axe;
}
public void useAxe() {
System.out.println(axe.chop());
}
}
为了使用构造注入,使用<constructor-arg>元素来指定构造器的参数。如下
<!-- 配置Japanese实例 -->
<bean id="japanese" class="com.spring.service.impl.Japanese">
<!-- 使用构造注入 ,为Japanese实例注入SteelAxe实例-->
<constructor-arg ref="stoneAxe"/>
</bean>
<!-- 配置stoneAxe实例 -->
<bean id="stoneAxe" class="com.spring.service.impl.StoneAxe" />
上面的配置文件使用<contructor-arg>元素指定了一个构造器参数,该参数类型是Axe,这指定Spring调用Chinese类里带一个Axe参数的构造器来创建chinese实例,因为使用了有参数的构造器创建实例,所以当Bean实例被创建完成后,该Bean的依赖关系也就已经设置完成。
<constructor-arg index="1" value="007"></constructor-arg>
<constructor-arg index="0" value="23"></constructor-arg>
可用index来指明第几个参数。
当参数是其他类型时,用<set><list><map><props>。。。
设值注入式先通过无参数的构造器创建一个Bean实例,然后调用它的setter方法注入依赖关系,而构造注入则是直接调用有参数的构造器,当Bean实例创建完成后,依赖关系也已经完成。
Spring支持两种依赖注入方式,这两种依赖注入方式并没有好坏之分,只是适合的场景有所不同。
设值注入有如下优点:
1、与传统的JavaBean的写法更相似,程序开发人员更加容易理解,接受。通过setter方法设定依赖关系显得更加直观、自然。
2、对于复杂的依赖关系,如果采用构造注入,会导致构造器过于臃肿,难以阅读。Spring在创建Bean实例时,需要同时实例化其依赖的全部实例,因此导致性能下降。而设值注入,则可以避免这些问题。
3、尤其是在某些属性可选的情况下,多参数的构造器更加笨重。
但是构造器也有如下优势:
1、构造注入可以再构造器中决定依赖关系的注入顺序,优先依赖的优先注入。
2、对于依赖关系无须变化的Bean,构造注入更有用处。因为没有setter方法,所有的依赖关系全部在构造器中设定,因此,无须担心后续的代码对依赖关系产生破坏。
3、依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系。对组件的调用者而言,组件内部的依赖关系完全透明,更加符合高内聚的原则。
通过上面的对比。所以建议用以设值注入为主,构造注入为辅的注入策略。对于依赖关系无须变化的注入,尽量采用构造注入;而其他的依赖关系,则考虑设值注入。
三. 注解注入
注解方式就不再需要setter方法。在beans.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
">
<!-- 注解注入 -->
<bean id="person" class="com.zero.dao.impl.PersonDaoBean"></bean>
<bean id="personServiceBean" class="com.zero.service.impl.PersonServiceBean" ></bean>
</beans>
手动方式:
一.@Autowired默认按类型匹配注入
1.@Autowired private PersonDaoBean client;
//默认按类型匹配注入,会找到PersonDaoBean类型的bean,然后注入
2.@Autowired @Qualifier("person")private PersonDaoBean client;
//配合使用可以按名称匹配,会找到id为person的bean,然后注入
3.@Autowired(required=false) @Qualifier("persons")private PersonDaoBean client;
//false时,按名称未找到时client可以赋值为null,true时,按名称未找到时就抛出异常
二.@Resource默认按名称匹配注入,即可以指定name,当找不到名称时才按类型装配
1.@Resource(name="person") private PersonDaoBean client;
或则:
2.@Resource
public void setClient(PersonDaoBean client) {
this.client = client;
}
//当直接使用@Resource private PersonDaoBean client;即未指定idname,则会根据变量名client去寻找跟client匹配的Bean,若未找到,则按类型来匹配注入。
在Spring2.5以后有组件自动扫描机制:让标注了一下注解的类自动成为被Spring管理的Bean。
@Component 泛指组件
@Service
业务层
@Controller
控制层
@Repository
数据访问层DAO
@Service("personService")
public class PersonServiceBean implements IPersonService {}
默认获取Bean时例如对PersonServiceBean类,用getBean(“personServiceBean”)获得。
可以指定作用域 @Scope(“prototype”)
在init()函数前加@PostConstruct实现init-method功能
@PreDestory实现destory-method功能
XML配置 <context:annotation-config /> 是告诉spring 容器采用自动扫描注解配置,
当然也可以通过添加如下bean来开启扫描注解配置:
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />