一.基本概念:
IoC:Inverse of Control(控制反转):
读作“反转控制”,更好理解,不是什么技术,而是一种设计思想,好比于MVC。就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
正控:若调用者需要使用某个对象,其自身就得负责该对象的创建。
反控:调用者只管负责从Spring容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了Spring框架。
在这里完美地体现了好莱坞法则(Don’t call me ,I’ll call you)。
DI:Dependency Injection(依赖注入)
从字面上分析:
IoC:指将对象的创建权,反转给了Spring容器;
DI :指Spring创建对象的过程中,将对象依赖属性(简单值,集合,对象)通过配置设值给该对象。
IoC和DI其实是同一个概念的不同角度描述,DI相对IoC而言,明确描述了“被注入对象依赖IoC容器配置依赖对象”。
Container:容器,在生活中容器就是一种盛放东西的器皿,从程序设计角度看作是装对象的对象,因为存在对对象的存入、取出等操作,所以容器还要管理对象的生命周期。
二.详细分析DI和IOC:
(1).IoC是什么
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:
●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
用图例说明一下,传统程序设计如下图1,都是主动去创建相关对象然后再组合起来:
(2).IoC能做什么
传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
(3)总结:
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”;分析如下:
●谁依赖于谁:当然是应用程序依赖于IoC容器;
●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
IoC和DI由什么关系呢?
其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),可以理解为:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
三.一些代码分析:
(1).为了实现IoC功能,Spring提供了两个类
①BeanFactory:Bean工厂,借助于配置文件能够实现对JavaBean的配置和管理,用于向使用者提供Bean的实例。
备注:bean是Spring管理的单位,在Spring中一切都是bean.
②ApplicationContext:ApplicationContext构建在BeanFactory基础之上,提供了更多的实用功能。
BeanFactory的初始化和ApplicationContext的初始化有一个很大的区别:
ApplicationContext初始化时会实例化所有单实例(注意是单实例)的bean,后面调用getBean方法的时候,就可以直接从缓存中进行读取;而BeanFactory初始化时不会实例化Bean,直到第一次访问某个Bean时才会进行实例化。因此初始化ApplicationContext比BeanFactory慢,但后面调用Bean实例对象的时候则ApplicationContext比BeanFactory快。
(2).实例化Bean的三种方式:
①.构造器实例化(无参数构造器),最标准,使用最多。
②.静态工厂方法实例化:解决系统遗留问题
③.实例工厂方法实例化:解决系统遗留问题
④.实现FactoryBean接口实例化:实例工厂变种:集成其他框架使用:LocalSessionFactoryBean
(3).bean标签常用属性:
1.id
唯一,必须以字母开头,不能包含一些特殊字符。Spring根据class属性创建对应类的实例后,会以id为键key,实例对象为值value放入一个Map中,当调用getBean方法时,则会根据id的值从Map中把实例取出来。
2.class
Bean类的全路径,不能是接口
3.name
可以出现特殊符号,当没有设置id的时候,name也可以作为id
4.scope
Bean的作用域
(4).bean的初始化和销毁方法:
比如DataSource,SqlSessionFactory最终都需要关闭资源:在Bean销毁之前,都要调用close方法.
<bean id="someBean" class="......" init-method="该类中初始化方法名" destroy-method="该类中销毁方法名">
</bean>
init-method:bean生命周期初始化方法,对象创建后就进行调用
destroy-method:容器被销毁的时候,如果bean被容器管理,会调用该方法。
default-init-method
分析原理:
如果bean的scope=“prototype”,那么容器只负责创建和初始化,它并不会被spring容器管理。交给用户自己调用.
总结:bean的生命周期过程
(5).属性注入:
创建对象的时候,向类里面的属性设置值。
两种种方式实现属性注入
1.使用set方法(常用)
2.使用带参的构造函数
<!-- 使用带参的构造方法为Bean中的属性设值 -->
<bean id="property1" class="com.fym.PropertyDemo">
<!-- name属性表示Bean类中属性的名字, value表示要设置的值-->
<constructor-arg name="name" value="lucy"></constructor-arg>
</bean>
<!-- 使用set方法为Bean中的属性设值 -->
<bean id="property2" class="com.fym.PropertyDemo2">
<!-- 使用property标签 -->
<property name="book" value="lili"></property>
</bean>
一些复杂类型的注入
1.数组类型
2.List类型
3.Map类型
4.java.util.Properties类型
(6).属性占位符:
举例:配置一个连接池的bean(druid连接池):
使用属性占位符:
1.引入context命名空间:
2:编写db.properties文件:
(7).自动装配(注解方式):
在Spring中使用注解来完成DI操作,我们称之为注解自动装配,存在两种用法.
①:使用Spring框架自身提供的标签:Autowired
②:使用JavaEE规范提供的标签:Resource
①使用Spring框架自身提供的标签:Autowired
Autowired和Qualifier标签:
1.通过@Autowired标签可以让Spring自动的把属性需要的对象从Spring容器中找出来,并注入(设置)给该属性。
2.第三方程序:Spring3.0之前,需要手动配置@Autowired解析注解程序,Spring就会自动的加入针对@Autowired标签的解析程序。从Spring3.0开始,可以不再需要改配置了。
3.@Autowired标签贴在字段或者setter方法上。
4.@Autowired可以同时为一个属性注入多个对象。
public void setXxx(OtherBean1 other1,OtherBean2 other2) {}
5.使用@Autowired标签可以注入Spring内置的重要对象,比如BeanFactory,ApplicationContext。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SpringTest {
@Autowired
private ApplicationContext ctx;
}
6.默认情况下@Autowired标签必须要能找到对应的对象,否则报错。不过,可使用required=false来避免该问题:@Autowired(required=false)
7.@Autowired找bean的方式:
1)、首先按照依赖对象的类型找,如果找到则使用setter方法或者字段直接注入;
2)、如果在Spring上下文中找到多个匹配的类型,再按照名字去找,如果没有匹配则报错;
3)、可以通过使用@Qualifier(“otherBean”)标签来规定依赖对象按照bean的id+类型去找;
注意:使用了该注解之后,就不能根据类型去匹配对象
②使用JavaEE规范提供的标签:Resource
@Resource标签:
1,@Resource标签是JavaEE规范的标签;
2,@Resource标签也可以作用于字段或者setter方法;
3,也可以使用@Resource标签注入一些spring内置的重要对象,比如BeanFactory.ApplicationContext;
4,@Resource必须要求有匹配的对象;
5,context:annotation-config既引入了@Autowired标签的解析器,也引入了@Resource的解析器;
6,@Resource标签找bean的方式:
1),首先按照名字去找,如果找到,就使用setter或者字段注入;
2),如果按照名字找不到,再按照类型去找,但如果找到多个匹配类型,报错;
3),可以直接使用name属性指定bean的名称;但是,如果指定的name,就只能按照name去找,如果找不到,就不会再按照类型去找;
(8).使用注解简化IoC:
使用标签简化IoC:
1.使用标签来完成IoC,就必须有IoC标签的解析器
使用context:component-scan来扫描spring需要管理的bean
base-package就告诉spring,去哪些包及其子包里去扫描bean,如果有多个包需要被扫描;只需要用逗号隔开多个包即可
2.标注Bean的注解:@Component
默认情况,直接使用类的名字(首字母小写作为bean的名字)
如果要修改bean的名称;直接使用value属性来重新定义bean的名称
@Component(“otherbean”)
public class OtherBean {}
3.使用@Component的限制:
1),不能运用到静态工厂方法和实例工厂方法,但是可以使用到FactoryBean;
2),对于没有源代码的类(框架内部的预定义类),只能用XML配置;必然DataSource就只能使用XML方式来做IOC.
4.bean组件版型标签
bean组件版型:
@Service用于标注业务层组件、
@Controller用于标注控制层组件(如struts中的action)、
@Repository用于标注数据访问组件,即DAO组件。
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
5.指定bean的作用域:@Scope(“prototype”)
6.初始化和销毁方法
@PostConstruct
public void init() {
相当于
@PreDestroy
public void destory() {
相当于
7.选用xml还是注解:
1),Annotation:使用方便,XML文件很小,但是,依赖关系又重新回到了代码当中;
2),XML:使用稍微复杂,但是,代码很干净,代码不会很任何框架产生关系;XML安全;
两种方式都必须掌握;