Spring学习记录之Bean的实例化方式
Spring学习记录之Bean的实例化方式
前言
这篇文章是我第二次学习b站老杜的spring
相关课程所进行的学习记录
,算是对课程内容及笔记的二次整理,以自己的理解方式进行二次记录,其中理解可能存在错误,欢迎且接受各位大佬们的批评指正;
关于本笔记,只是我对于相关知识遗忘时快速查阅了解使用,至于课程中实际实验配置等,也只是记录关键,并不会记录详细步骤,若想了解可以关注我博客的项目经验模块,我会在实际项目开发过程中总结项目经验,在该模块发布!
学习视频地址:https://www.bilibili.com/video/BV1Ft4y1g7Fb/
视频配套笔记:https://www.yuque.com/dujubin/ltckqu/kipzgd?singleDoc# 《Spring6》 密码:mg9b
目录
一、我个人对这部分学习的一些见解
我们要熟悉Spring
实例化Bean
有四种方式,以下我也会提到相应方式的应用场景,理解这些应用场景,用到的时候查阅即可;
这部分我会继续引用老杜的笔记。
二、Bean实例化的四种方式
Spring
为Bean
提供了多种实例化方式,通常包括4种方式。(也就是说在Spring
中为Bean
对象的创建准备了多种方案,目的是:更加灵活)
- 第一种:通过构造方法实例化
- 第二种:通过简单工厂模式实例化
- 第三种:通过
factory-bean
实例化(工厂方法模式) - 第四种:通过
FactoryBean
接口实例化
① 通过构造方法实例化
在之前的学习中我们一直使用的都是通过构造方法实例化xml
文件中配置的Bean
对象。默认情况下,会调用Bean
的无参数构造方法实例化对象之后,再通过对象属性的set
方法或对象的构造方法去给对象属性赋值(依赖注入)。
User
package com.powernode.spring6.bean;
/**
* @author 动力节点
* @version 1.0
* @className User
* @since 1.0
**/
public class User {
public User() {
System.out.println("User类的无参数构造方法执行。");
}
}
spring.xml
<?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.xsd">
<bean id="userBean" class="com.powernode.spring6.bean.User"/>
</beans>
测试程序:
package com.powernode.spring6.test;
import com.powernode.spring6.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 动力节点
* @version 1.0
* @className SpringInstantiationTest
* @since 1.0
**/
public class SpringInstantiationTest {
@Test
public void testConstructor(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
User user = applicationContext.getBean("userBean", User.class);
System.out.println(user);
}
}
执行结果:
② 通过简单工厂模式实例化
第一步:定义一个Bean
Vip
package com.powernode.spring6.bean;
/**
* @author 动力节点
* @version 1.0
* @className Vip
* @since 1.0
**/
public class Vip {
}
第二步:编写简单工厂模式当中的工厂类
VipFactory
package com.powernode.spring6.bean;
/**
* @author 动力节点
* @version 1.0
* @className VipFactory
* @since 1.0
**/
public class VipFactory {
public static Vip get(){
return new Vip();
}
}
第三步:在Spring
配置文件中指定创建该Bean
的方法(使用factory-method
属性指定)
spring.xml
<bean id="vipBean" class="com.powernode.spring6.bean.VipFactory" factory-method="get"/>
第四步:编写测试程序
@Test
public void testSimpleFactory(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Vip vip = applicationContext.getBean("vipBean", Vip.class);
System.out.println(vip);
}
执行结果:
该方式的应用场景补充理解:
这种方法的应用场景,如果你不好理解,你可以不考虑自己编写的代码会使用,你可以先尝试考虑我们在使用其他框架或者jar
包中别人提供的具体实现类产品时,一般对外提供的都是该产品的工厂,我们也只知道使用该工厂调用什么方法就可以创建该具体实现类产品。那么,我们想要去在Spring
中实例化该产品对象,只能通过这个方式获取。这里的id
值vipBean
标识的就是vip
对象,而不是VipFactory
对象。
③ 通过factory-bean实例化(工厂方法模式)
这种方式本质上是:通过工厂方法模式进行实例化。
第一步:定义一个Bean
package com.powernode.spring6.bean;
/**
* @author 动力节点
* @version 1.0
* @className Order
* @since 1.0
**/
public class Order {
}
第二步:定义具体工厂类,工厂类中定义实例方法
package com.powernode.spring6.bean;
/**
* @author 动力节点
* @version 1.0
* @className OrderFactory
* @since 1.0
**/
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"/>
第四步:编写测试程序
@Test
public void testSelfFactoryBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Order orderBean = applicationContext.getBean("orderBean", Order.class);
System.out.println(orderBean);
}
执行结果:
该方式的应用场景补充理解:
我们知道工厂方法模式解决了简单工厂模式的两个主要缺点,一是弱化了工厂的主导地位,一个产品对应一个工厂,生产产品的方法为非静态方法,使用时需要创建对应的工厂对象。二是解决了简单工厂模式违背OCP原则的问题。
所以,我们在使用factory-bean
的方式进行对象实例化的过程中,需要在Spring
容器中先创建对应产品的工厂对象OrderFactory
,然后再通过配置factory-bean
属性指定哪一个对象是该产品的产品工厂,同时配置factory-method
属性指定调用该对象的哪一个方法创建该产品。这里配置的id
属性标识的就是由该产品工厂创建出来的产品对象。
应用场景只考虑使用其他框架或jar
包时创建需要指定某工厂才能创建该产品的情况下使用。该产品的创建其使用的是工厂方法模式就用factory-bean
方式实例化该产品。
④ 通过FactoryBean接口实例化
以上的第三种方式中,factory-bean
是我们自定义的,factory-method
也是我们自己定义的。
在Spring
中,当你编写的类直接实现FactoryBean
接口之后,factory-bean
不需要指定了,factory-method
也不需要指定了。
factory-bean
会自动指向实现FactoryBean
接口的类,factory-method
会自动指向getObject()
方法。
第一步:定义一个Bean
package com.powernode.spring6.bean;
/**
* @author 动力节点
* @version 1.0
* @className Person
* @since 1.0
**/
public class Person {
}
第二步:编写一个类实现FactoryBean
接口
package com.powernode.spring6.bean;
import org.springframework.beans.factory.FactoryBean;
/**
* @author 动力节点
* @version 1.0
* @className PersonFactoryBean
* @since 1.0
**/
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"/>
测试程序:
@Test
public void testFactoryBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Person personBean = applicationContext.getBean("personBean", Person.class);
System.out.println(personBean);
Person personBean2 = applicationContext.getBean("personBean", Person.class);
System.out.println(personBean2);
}
执行结果:
FactoryBean在Spring中是一个接口。被称为“工厂Bean”。“工厂Bean”是一种特殊的Bean。所有的“工厂Bean”都是用来协助Spring框架来创建其他Bean对象的。
该方式的应用场景补充理解:
其实FactoryBean
接口实例化Bean
的方式本质上就是简化了使用factory-bean
方式实例化Bean
对象时xml
的配置。
我们知道工厂方法模式实现产品的创建时,是一个产品对应一个工厂,而我们在使用factory-bean
的方式实例化想要的产品对象时,是先在xml
文件中配置该产品的工厂,然后再配置使用哪个工厂调用哪个方法来创建响应的产品对象。但既然我们知道一个产品对应一个工厂了,即这个产品工厂只能生产该产品。我们当然希望只配置该工厂,Spring
就会创建好对应的产品,被指定的id
直接标识该对象就可以了。Spring
中将实现了FactoryBean
接口的产品工厂,在xml
中直接进行如下响应配置即可创建需要的产品。
<bean id="personBean" class="com.powernode.spring6.bean.PersonFactoryBean"/>
应用场景只考虑使用其他框架或jar
包时创建需要指定某工厂才能创建该产品且该工厂实现了FactoryBean
接口的情况下使用。该产品的创建其使用的是工厂方法模式就用FactoryBean
接口方式实例化该产品。
三、BeanFactory和FactoryBean的区别(面试题)
BeanFactory
:Spring IoC
容器的顶级对象,BeanFactory
被翻译为“Bean工厂”,在Spring
的IoC
容器中,“Bean工厂”负责创建Bean
对象。BeanFactory
是工厂。
FactoryBean
:它是一个Bean
,是一个能够辅助Spring
实例化其它Bean
对象的一个Bean
。
在Spring
中,Bean
可以分为两类:
- 第一类:普通
Bean
- 第二类:工厂
Bean
(记住:工厂Bean
也是一种Bean
,只不过这种Bean
比较特殊,它可以辅助Spring
实例化其它Bean
对象。)
四、注入自定义Date(FactoryBean接口方式应用举例)
我们前面说过,java.util.Date
在Spring
中被当做简单类型,简单类型在注入的时候可以直接使用value
属性或value
标签来完成。但我们之前已经测试过了,对于Date
类型来说,采用value
属性或value
标签赋值的时候,对日期字符串的格式要求非常严格,必须是这种格式的:Mon Oct 10 14:30:26 CST 2022
。其他格式是不会被识别的。如以下代码:
Student
package com.powernode.spring6.bean;
import java.util.Date;
/**
* @author 动力节点
* @version 1.0
* @className Student
* @since 1.0
**/
public class Student {
private Date birth;
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Student{" +
"birth=" + birth +
'}';
}
}
spring.xml
<bean id="studentBean" class="com.powernode.spring6.bean.Student">
<property name="birth" value="Mon Oct 10 14:30:26 CST 2002"/>
</bean>
测试程序:
@Test
public void testDate(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Student studentBean = applicationContext.getBean("studentBean", Student.class);
System.out.println(studentBean);
}
执行结果:
如果把日期格式修改一下:
spring.xml
<bean id="studentBean" class="com.powernode.spring6.bean.Student">
<property name="birth" value="2002-10-10"/>
</bean>
执行结果:
这种情况下,我们就可以使用FactoryBean
来完成这个骚操作。
编写DateFactoryBean
实现FactoryBean
接口:
DateFactoryBean
package com.powernode.spring6.bean;
import org.springframework.beans.factory.FactoryBean;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author 动力节点
* @version 1.0
* @className DateFactoryBean
* @since 1.0
**/
public class DateFactoryBean implements FactoryBean<Date> {
// 定义属性接收日期字符串
private String date;
// 通过构造方法给日期字符串属性赋值
public DateFactoryBean(String date) {
this.date = date;
}
@Override
public Date getObject() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(this.date);
}
@Override
public Class<?> getObjectType() {
return null;
}
}
编写spring
配置文件:
spring.xml
<bean id="dateBean" class="com.powernode.spring6.bean.DateFactoryBean">
<constructor-arg name="date" value="1999-10-11"/>
</bean>
<bean id="studentBean" class="com.powernode.spring6.bean.Student">
<property name="birth" ref="dateBean"/>
</bean>
执行测试程序:
五、总结
这部分我们了解到:
- 默认情况下,
Spring
会调用Bean
的无参数构造方法实例化对象之后,再通过对象属性的set
方法或对象的构造方法去给对象属性赋值(依赖注入) factory-bean
实例化的本质是通过工厂方法模式进行实例化。FactoryBean
接口实例化Bean
的方式本质上就是简化了使用factory-bean
方式实例化Bean
对象时xml
的配置。
这里需要去了解老杜这节相关讲解,可以直接点击下面链接跳转到对应课程学习了解!