一、Bean的作用域
1.1 singleton
默认情况下,Spring的IoC容器创建的Bean对象是单例的,Bean对象的创建是在初始化Spring上下⽂的时候就完成的。
1.2 prototype
如果想让Spring的Bean对象以多例的形式存在,可以在bean标签中指定scope属性的值为:prototype,这样Spring会在每⼀次执⾏getBean()⽅法的时候创建Bean对象,调⽤⼏次则创建⼏次。
<bean id="sb" class="com.powernode.spring6.beans.SpringBean" scope="prototype" />
scope如果没有配置,默认值是singleton,单例的。
1.3 其他scope
scope属性的值不⽌两个,它⼀共包括8个选项:
- singleton:默认的,单例。
- prototype:原型。每调⽤⼀次getBean()⽅法则获取⼀个新的Bean对象。或每次注⼊的时候都是新对象。
- request:⼀个请求对应⼀个Bean。仅限于在WEB应⽤中使⽤。
- session:⼀个会话对应⼀个Bean。仅限于在WEB应⽤中使⽤。
- global session:portlet应⽤中专⽤的。如果在Servlet的WEB应⽤中使⽤global session的话,和session⼀个效果。(portlet和servlet都是规范。servlet运⾏在servlet容器中,例如Tomcat。portlet运⾏在portlet容器中。)
- application:⼀个应⽤对应⼀个Bean。仅限于在WEB应⽤中使⽤。
- websocket:⼀个websocket⽣命周期对应⼀个Bean。仅限于在WEB应⽤中使⽤。
- ⾃定义scope:很少使⽤。
二、Bean的生命周期
2.1 Bean的生命周期
Spring其实就是⼀个管理Bean对象的⼯⼚。它负责对象的创建,对象的销毁等。所谓的⽣命周期就是:对象从创建开始到最终销毁的整个过程。
2.2 Bean的声明周期之5步
Bean⽣命周期可以粗略的划分为五⼤步:
- 第⼀步:实例化Bean
- 第⼆步:Bean属性赋值
- 第三步:初始化Bean
- 第四步:使⽤Bean
- 第五步:销毁Bean
定义⼀个Bean
public class User {
private String name;
public User() {
System.out.println("1.实例化Bean");
}
public void setName(String name) {
this.name = name;
System.out.println("2.Bean属性赋值");
}
public void initBean(){
System.out.println("3.初始化Bean");
}
public void destroyBean(){
System.out.println("5.销毁Bean");
}
}
配置⽂件中的init-method指定初始化⽅法。destroy-method指定销毁⽅法。
<bean id="userBean" class="com.powernode.spring6.bean.User" init-method="initBean" destroy-method="destroyBean">
<property name="name" value="zhangsan"/>
</bean>
2.3 Bean的声明周期之7步
在以上的5步中,第3步是初始化Bean,如果你还想在初始化前和初始化后添加代码,可以加⼊“Bean后处理器”。
编写⼀个类实现BeanPostProcessor类,并且重写before和after⽅法:
public class LogBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean后处理器的before⽅法执⾏,即将开始初始化");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean后处理器的after⽅法执⾏,已完成初始化");
return bean;
}
}
在spring.xml⽂件中配置“Bean后处理器”:
<bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/>
注意:在spring.xml⽂件中配置的Bean后处理器将作⽤于当前配置⽂件中所有的Bean。
三、GoF之工厂模式
GoF23种设计模式可分为三⼤类:
- 创建型(5个):解决对象创建问题。
- 单例模式
- ⼯⼚⽅法模式
- 抽象⼯⼚模式
- 建造者模式
- 原型模式
- 结构型(7个):⼀些类或对象组合在⼀起的经典结构。
- 代理模式
- 装饰模式
- 适配器模式
- 组合模式
- 享元模式
- 外观模式
- 桥接模式
- ⾏为型(11个):解决类或对象之间的交互问题。
- 策略模式
- 模板⽅法模式
- 责任链模式
- 观察者模式
- 迭代⼦模式
- 命令模式
- 备忘录模式
- 状态模式
- 访问者模式
- 中介者模式
- 解释器模式
⼯⼚模式是解决对象创建问题的,所以⼯⼚模式属于创建型设计模式,Spring框架底层使⽤了⼤量的⼯⼚模式。
3.1 工厂模式的三种形态
⼯⼚模式通常有三种形态:
- 简单⼯⼚模式(Simple Factory):不属于23种设计模式之⼀。简单⼯⼚模式⼜叫做:静态 ⼯⼚⽅法模式。简单⼯⼚模式是⼯⼚⽅法模式的⼀种特殊实现。
- ⼯⼚⽅法模式(Factory Method):是23种设计模式之⼀。
- 抽象⼯⼚模式(Abstract Factory):是23种设计模式之⼀。
3.2 简单工厂模式
简单⼯⼚模式的⻆⾊包括三个:
- 抽象产品 ⻆⾊
- 具体产品 ⻆⾊
- ⼯⼚类 ⻆⾊
举一个简单的例子:
你有一家兵工厂可以制作坦克、飞机、大炮。如果你不采用任何设计模式,你需要造多家兵工厂来满足消费者;同时如果你可以制造导弹,那么就需要你的兵工厂都做出改变,比较麻烦;而且你的制作和销售是搅和在一起的,比较混乱。
如果你采用简单工厂模式,你可以定义一个“抽象武器基类”(规定武器具有攻击行为);再通过抽象武器基类定义三个“产品类”坦克类、飞机类、大炮类;最后关键就是创建“武器工厂类”,你输入什么武器,它就生产什么武器。
简单⼯⼚模式的优点:
- 客户端程序不需要关⼼对象的创建细节,需要哪个对象时,只需要向⼯⼚索要即可,初步实现了责任的分离。客户端只负责“消费”,⼯⼚负责“⽣产”。⽣产和消费分离。
简单⼯⼚模式的缺点:
- 缺点1:⼯⼚类集中了所有产品的创造逻辑,形成⼀个⽆所不知的全能类,有⼈把它叫做上帝类。显然⼯⼚类⾮常关键,不能出问题,⼀旦出问题,整个系统瘫痪。
- 缺点2:不符合OCP开闭原则,在进⾏系统扩展时,需要修改⼯⼚类。
3.3 工厂方法模式
⼯⼚⽅法模式的⻆⾊包括:
- 抽象产品⻆⾊
- 具体产品⻆⾊
- 抽象⼯⼚⻆⾊
- 具体⼯⼚⻆⾊
举一个简单的例子:
现在兵工厂可以制造手雷、手枪这些小武器。如果我们还是采用简单工厂模式,除了添加具体的产品类,还要更改最后的工厂类,使得工厂类变得比较臃肿。
采用工厂方法模式我们除了添加具体的产品类,还有就是添加抽象工厂类(规定可以生产武器),再根据它创建具体工厂类小型武器类、大型武器类
⼯⼚⽅法模式的优点:
- ⼀个调⽤者想创建⼀个对象,只要知道其名称就可以了。
- 扩展性⾼,如果想增加⼀个产品,只要扩展⼀个⼯⼚类就可以。
- 屏蔽产品的具体实现,调⽤者只关⼼产品的接⼝。
⼯⼚⽅法模式的缺点: - 每次增加⼀个产品时,都需要增加⼀个具体类和对象实现⼯⼚,使得系统中类的个数成倍增加,在⼀定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
3.4 抽象工厂模式 (了解)
抽象⼯⼚模式相对于⼯⼚⽅法模式来说,就是⼯⼚⽅法模式是针对⼀个产品系列的,⽽抽象⼯⼚模式是针对多个产品系列的,即⼯⼚⽅法模式是⼀个产品系列⼀个⼯⼚类,⽽抽象⼯⼚模式是多个产品系列⼀个⼯⼚类。
抽象⼯⼚模式特点:抽象⼯⼚模式是所有形态的⼯⼚模式中最为抽象和最具⼀般性的⼀种形态。抽象⼯⼚模式是指当有多个抽象⻆⾊时,使⽤的⼀种⼯⼚模式。抽象⼯⼚模式可以向客户端提供⼀个接⼝,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。它有多个抽象产品类,每个抽象产品类可以派⽣出多个具体产品类,⼀个抽象⼯⼚类,可以派⽣出多个具体⼯⼚类,每个具体⼯⼚类可以创建多个具体产品类的实例。每⼀个模式都是针对⼀定问题的解决⽅案,⼯⼚⽅法模式针对的是⼀个产品等级结构;⽽抽象⼯⼚模式针对的是多个产品等级结果。
3.5 三种工厂模式对比
| 简单工厂 | 中央兵工厂 | 一个工厂生产所有产品,通过参数区分 |
| 工厂方法 | 分武器专属兵工厂 | 一个产品一个工厂,关注产品扩展 |
| 抽象工厂 | 各兵种武器兵工厂 | 一个工厂生产一套产品,关注产品族扩展 |
四、Bean的实例化方法
Spring为Bean提供了多种实例化⽅式,通常包括4种⽅式。
- 通过构造⽅法实例化
- 通过简单⼯⼚模式实例化
- 通过factory-bean实例化
- 通过FactoryBean接⼝实例化
4.1 通过构造方法实例化
我们之前⼀直使⽤的就是这种⽅式。默认情况下,会调⽤Bean的⽆参数构造⽅法。
public class User {
public User() {
System.out.println("User类的⽆参数构造⽅法执⾏。");
}
}
<?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 htt
p://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userBean" class="com.powernode.spring6.bean.User"/>
</beans>
当我们使用User user = applicationContext.getBean("userBean", User.class);实例化时会调⽤Bean的⽆参数构造⽅法,控制台会打印“User类的⽆参数构造⽅法执⾏。”。
4.2 通过简单工厂模式实例化
第⼀步:定义⼀个Bean
public class Vip {
}
第⼆步:编写简单⼯⼚模式当中的⼯⼚类
public class VipFactory {
public static Vip get(){
return new Vip();
}
}
第三步:在Spring配置⽂件中指定创建该Bean的⽅法(使⽤factory-method属性指定)
<bean id="vipBean" class="com.powernode.spring6.bean.VipFactory" factory-method="get"/>
通过Vip vip = applicationContext.getBean("vipBean", Vip.class);调用。
4.3 通过factory-bean实例化(⼯⼚⽅法模式)
第⼀步:定义⼀个Bean
public class Order {
}
第⼆步:定义具体⼯⼚类,⼯⼚类中定义实例⽅法
public class OrderFactory {
public Order get(){
return new Order();
}
}
第三步:在Spring配置⽂件中指定factory-bean以及factory-method
<bean id="orderFactory" class="com.powernode.spring6.bean.OrderFactory"/>
<bean id="orderBean" factory-bean="orderFactory" factory-method="get"/>
通过Order orderBean = applicationContext.getBean("orderBean", Order.class);调用
4.4 通过FactoryBean接口实例化
以上的第三种⽅式中,factory-bean是我们⾃定义的,factory-method也是我们⾃⼰定义的。
在Spring中,当你编写的类直接实现FactoryBean接⼝之后,factory-bean不需要指定了,factorymethod也不需要指定了。factory-bean会⾃动指向实现FactoryBean接⼝的类,factory-method会⾃动指向getObject()⽅法。
public class Person {
}
第⼆步:编写⼀个类实现FactoryBean接⼝
public class PersonFactoryBean implements FactoryBean<Person> {
// 这个方法返回真正要的对象
@Override
public Person getObject() throws Exception {
return new Person();
}
// 返回要创建的类型
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
// true表示单例
// false表示原型
return true;
}
}
第三步:在Spring配置⽂件中配置FactoryBean
<bean id="personBean" class="com.powernode.spring6.bean.PersonFactoryBean"/>
FactoryBean在Spring中是⼀个接⼝。被称为“⼯⼚Bean”。“⼯⼚Bean”是⼀种特殊的Bean。所有的“⼯⼚Bean”都是⽤来协助Spring框架来创建其他Bean对象的。
4.5 BeanFactory和FactoryBean的区别
4.5.1 BeanFactory(Spring 的对象大管家)
Spring IoC容器的顶级对象,BeanFactory被翻译为“Bean⼯⼚”,在Spring的IoC容器中,负责 Bean 的创建、配置、组装和管理生命周期。。BeanFactory是⼯⼚。我们通常不会直接操作 BeanFactory,而是用它的高级版本 ApplicationContext(它继承了 BeanFactory,并添加了更多企业级功能)。
// 1. 创建 BeanFactory(建立人事管理系统)
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// 2. 注册 Bean 定义(制定招聘岗位说明书)
RootBeanDefinition beanDef = new RootBeanDefinition(UserServiceImpl.class);
factory.registerBeanDefinition("userService", beanDef);
// 3. 获取 Bean(招人到岗)
UserService service = factory.getBean("userService");
其体现了工厂模式、单例模式、控制反转(IoC)、依赖注入等设计模式。
4.5.2 FactoryBean(写的一个工具类,用于创建复杂对象)
FactoryBean:它是⼀个Bean,是⼀个能够辅助Spring实例化其它Bean对象的⼀个Bean。FactoryBean 本身也是被 BeanFactory 管理的!
在Spring中,Bean可以分为两类:
- 普通Bean
- ⼯⼚Bean(记住:⼯⼚Bean也是⼀种Bean,只不过这种Bean⽐较特殊,它可以辅助Spring实例化其它Bean对象。)
五、Bean的循环依赖问题
A对象中有B属性。B对象中有A属性。这就是循环依赖。
例:
public class Husband {
private String name;
private Wife wife;
}
public class Wife {
private String name;
private Husband husband;
}
5.1 singleton下的set注入产生的循环依赖
在singleton + set注⼊的情况下,循环依赖是没有问题的。Spring可以解决这个问题。
Husband和Wife类中采用的set注入,public void setWife(Wife wife){this.wife = wife;}和public void setHusband(Husband husband){this.husband = husband;}
在spring.xml文件中:
<bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="singleton">
<property name="name" value="张三"/>
<property name="wife" ref="wifeBean"/>
</bean>
<bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="singleton">
<property name="name" value="⼩花"/>
<property name="husband" ref="husbandBean"/>
</bean>
5.2 prototype下的set注入产生的循环依赖
在prototype + set注⼊的情况下,会产⽣循环依赖,Spring⽆法解决,会出现BeanCurrentlyInCreationException异常。(如果其中⼀个是singleton,另⼀个是prototype,是没有问题的。)
在spring.xml文件中:
<bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="prototype">
<property name="name" value="张三"/>
<property name="wife" ref="wifeBean"/>
</bean>
<bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="prototype">
<property name="name" value="⼩花"/>
<property name="husband" ref="husbandBean"/>
</bean>
5.3 singleton下的构造注入产生的循环依赖
在singleton+构造注入的情况下,会产生循环依赖,Spring无法解决。因为构造⽅法注⼊会导致实例化对象的过程和对象属性赋值的过程没有分离开,必须在⼀起完成导致的。Husband和Wife类中采用的构造注入,public Husband(String name, Wife wife) { this.name = name;this.wife = wife; }和public Wife(String name, Husband husband) { this.name = name;this.husband = husband; }
<bean id="hBean" class="com.powernode.spring6.bean2.Husband" scope="singleton">
<constructor-arg name="name" value="张三"/>
<constructor-arg name="wife" ref="wBean"/>
</bean>
<bean id="wBean" class="com.powernode.spring6.bean2.Wife" scope="singleton">
<constructor-arg name="name" value="⼩花"/>
<constructor-arg name="husband" ref="hBean"/>
</bean>
5.4 Spring解决循环依赖的机理
Spring之所以可以解决set+singleton模式下的循环依赖,是因为这种⽅式可以做到将“实例化Bean”和“给Bean属性赋值”这两个动作分开去完成。
实例化Bean的时候:调⽤⽆参数构造⽅法来完成。此时可以先不给属性赋值,可以提前将该Bean对象“曝光”给外界。
给Bean属性赋值的时候:调⽤setter⽅法来完成。
也就是说,Bean都是单例的,我们可以先把所有的单例Bean实例化出来,放到⼀个集合当中(我们可以称之为缓存),所有的单例Bean全部实例化完成之后,以后我们再慢慢的调⽤setter⽅法给属性赋值。这样就解决了循环依赖的问题。
最后感谢动力节点提供的优质学习资源,让我们在技术道路上能够站在巨人的肩膀上继续前行。本资料仅为学习过程的副产品,希望能帮助到更多同样在努力学习的开发者。
网硕互联帮助中心






评论前必须登录!
注册