这篇博客简单分析了IOC和DI的原理及区别,并介绍了基于xml和注解的Bean配置方式。
1. 什么是IOC?
1.1 IoC(Inversion of Control)控制反转
- 控制:控制的双方是什么?谁控制谁?
- 主控方:Spring
- 被控方:应用程序所使用的资源
- (原始)应用程序控制自己执行操作需要使用的外部资源
- (Spring)Spring控制整个程序中所需要使用的外部资源
- 反转:什么是反转?反转什么?
- 正向:应用程序直接调用资源
- 反向:应用程序依赖Spring为其提供资源
- 反转的是资源的控制权
- 应用程序由主动调用资源,变为被动的等待Spring提供资源
- 有什么好处?
- 正向缺点:应用程序控制的资源分布在程序的每一个地方
- 反向的优点:所有的资源都在Spring中,便于管理
- 总结:Spring反向控制应用程序所需要使用的外部资源。资源受Spring管理,利用率最大化。
1.2 spring演进模式
A.基于分层设计
UserAction 使用UserService,new UserService()
UserService 使用UserDAO,new UserDAO()
UserDAO
B.工厂模式,使用面向接口编程设计,解决层与层之间的紧耦合
制作接口,制作实现类,制作对象工厂
1 | A a = AFactory.createA(); |
C.工厂模式+配置
制作XML文件,将实现类配置到XML文件中
读取XML文件中的配置信息,得到实现类的类名
使用反射思想,获取实现类对象 class.newInstance();
1 | A a = Factory.create(“a”); |
1 | <!--配置xml--> |
D.自动的工厂+配置
1 | //返回装配好的a |
1 | <!--xml配置--> |
E.工厂由Spring提供,实现类使用XML格式配置
工厂由Spring提供,实现类使用XML格式配置
1 | //返回装配好的a |
1 | <!--xml配置--> |
2. 什么是DI?
2.1 DI的概念
DI(Dependency Injection)依赖注入,指应用程序依赖Spring为其提供运行时所需的资源.
- 谁依赖于谁? ——- 应用程序依赖于IoC容器
- 为什么需要依赖? ——- 应用程序依赖于IoC容器装配类之间的关系
- 依赖什么东西? ——- 依赖了IoC容器的装配功能
- 谁注入于谁? ——- IoC容器注入应用程序
- 注入什么东西? ——- 注入应用程序需要的资源(类之间的关系)
更能描述容器其特点的名字——“依赖注入”(Dependency Injection)
IoC容器应该具有依赖注入功能,因此也可以叫DI容器
2.2 DI实现方式
- 构造器注入
- setter注入
- 接口注入
3. IOC和DI的关系?
IOC和DI其实是同一概念的两种说法,两者站立的角度不一样。
IOC:从spring的角度看,资源的控制权限被反转了
DI:从应用程序的角度看,应用程序要依赖spring为其注入资源。
4. BeanFactory
- BeanFactory是ApplicationContext的顶层父接口,使用BeanFactory接口也可以完成获取Bean的操作.
- 操作步骤:
1
2
3
4
5
6//初始化Resource对象
Resource res = new ClassPathResource("applicationContext.xml");
//初始化BeanFactory对象
BeanFactory bf = new XmlBeanFactory(res);
//根据id名称获取Bean
UserService service = (UserService) bf.getBean("userService");
AppplicationContext与BeanFactory的区别
A. 加载方式不同AppplicationContext:立即加载,加载配置文件时即加载 BeanFactory:延迟加载,获取Bean实例时才加载
B. AppplicationContext具有更多的功能
国际化处理 事件传递 Bean自动装配 各种不同应用层的Context实现 注意:实际开发中,优先选择ApplicationContext对象,避免使用BeanFactory
Schema风格离线约束配置方式
A.拷贝访问路径 B.打开MyEclipse设置中的XML catalog选项 C.创建新的映射 D.选择对应的schema风格的约束文件 E.将key type修改为schema location F.将要配置的路径复制到key中
5. Bean配置(XML)
5.1 Bean初始化
- 构造器初始化(重点)
Bean对应的类必须提供一个默认无参可访问的构造方法 静态工厂初始化
1
2
3
4
5
6<bean id="userService2"
<!--配置中所配置的class配置成工厂类的类名-->
class="com.topvision.bean.UserStataicFactory"
<!--配置工厂类中的创建实例的静态方法-->
factory-method="getInst">
</bean>实例工厂初始化
提供一个实例工厂类,使用其中的实例方法获取对象。由于该工厂类本身需要创建对象,因此该对象必须受Spring控制,所以必须配置该工厂类为Bean.1
2
3
4
5
6<!—实例化工厂Bean -->
<bean id="uf" class="com.topvision.bean.UserFactory"></bean>
<!--使用实例工厂创建Bean -->
<!--factory-bean: 配置实例工厂在Spring范围内对应的Bean的id名称-->
<!--factory-method:配置工厂类中的创建实例的实例方法-->
<bean id="userService3" factory-bean="uf" factory-method="getInst2"></bean>
5.2 Bean的作用域
Spring初始化的Bean默认为单例模式,如果想修改成非单例模式需要修改Bean的作用范围。1
<bean id=”beanId” class=”BeanClassName” scope=”prototype”></bean>
scope属性:
singleton:单例
prototype:非单例
request:请求对象范围request.setAttribute("beanId",obj);
session:会话Session范围request.getSession().setAttribute("beanId",obj);
globalSession:全局会话,分布式服务器
5.3 Bean的生命周期
定义Bean初始化与销毁时的动作,属于回调方法配置
定义bean时指定两个回调方法,控制bean的初始化与销毁操作时执行的动作
- init-method:初始化回调方法名,配置的方法必须由bean对象提供
- destroy-method:销毁回调方法名,配置的方法必须由bean对象提供
1 | <bean id="user" class="com.topvision.lifecycle.User" init-method="init" destroy-method="destroy" scope="prototype"></bean> |
- 销毁操作只针对scope=”singletion”的对象,对于非单例对象无效
- 单例对象的销毁是在IoC容器关闭时发生,使用ClassPathXmlApplicationContext对象close方法完成
- 非单例Bean对象的内存回收交由JVM完成——GC
5.4 Bean属性注入
构造器注入(了解)
A.在domain中提供对象的构造方法
B.xml配置中设置构造方法的参数1
2
3
4
5
6<!-- constructor-arg:使用构造器传递参数 -->
<!-- value:赋值 -->
<bean id="bean6" class="com.topvision.bean.xml.Bean6">
<constructor-arg value="topvision"/>
<constructor-arg value="2017"/>
</bean>注意:如果类型匹配不成功,可以为配置中指定index索引属性,对应构造器中参数的位置.
1
2
3
4<bean id="bean6" class="com.topvision.bean.xml.Bean6">
<constructor-arg index="0" value="2014" type="java.lang.Integer"></constructor-arg>
<constructor-arg index="1" value="topvision" type="java.lang.String"></constructor-arg>
</bean>说明:构造器传参受构造器参数位置和类型的限定,因此不常使用
setter注入
前提:setter注入要求Bean必须提供无参可访问构造方法A.注入简单类型
1.提供对应要注入的属性 2.为每个要注入的属性提供对应的标准封装setter方法 3.在配置中为Bean指定要注入的属性,使用property元素 name=“属性名” value=”值”
1
<property name="属性名" value="值"/>
B.注入引用类型
1.为某个Bean注入引用类型的值,首先在Bean对应的类中声明对应的属性
1
2
3
4
5
6
7
8private TeacherDAO dao;
```
2.为每个要注入的属性提供对应的标准封装setter方法 (访问器)
```xml
public void setDao(TeacherDAO dao) {
this.dao = dao;
}3.必须保障引入的对象是Spring控制的Bean
1
2<!-- 声明引用类型的资源为Bean -->
<bean id="teacherDao" class="com.topvision.di.setter.TeacherDAO"> </bean>4.在Bean的属性注入中,使用ref引用对应的资源 ref=”beanId/beanName”
1
2
3
4
5
6
7
8
9<!-- setter注入 -->
<!-- name:属性名 -->
<!-- value:简单类型的值 -->
<!-- ref:引用别的Bean,beanId/beanName -->
<bean id="bean7" class="com.topvision.bean.xml.Bean7">
<property name="name" value="topvision"/>
<property name="age" value="8"/>
<property name="dao" ref="teacherDao"></property>
</bean>
6. Bean配置(注解)
从Spring2.5开始提供使用注解的形式配置Bean。
6.1 配置Bean使用@Component注解
如需为Bean定义名称,在参数中添加Bean名称@Component(“beanName”)
6.2 设定Spring的自动扫描路径
用于检测对应的Bean是否配置了注解,并加载配置了注解的类
开启context空间的支持
1
2
3
4
5
6
7
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">开启自动扫描功能,并指定扫描的路径
1
<context:component-scan base-package="com.topvision.annotation"></context:component-scan>
说明:扫描路径可以设置多个,中间使用,隔开,扫描路径包含指定包和子包内所有的类,通常使用*号通配符匹配路径
6.3 Spring提供的三个注解
- @Repository 用于数据层实现类标注
- @Service 用于业务逻辑层实现类标注
- @Controller 用于控制层实现类进行标注
6.4 注入简单类型
- 在属性名上方声明该属性自动装配@Autowired
- 在属性名上方声明该属性注入的值@Value(value)
1
2
3
"topvision") (
private String msg;
注意:注解自动装配属性值无需提供对应属性的setter方法
6.5 注入引用类型
- 在属性名上方声明该属性自动装配@Autowired
- 在属性名上方声明该属性注入的值@Qualifier(bean引用名称)
1
2
3
"anno2") (
private AnnotationOther other;
6.6 Autowired
Autowired的required属性用用于声明自动装配的数据是必须的。
- 如果required=true,必须为其装配数据,如果没有值,抛出异常
- 如果required=false,自动装配数据失败,则注入值为null
6.7 Resource
- 格式一:@Resource(name=”myImpl”)
- 格式二:@Autowired(required=false)
@Qualifier(“myImpl”)
以上两种格式效果相同
6.8 Bean声明周期注解
- @PostConstruct
功能:为当前Bean指定init-method参数
格式:定义在成员方法的上方,兼容静态方法 - @PreDestroy
功能:为当前Bean指定destory-method参数
格式:定义在成员方法的上方,兼容静态方法
注意:要求当前类被注册为Bean,否则无效果
6.9 Bean作用域注解
在类的定义上方添加@Scope指定Bean的作用域
常用:@Scope(“prototype”)
默认:@Scope(“singleton”)
6.10 配置类注解
@Configuration配置当前类为配置类,用于加载其中配置的Bean,与静态工厂初始化Bean很相似
@Bean(name=”b1”)将配置类中的某个方法的返回值声明为Bean,该方法必须返回一个对象1
2
3
4
5
6
7//设定该类参与自动扫描
public class MyBeanFactory {
"aa","bb","cc"})//设定该方法的返回值是一个Bean (name={
public static BookService getInst(){
return new BookService();
}
}
以上操作必须基于自动扫描功能,如不开启自动扫描,Spring将无法查找到对应的注解配置
6.11 AnnotationConfigApplicationContext不使用自动扫描
AnnotationConfigApplicationContext对象可以手工加载基于注解配置的Bean1
2
3
4
5
6//用于手工添加注解开发Bean的上下文对象
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
//加载配置管理类
ctx.register(MyConfiguration.class);
//刷新上下文对象
ctx.refresh();
注意:注册完成后一定要进行刷新,否则加载的Bean将无法被使用