一、背景
我们都知道,在采用面向对象思想设计的应用系统中,一个系统或功能接口实现的底层实现都是由Ñ个对象组成的,所有的对象通过彼此的合作,最终实现系统或接口的业务逻辑。比如获取一个用户的角色权限信息,那么我们可能需要先获取用户的角色信息,再根据角色获取对应的菜单和按钮的数据。各个对象中产生直接或间接的耦合关系。就好像一个齿轮组,它拥有多个独立的齿轮,这些齿轮相互啮合在一起,协同工作,共同完成某项任务。
耦合关系不仅会出现在对象与对象之间,也会出现在软件系统的各模块之间。如何降低系统之间,模块之间和对象之间的耦合度,是应用系统追求的目标之一。
二、IOC简介
IOC(Inversion of Control)控制反转,这是Spring的核心,是一种设计思想。它在整个应用中贯穿始末。Spring通过DI(Dependency Injection)依赖注入的方式来实现IOC,在应用启动时创建应用所需的Bean对象,并交由IOC容器进行统一管理,IOC容器来管理应用中对象的生命周期和对象间的关系,应用需要哪种对象,只需使用依赖注入的形式告诉Spring就可以了,Spring会动态的向应用提供系统所需的Bean对象,确保系统的正常运行。降低模块、对象间的耦合度。
三、IOC容器
Spring提供两种IOC容器:1,BeanFactory 2,ApplicationContext
1,BeanFactory的设计路径,从接口BeanFactory到HiearerchialBeanFactory,再到ConfigurableBeanFactory。
这条接口设计路径中,BeanFactory接口定义了基本的IOC容器规范。在这个接口定义中,BeanFactory包括了getBean()这样的IOC容器的基本方法。而HiearerchialBeanFactory接口在继承了BeanFactory的基本接口之后,增加了getParentBeanFactory()的接口功能,使得BeanFactory具备了双亲IOc容器的管理功能。
ConfigurableBeanFactory主要定义了对BeanFactory的配置功能,比如setParentBeanFactory()设置双亲IOC容器,通过addBeanPostProcessor()配置Bean的后置处理器等。
BeanFactory。基础类型IOC容器,提供完整的IOC服务支持。如果没有特殊指定,默认采用延迟初始化策略(lazy-load)。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较合适的IoC容器选择。
2,ApplicationContext应用上下文接口为核心的接口设计。
这里涉及的主要设计接口有,从BeanFactory到ListableBeanFactory,再到ApplicationContext,再到常用的WebApplicationContext或者ConfigurableApplicationContext接口。项目中常用的应用上下文基本都是ConfigurableApplicationContext或者WebApplicationContext的实现。
在这个接口体系中,ListableBeanFactory和HiearerchialBeanFactory俩个接口,连接BeanFactory接口定义和applicationContext应用上下文接口定义。
ListableBeanFactory接口中,细化了许多BeanFactory的接口功能,比如定义了setBeanDefinitionNames接口方法;
对于ApplicationContext接口,它通过继承了MessageSource、ResourceLoader、ApplicationEventPublisher接口,在BeanFactory简单Ioc容器的基础上添加了许多对高级容器的特性的支持。
ApplicationContext。ApplicationContext在BeanFactory的基础上构建,是相对比较高级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级特性,比如事件发布、国际化信息支持等,ApplicationContext所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于BeanFactory来说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容器启动时间较之BeanFactory也会长一些。在那些系统资源充足,并且要求更多功能的场中,ApplicationContext类型的容器是比较合适的选择。
四、IOC获取依赖关系的方式
我们无论使用哪种容器,我们都需要通过某种方法告诉容器关于对象依赖的信息,只有这样,容器才能合理的创造出对象,否则,容器自己也不知道哪个对象依赖哪个对象。
Spring提供的方法:
1,通过最基本的文本文件来记录被注入对象和其依赖对象之间的对应关系;
2,通过编写代码的方式来注入这些对应信息;
3,通过描述性较强的XML文件格式来记录对应信息;
4,通过注解方式来注入这些对应信息;
而我们常用的也就是XML文件和注解的方式来注入对象与对象间的依赖关系。
五、IOC的注入方式 ,DEMO实例
1,不使用IOC
package com.spring.iocdemo01;
public class Programmer {
private String name;
public void coding(){
//要用到computer对象,调用computer的coding方法,
//在这里new computer对象,控制权在Programmer手里
Computer computer = new Computer();
computer.coding();
}
}
package com.spring.iocdemo01;
public class Computer {
private String brand;
public void coding() {
System.out.println("Computer is coding!!!");
}
}
以上就是为是IOC的代码形式,需要手动new对应的依赖对象,执行相应的方法。
2,setter注入
Programmer类以属性的形式声明依赖关系,并添加该属性的setter方法
package com.spring.iocdemo02;
public class Programmer {
private String name;
//在这里定义依赖的computer属性
private Computer computer;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 为依赖的属性添加Setter方法
*/
public void setComputer(Computer computer) {
this.computer = computer;
}
public Computer getComputer() {
return computer;
}
}
package com.spring.iocdemo02;
public class Computer {
private String brand;
public void coding() {
System.out.println("Computer is coding!!!");
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
}
使用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对象,以及对象中的属性和属性值-->
<bean id="programmer" class="com.spring.iocdemo02.Programmer">
<property name="name" value="小李"></property>
<property name="computer" ref="computer"></property>
</bean>
<bean id="computer" class="com.spring.iocdemo02.Computer">
<property name="brand" value="hp"></property>
</bean>
</beans>
每个<bean></bean>标签都是声明一个bean,可以理解为实例化了一个对象,id声明该bean对象在IOC容器中的唯一标识,property声明bean对象中的属性,value为每个属性赋值。ref是引用的意思,引用id为computer的bean标签所声明的bean对象。以上,就是属性注入。关键的是在类里面声明属性,写set方法,然后在xml里面配置bean和property的值。
3,构造方法注入
构造器注入,顾名思义,就是在构造器里面注入依赖对象。它的实现方式其实跟setter注入差不多,bean对象中必须定义一个有参构造器,然后配置xml文件就行了。
package com.spring.iocdemo03;
import com.spring.iocdemo03.Computer;
public class Programmer {
private Computer computer;
public Programmer(Computer computer){
this.computer = computer;
}
}
package com.spring.iocdemo03;
public class Computer {
private String brand;
public Computer(String brand) {
this.brand = brand;
}
}
<?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.iocdemo03.Programmer">
<constructor-arg ref="computer"></constructor-arg>
</bean>
<!-- 构造器里面没有name字段,只有value,是根据构造器的方法参数顺序来定义的 -->
<bean id="computer" class="com.spring.iocdemo03.Computer">
<constructor-arg value="联想"></constructor-arg>
</bean>
</beans>
constructor-arg就是声明构造器中的参数,value为参数赋值。ref还是引用的意思,引用指定id的bean对象为构造器的参数。
4,自动装配
通过Spring提供的@Autowire的注解声明属性,以实现bean对象的自动装配。
package com.spring.iocdemo04;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Programmer {
@Autowired
Computer computer;
}
package com.spring.iocdemo04;
import org.springframework.stereotype.Component;
@Component
public class Computer {
private String brand;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
}
<?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">
<!--设置Spring扫描的包路径,将指定包路径下所有带有注解的bean对象加载到IOC容器中-->
<context:component-scan base-pakage="com.spring.iocdemo04">
</beans>
需要注意的是,在类上加@Component、@Controller、@Service、@Repository(这四个注解使用在不同应用层的bean对象,但都是声明该类在应用启动时加载到IOC容器中)注解,在需要注入的属性上加@Autowired注解,这样xml里面的自动扫描就会扫描到这些加了注解的类和属性,在实例化bean的时候,Spring容器会把加了@Component的类实例化;在实际运行时,会给加了@Autowired的属性注入对应的实例。
Spring自动装配注意点:
- 如果使用了构造器注入或者setter注入,那么将覆盖自动装配的依赖关系,因为自动装配优先XML文件前加载,到加载XML时就覆盖了自动装配。
- 基本数据类型的值、字符串无法使用自动装配来注入。