Spring IOC应用
Spring IOC基础
BeanFactory与ApplicationContext区别
- BeanFactory是Spring框架中IoC容器的顶层接⼝,它只是⽤来定义⼀些基础功能,定义⼀些基础规范
- ApplicationContext是它的⼀个⼦接⼝,所以ApplicationContext是具备BeanFactory提供的全部功能的
通常,我们称BeanFactory为SpringIOC的基础容器ApplicationContext是容器的⾼级接⼝,⽐BeanFactory要拥有更多的功能,比如说国际化支持和资源访问(xml,java配置类)等等
启动IOC容器方式
-
Java环境下启动IOC容器
- ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用)
- FileSystemXmlApplicationContext:从磁盘路径上加载文件
- AnnotationConfigApplicationContext:纯注解模式下加载配置类
Bean的作用范围及⽣命周期
作⽤范围的改变
在spring框架管理Bean对象的创建时,Bean对象默认都是单例的,但是它⽀持配置的⽅式改变作⽤范围。作⽤范围官⽅提供的说明如下图:
在上图中提供的这些选项中,我们实际开发中⽤到最多的作⽤范围就是singleton(单例模式)和prototype(原型模式,也叫多例模式)。配置⽅式参考下⾯的代码:
不同作⽤范围的⽣命周期
单例模式 singleton
- 对象出⽣:当创建容器时,对象就被创建了
- 对象活着:只要容器在,对象⼀直活着
- 对象死亡:当销毁容器时,对象就被销毁了
⼀句话总结:单例模式的bean对象⽣命周期与容器相同
多例模式 prototype
- 对象出⽣:当使⽤对象时,创建新的对象实例
- 对象活着:只要对象在使⽤中,就⼀直活着
- 对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了
⼀句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁
Bean标签属性
在基于xml的IoC配置中,bean标签是最基础的标签。它表示了IoC容器中的⼀个对象。换句话说,如果⼀个对象想让spring管理,在XML的配置中都需要使⽤此标签配置,Bean标签的属性如下:
- id属性:⽤于给bean提供⼀个唯⼀标识。在⼀个标签内部,标识必须唯⼀
- class属性:⽤于指定创建Bean对象的全限定类名
- name属性:⽤于给bean提供⼀个或多个名称(别名)。多个名称⽤空格分隔
- factory-bean属性:⽤于指定创建当前bean对象的⼯⼚bean的唯⼀标识。当指定了此属性之后,class属性失效
- factory-method属性:⽤于指定创建当前bean对象的⼯⼚⽅法,如配合factory-bean属性使⽤,则class属性失效。如配合class属性使⽤,则⽅法必须是static的
- scope属性:⽤于指定bean对象的作⽤范围。通常情况下就是singleton。当要⽤到多例模式时,可以配置为prototype
- init-method属性:⽤于指定bean对象的初始化⽅法,此⽅法会在bean对象装配后调⽤。必须是⼀个⽆参⽅法
- destory-method属性:⽤于指定bean对象的销毁⽅法,此⽅法会在bean对象销毁前执⾏。它只能为scope是singleton时起作⽤
DI依赖注⼊的xml配置
-
依赖注⼊分类
-
按照注⼊的⽅式分类
- 构造函数注⼊:顾名思义,就是利⽤带参构造函数实现对类成员的数据赋值
- set⽅法注⼊:它是通过类成员的set⽅法实现数据的注⼊(使⽤最多的)
-
按照注⼊的数据类型分类
- 基本类型和String:注⼊的数据类型是基本类型或者是字符串类型的数据。
- 其他Bean类型:注⼊的数据类型是对象类型,称为其他Bean的原因是,这个对象是要求出现在IoC容器中的。那么针对当前Bean来说,就是其他Bean了
- 复杂类型(集合类型):注⼊的数据类型是Aarry,List,Set,Map,Properties中的⼀种类型
-
xml与注解相结合模式
注意:
- 实际企业开发中,纯xml模式使⽤已经很少了
- 引⼊注解功能,不需要引⼊额外的jar
- xml+注解结合模式,xml⽂件依然存在,所以,springIOC容器的启动仍然从加载xml开始
-
哪些bean的定义写在xml中,哪些bean的定义使⽤注解
第三⽅jar中的bean定义在xml,⽐如德鲁伊数据库连接池,⾃⼰开发的bean定义使⽤注解
xml中标签与注解的对于(IOC)
xml形式 | 对应的注解形式 |
---|---|
标签 | @Component("accountDao"),注解加在类上bean的id属性内容直接配置在注解后⾯如果不配置,默认定义个这个bean的id为类的类名⾸字⺟⼩写;另外,针对分层代码开发提供了@Componenet的三种别名@Controller、@Service、@Repository分别⽤于控制层类、服务层类、dao层类的bean定义,这四个注解的⽤法完全⼀样,只是为了更清晰的区分⽽已 |
标签的scope属性 | @Scope("prototype"),默认单例,注解加在类上 |
标签的init- method属性 | @PostConstruct,注解加在⽅法上,该⽅法就是初始化后调⽤的⽅法 |
标签的destory- method属性 | @PreDestory,注解加在⽅法上,该⽅法就是销毁前调⽤的⽅法 |
Spring IOC高级特性
lazy-Init延迟加载
Bean的延迟加载(延迟创建)
ApplicationContext容器的默认⾏为是在启动服务器时将所有 singletonbean提前进⾏实例化。提前实例化意味着作为初始化过程的⼀部分,ApplicationContext实例会创建并配置所有的singleton bean
⽐如:
该bean默认的设置为:
lazy-init="false",⽴即加载,表示在spring启动时,⽴刻进⾏实例化,如果使用注解则为@Lazy,默认参数就是true
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
@Lazy
public class BeanExp {
}
如果不想让⼀个singleton bean 在 ApplicationContext实现初始化时被提前实例化,那么可以将bean设置为延迟实例化
设置 lazy-init为true
的 bean将不会在 ApplicationContext启动时提前被实例化,⽽是第⼀次向容器通过 getBean索取 bean时实例化的
如果⼀个设置了⽴即加载的 bean1,引⽤了⼀个延迟加载的 bean2 ,那么 bean1 在容器启动时被实例化,⽽ bean2 由于被 bean1 引⽤,所以也被实例化,这种情况也符合延时加载的 bean 在第⼀次调⽤时才被实例化的规则
也可以在容器层次中通过在 元素上使⽤ "default-lazy-init" 属性来控制延时初始化。如下⾯配置:
如果⼀个 bean 的 scope 属性为 scope="pototype"
时,即使设置了 lazy-init="false"
,容器启动时也不会实例化bean,⽽是调⽤ getBean ⽅法实例化的
应⽤场景
- 开启延迟加载⼀定程度提⾼容器启动和运转性能
- 对于不常使⽤的 Bean设置延迟加载,这样偶尔使⽤的时候再加载,不必要从⼀开始该 Bean就占⽤资源
FactoryBean和BeanFactory
- BeanFactory接⼝是容器的顶级接⼝,定义了容器的⼀些基础⾏为,负责⽣产和管理Bean的⼀个⼯⼚, 具体使⽤它下⾯的⼦接⼝类型,⽐如ApplicationContext
- Spring中Bean有两种,⼀种是普通Bean,⼀种是⼯⼚Bean(FactoryBean),FactoryBean可以⽣成某⼀个类型的Bean实例(返回给我们),也就是说我们可以借助于它⾃定义Bean的创建过程
Bean创建的三种⽅式中的静态⽅法和实例化⽅法和FactoryBean作⽤类似,FactoryBean使⽤较多,尤其在Spring框架⼀些组件中会使⽤,还有其他框架和Spring框架整合时使⽤
BeanExp3类
public class BeanExp3 {
String name;
String address;
int type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
@Override
public String toString() {
return "BeanExp3{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
", type=" + type +
'}';
}
}
MyFactoryBean类
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyFactoryBean implements FactoryBean {
@Value("测试Bean,中环大厦,1")
public String info;
@Override
public BeanExp3 getObject() throws Exception {
System.out.println("info:" + info);
BeanExp3 beanExp3 = new BeanExp3();
String[] array = info.split(",");
beanExp3.setName(array[0]);
beanExp3.setAddress(array[1]);
beanExp3.setType(Integer.valueOf(array[2]));
return beanExp3;
}
@Override
public Class> getObjectType() {
return BeanExp.class;
}
}
xml配置
测试,获取FactoryBean产⽣的对象
Object beanExp3 = appcationContext.getBean("myFactoryBean");
System.out.println(beanExp3);
测试,获取FactoryBean,需要在id之前添加“&”
Object beanExp3 = appcationContext.getBean("&myFactoryBean");
System.out.println(beanExp3);
后置处理器
Spring提供了两种后处理bean的扩展接⼝,分别为 BeanPostProcessor和BeanFactoryPostProcessor,两者在使⽤上是有所区别的
⼯⼚初始化(BeanFactory)—> Bean对象
在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor进⾏后置处理做⼀些事情
在Bean对象实例化(并不是Bean的整个⽣命周期完成)之后可以使⽤BeanPostProcessor进⾏后置处理做⼀些事情
注意:对象不⼀定是springbean,⽽springbean⼀定是个对象
BeanPostProcessor
BeanPostProcessor是针对Bean级别的处理,可以针对某个具体的Bean
该接⼝提供了两个⽅法,分别在Bean的初始化⽅法前和初始化⽅法后执⾏,具体这个初始化⽅法指的是 什么⽅法,类似我们在定义bean时,定义了init-method所指定的⽅法
定义⼀个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进⾏处理。如果要对 具体的某个bean处理,可以通过⽅法参数判断,两个类型参数分别为Object和String,第⼀个参数是每个bean的实例,第⼆个参数是每个bean的name或者id属性的值。所以我们可以通过第⼆个参数,来判断我们将要处理的具体的bean。
注意:处理是发⽣在Spring容器的实例化和依赖注⼊之后。
BeanFactoryPostProcessor
BeanFactory级别的处理,是针对整个Bean的⼯⼚进⾏处理,典型应
⽤:PropertyPlaceholderConfigurer
⽅法名字类似我们bean标签的属性,setBeanClassName对应bean标签中的class属性,所以当我们拿到BeanDefinition对象时,我们可以⼿动修改bean标签中所定义的属性值。
BeanDefinition对象:我们在XML中定义的bean标签,Spring解析bean标签成为⼀个JavaBean,这个JavaBean就是BeanDefinition
注意:调⽤ BeanFactoryPostProcessor ⽅法时,这时候bean还没有实例化,此时 bean 刚被解析成BeanDefinition对象