欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > spring的第三天学习笔记

spring的第三天学习笔记

2025/4/19 9:33:34 来源:https://blog.csdn.net/m0_73446768/article/details/147265468  浏览:    关键词:spring的第三天学习笔记

七、Bean的实例化方式

Spring为Bean提供了多种实例化方式,通常包括4种方式。(也就是说在Spring中为Bean对象的创建准备了多种方案,目的是:更加灵活)

  • 第一种:通过构造方法实例化
  • 第二种:通过简单工厂模式实例化
  • 第三种:通过factory-bean实例化
  • 第四种:通过FactoryBean接口实例化

7.1 通过构造方法实例化

我们之前一直使用的就是这种方式。默认情况下,会调用Bean的无参数构造方法。

package com.powernode.spring6.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 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;public class SpringInstantiationTest {@Testpublic void testConstructor(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");User user = applicationContext.getBean("userBean", User.class);System.out.println(user);}
}

执行结果:

  • 控制台输出:User类的无参数构造方法执行。
  • 输出User对象的地址信息

7.2 通过简单工厂模式实例化

第一步:定义一个Bean

package com.powernode.spring6.bean;public class Vip {
}

第二步:编写简单工厂模式当中的工厂类(简单工厂是使用静态方法)

package com.powernode.spring6.bean;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"/>

第四步:编写测试程序

@Test
public void testSimpleFactory(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Vip vip = applicationContext.getBean("vipBean", Vip.class);System.out.println(vip);
}

执行结果:

  • 控制台输出Vip对象的地址信息

7.3 通过factory-bean实例化

这种方式本质上是:通过工厂方法模式进行实例化。
第一步:定义一个Bean

package com.powernode.spring6.bean;public class Order {
}

第二步:定义具体工厂类,工厂类中定义实例方法

package com.powernode.spring6.bean;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);
}

执行结果:

  • 控制台输出Order对象的地址信息

7.4 通过FactoryBean接口实例化

以上的第三种方式中,factory-bean是我们自定义的,factory-method也是我们自己定义的。在Spring中,当你编写的类直接实现FactoryBean接口之后,factory-bean不需要指定了,factory-method也不需要指定了。
factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向getObject()方法。
第一步:定义一个Bean

package com.powernode.spring6.bean;public class Person {
}

第二步:编写一个类实现FactoryBean接口

package com.powernode.spring6.bean;import org.springframework.beans.factory.FactoryBean;public class PersonFactoryBean implements FactoryBean<Person> {@Overridepublic Person getObject() throws Exception {// 这里负责对象的创建System.out.println("PersonFactoryBean's getObject executed.");return new Person();}@Overridepublic Class<?> getObjectType() {// 返回创建的Bean的类型return Person.class;}@Overridepublic boolean isSingleton() {// true表示单例// false表示原型 (每次调用 getBean() 都产生新对象)return true;}
}

第三步:在Spring配置文件中配置FactoryBean

<bean id="personBean" class="com.powernode.spring6.bean.PersonFactoryBean"/>

测试程序:

@Test
public void testFactoryBean(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");// 通过Spring容器获取到的实际上是 getObject() 方法返回的对象Person personBean = applicationContext.getBean("personBean", Person.class);System.out.println(personBean);Person personBean2 = applicationContext.getBean("personBean", Person.class);System.out.println(personBean2);// 如果想获取 PersonFactoryBean 本身,需要在ID前加 '&'// Object factoryBean = applicationContext.getBean("&personBean");// System.out.println(factoryBean);
}

执行结果:

  • 控制台输出:PersonFactoryBean’s getObject executed.
  • 控制台输出两个Person对象的地址信息,由于isSingleton()返回true,两个对象地址相同

7.5 BeanFactory和FactoryBean的区别

7.5.1 BeanFactory

Spring IoC容器的顶级接口,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,“Bean工厂”负责管理(包括创建、配置、销毁等)Bean对象。
BeanFactory是Spring IoC容器的核心,是真正的工厂。

7.5.2 FactoryBean

FactoryBean:它是一个Bean,是一个能够辅助Spring实例化其它Bean对象的一个特殊的Bean。
在Spring中,Bean可以分为两类:

  • 第一类:普通Bean (我们自己定义的业务类、组件等)
  • 第二类:工厂Bean (实现了FactoryBean接口的Bean,用于创建其他Bean)

记住:工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象。当从容器中获取工厂Bean时(例如getBean("personBean")),默认情况下获取到的是它getObject()方法返回的对象,而不是工厂Bean本身。


7.6 使用FactoryBean注入自定义格式的Date

我们前面说过,java.util.Date在Spring中虽然可以被当做简单类型,使用value属性或<value>标签注入字符串,但Spring默认的PropertyEditor对日期字符串的格式要求非常严格,必须是toString()方法的默认输出格式(例如:Mon Oct 10 14:30:26 CST 2022)。其他自定义格式(如yyyy-MM-dd)直接使用value注入是无法识别的。

示例:定义Student类

package com.powernode.spring6.bean;import java.util.Date;public class Student {private Date birth;// Setter用于属性注入public void setBirth(Date birth) {this.birth = birth;}@Overridepublic String toString() {return "Student{" +"birth=" + birth +'}';}
}

尝试使用标准格式注入:

<bean id="studentBean" class="com.powernode.spring6.bean.Student"><!-- Spring 默认可以识别这种格式 --><property name="birth" value="Mon Oct 10 14:30:26 CST 2002"/>
</bean>

测试:

@Test
public void testDateDefaultFormat(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Student studentBean = applicationContext.getBean("studentBean", Student.class);System.out.println(studentBean);
}

执行结果:

  • 控制台输出Student对象信息,包含正确格式化的日期 Mon Oct 10 14:30:26 CST 2002

这种情况下,我们还可以使用FactoryBean来完成这个操作。
编写DateFactoryBean实现FactoryBean接口:

package com.powernode.spring6.bean;import org.springframework.beans.factory.FactoryBean;import java.text.SimpleDateFormat;
import java.util.Date;public class DateFactoryBean implements FactoryBean<Date> {// 可配置的日期格式private String strDate;public DateFactoryBean(String strDate) {this.strDate = strDate;}@Overridepublic Date getObject() throws Exception {// 格式化日期对象SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");return sdf.parse(strDate);}@Overridepublic Class<?> getObjectType() {return null;}
}

在Spring配置文件中配置DateFactoryBean并注入格式:

<!-- 配置 DateFactoryBean,并通过构造函数注入日期格式 -->
<bean id="dateFactory" class="com.powernode.spring6.bean.DateFactoryBean"><constructor-arg name="dateFormat" value="yyyy-MM-dd"/>
</bean><!-- 配置 Student Bean,并注入由 DateFactoryBean 创建的 Date 对象 -->
<bean id="studentBeanCustomDate" class="com.powernode.spring6.bean.Student"><!-- 注意这里 ref 指向的是 FactoryBean 的 ID --><!-- 通过工厂Bean:DateFactoryBean 来返回普通Bean:java.util.Date --><bean id="date" class="com.powernode.spring6.bean.DateFactoryBean"><constructor-arg name="strDate" value="2003-7-10"/></bean><bean id="student" class="com.powernode.spring.bean.Student"><property name="birth" ref="date"/></bean></bean>
</beans>
</bean>

编写测试程序:

@Test
public void testDateFactoryBean(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Student studentBean = applicationContext.getBean("studentBeanCustomDate", Student.class);// 输出的日期会是当前日期,因为 DateFactoryBean 的 getObject 直接返回 new Date()// 但关键在于,可以通过 FactoryBean 控制创建过程和类型System.out.println(studentBean);
}

执行结果:

  • 控制台输出Student对象信息,其birth属性将是由DateFactoryBean根据yyyy-MM-dd格式(虽然示例中直接返回new Date(),但原理是这样)创建的Date对象。

通过FactoryBean,我们可以灵活地控制复杂对象的创建过程,例如根据配置的格式字符串来创建Date对象,解决了Spring默认PropertyEditor无法处理自定义日期格式的问题。

八、Bean的生命周期

8.1 什么是Bean的生命周期

Spring框架作为一个IoC容器,本质上是一个高级对象工厂,它完全接管了Bean对象的创建、配置、初始化、使用和销毁的完整生命周期。所谓Bean的生命周期,就是指一个Bean对象从被Spring容器实例化开始,到最终被销毁的整个过程。

在深入理解Spring的Bean生命周期时,需要关注以下关键问题:

  • Bean对象何时被实例化?(对应构造方法的调用)
  • 依赖注入在何时完成?(属性赋值的时机)
  • 哪些初始化操作会在Bean就绪前执行?(初始化回调)
  • Bean对象何时可以被应用程序使用?
  • Bean对象何时被销毁?(容器关闭时机)
  • 销毁前会执行哪些清理操作?(销毁回调)

8.2 为什么要了解Bean的生命周期

掌握Bean生命周期的核心意义在于:了解特定时间节点上Spring容器调用了哪些类的哪些方法,这些知识对开发者具有以下价值:

  1. 自定义初始化逻辑:在Bean初始化阶段执行资源加载、连接建立等操作
  2. 实现优雅关闭:在Bean销毁阶段释放资源、关闭连接
  3. 解决依赖问题:理解依赖注入的精确时机,解决复杂依赖关系
  4. 开发自定义扩展点:编写自定义的BeanPostProcessor等组件
  5. 调试和排错:理解Bean创建失败的原因和位置

8.3 Bean生命周期的5个基本阶段

根据Spring核心源码(AbstractAutowireCapableBeanFactory类的doCreateBean()方法实现),Bean的标准生命周期可以概括为5个主要阶段:

  1. 实例化Bean:调用构造方法创建对象实例,分配内存空间
  2. 属性注入:填充属性值,完成依赖注入(DI)
  3. 初始化Bean:执行自定义初始化方法,准备Bean投入使用
  4. 使用Bean:Bean可被应用程序正常使用
  5. 销毁Bean:容器关闭时执行自定义销毁方法,释放资源

示例代码

public class User {private String name;// 第一步:实例化Bean(构造方法)public User() {System.out.println("1.实例化Bean - 构造方法执行");}// 第二步:Bean属性赋值(依赖注入)public void setName(String name) {this.name = name;System.out.println("2.Bean属性赋值 - 依赖注入");}// 第三步:初始化Bean(初始化方法)public void initBean() {System.out.println("3.初始化Bean - 执行自定义初始化方法");}// 第五步:销毁Bean(销毁方法)public void destroyBean() {System.out.println("5.销毁Bean - 执行自定义销毁方法");}
}

XML配置详解

<bean id="userBean" class="com.powernode.spring6.bean.User" init-method="initBean"      <!-- 指定初始化方法 -->destroy-method="destroyBean" <!-- 指定销毁方法 -->lazy-init="false">          <!-- 是否延迟初始化,默认false --><property name="name" value="zhangsan"/> <!-- 属性注入 -->
</bean>

测试代码与执行流程

public class BeanLifecycleTest {@Testpublic void testLifecycle() {// 创建并初始化Spring容器,加载配置,实例化并初始化BeanApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");// 从容器获取Bean实例(单例Bean在容器启动时已创建完毕)User userBean = applicationContext.getBean("userBean", User.class);// 第四步:使用BeanSystem.out.println("4.使用Bean - " + userBean.getName());// 关闭容器,触发Bean销毁过程// 注意:只有正常关闭spring容器才会执行销毁方法ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;context.close();}
}

关键注意事项

  1. 销毁方法的调用条件:只有通过context.close()context.registerShutdownHook()正常关闭Spring容器时,Bean的销毁方法才会被调用
  2. 容器关闭方法:只有ConfigurableApplicationContext的实现类(如ClassPathXmlApplicationContext)才提供close()方法
  3. 方法指定:在XML配置中通过init-method属性指定初始化方法,destroy-method属性指定销毁方法
  4. 作用域影响:对于prototype作用域的Bean,Spring不会调用其销毁方法

8.4 Bean生命周期的7个阶段(加入Bean后处理器)

Spring提供了强大的扩展机制,通过实现BeanPostProcessor接口,可以在Bean初始化前后插入自定义逻辑,将生命周期扩展为7个阶段:

  1. 实例化Bean:创建Bean实例
  2. 属性赋值:依赖注入
  3. BeanPostProcessor前置处理:执行postProcessBeforeInitialization方法
  4. 初始化Bean:执行初始化方法
  5. BeanPostProcessor后置处理:执行postProcessAfterInitialization方法
  6. 使用Bean:应用程序使用Bean
  7. 销毁Bean:执行销毁方法

Bean后处理器详解

/*** Bean后处理器示例 - 在初始化前后添加自定义逻辑*/
public class LogBeanPostProcessor implements BeanPostProcessor {/*** 在Bean初始化方法之前执行* @param bean 原始Bean实例* @param beanName Bean的名称* @return 处理后的Bean实例(可以是原始Bean或包装过的Bean)*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("Bean后处理器的before方法执行:" + beanName + " 即将初始化");// 这里可以修改Bean属性、添加通用逻辑等return bean; // 返回原始Bean或修改后的Bean}/*** 在Bean初始化方法之后执行* @param bean 已初始化的Bean实例* @param beanName Bean的名称* @return 最终的Bean实例(可能是原始Bean或包装过的Bean)*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("Bean后处理器的after方法执行:" + beanName + " 已完成初始化");// 这里可以创建代理、添加额外功能等return bean; // 返回原始Bean或包装后的Bean}
}

配置Bean后处理器

<!-- 配置Bean后处理器 - 无需指定id属性这个后处理器将作用于当前配置文件中所有的bean
-->
<bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/>

Bean后处理器的应用场景

  1. AOP实现:Spring AOP就是通过BeanPostProcessor和动态代理实现的
  2. 注解处理:处理Bean上的注解,如@Autowired@Value
  3. 自定义检查:验证Bean的必要属性是否设置
  4. 包装增强:给Bean添加额外功能,如缓存、事务等

8.5 Bean生命周期的10个阶段(完整生命周期)

通过Spring源码分析,一个Bean的完整生命周期实际上可以更精细地划分为10个阶段:

  1. 实例化Bean:调用构造方法创建对象
  2. 属性赋值:进行依赖注入
  3. 执行Aware接口方法:如果Bean实现了相关Aware接口,调用对应方法
    • BeanNameAware.setBeanName():注入Bean的名称
    • BeanClassLoaderAware.setBeanClassLoader():注入类加载器
    • BeanFactoryAware.setBeanFactory():注入BeanFactory容器
  4. BeanPostProcessor前置处理:调用postProcessBeforeInitialization
  5. InitializingBean初始化:如果Bean实现了InitializingBean接口,调用afterPropertiesSet()方法
  6. 自定义初始化方法:调用init-method指定的方法
  7. BeanPostProcessor后置处理:调用postProcessAfterInitialization
  8. 使用Bean:Bean可以被应用程序使用
  9. DisposableBean销毁:如果Bean实现了DisposableBean接口,调用destroy()方法
  10. 自定义销毁方法:调用destroy-method指定的方法

实现10步生命周期的完整Bean

public class CompleteLifecycleBean implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {private String property;// 步骤1: 实例化Beanpublic CompleteLifecycleBean() {System.out.println("1. 实例化Bean: 构造方法执行");}// 步骤2: 属性赋值public void setProperty(String property) {this.property = property;System.out.println("2. 属性赋值: " + property);}// 步骤3: Aware方法 - 注入Bean名称@Overridepublic void setBeanName(String name) {System.out.println("3. Aware接口: 注入Bean名称 - " + name);}// 步骤3: Aware方法 - 注入ClassLoader@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {System.out.println("3. Aware接口: 注入ClassLoader");}// 步骤3: Aware方法 - 注入BeanFactory@Overridepublic void setBeanFactory(BeanFactory beanFactory) {System.out.println("3. Aware接口: 注入BeanFactory");}// 步骤5: InitializingBean接口方法@Overridepublic void afterPropertiesSet() {System.out.println("5. InitializingBean: 执行afterPropertiesSet方法");}// 步骤6: 自定义初始化方法public void customInit() {System.out.println("6. 自定义初始化: 执行init-method指定的方法");}// 步骤9: DisposableBean接口方法@Overridepublic void destroy() {System.out.println("9. DisposableBean: 执行destroy方法");}// 步骤10: 自定义销毁方法public void customDestroy() {System.out.println("10. 自定义销毁: 执行destroy-method指定的方法");}
}

执行顺序的关键发现

  1. 初始化顺序InitializingBean.afterPropertiesSet()方法先执行,然后才是init-method指定的自定义初始化方法
  2. 销毁顺序DisposableBean.destroy()方法先执行,然后才是destroy-method指定的自定义销毁方法
  3. Aware接口顺序:通常按照BeanNameAwareBeanClassLoaderAwareBeanFactoryAware的顺序执行

8.6 不同作用域Bean的生命周期管理差异

Spring对不同作用域的Bean采用不同的生命周期管理策略,这直接影响到开发者如何处理资源释放:

1. singleton作用域(默认)

  • 特点

    • 每个Spring容器只创建一个Bean实例
    • Spring容器负责完整的生命周期管理
    • 容器启动时创建,容器关闭时销毁
  • 生命周期管理

    • 完整经历从实例化到销毁的10个阶段
    • 销毁方法在容器关闭时自动调用
    • 适合共享状态且线程安全的对象

2. prototype作用域

  • 特点

    • 每次请求都创建新的Bean实例
    • Spring容器只负责创建,不负责销毁
    • 创建后交由客户端代码管理
  • 生命周期管理

    • 只经历从实例化到初始化后的前7个阶段
    • 销毁方法不会被Spring容器调用
    • 需要客户端代码手动清理资源
    • 适合存储请求相关状态的对象

示例配置

<!-- 单例Bean配置(默认) -->
<bean id="singletonBean" class="com.example.SingletonBean"init-method="init" destroy-method="destroy">
</bean><!-- 原型Bean配置 -->
<bean id="prototypeBean" class="com.example.PrototypeBean"scope="prototype" init-method="init" destroy-method="destroy">
</bean>

原型Bean资源释放解决方案

对于prototype作用域的Bean,如果需要在不使用时释放资源,可以:

  1. 使用自定义的BeanPostProcessor跟踪创建的prototype Bean
  2. 实现类似Java中try-with-resources的模式自行管理资源
  3. 对于重量级资源,考虑改用单例模式并确保线程安全

8.7 手动注册Bean到Spring容器

在某些场景下,需要将手动创建的对象或第三方库的对象纳入Spring容器管理,可以使用Spring提供的Bean注册API:

/*** 手动将已创建的对象注册到Spring容器示例*/
public class ManualBeanRegistrationExample {@Testpublic void testManualBeanRegistration() {// 1. 手动创建对象User user = new User();user.setName("手动创建的用户");// 2. 创建可编程的BeanFactoryDefaultListableBeanFactory factory = new DefaultListableBeanFactory();// 3. 将对象注册为单例Beanfactory.registerSingleton("userBean", user);// 4. 从Spring容器获取BeanUser retrievedBean = factory.getBean("userBean", User.class);// 5. 验证是同一个对象System.out.println("是否为同一对象: " + (user == retrievedBean));}
}

编程式Bean注册的应用场景

  1. 集成第三方库:将无法通过Spring自动管理的组件纳入容器
  2. 动态Bean创建:根据运行时条件创建不同的Bean实现
  3. 测试环境:在单元测试中注册Mock对象
  4. 插件系统:动态加载和注册插件组件

其他注册方式

除了registerSingleton外,Spring还提供了多种注册Bean的方式:

// 注册Bean定义
BeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName("com.example.MyBean");
factory.registerBeanDefinition("myBean", beanDefinition);// 通过FactoryBean注册
factory.registerSingleton("myFactoryBean", new MyFactoryBean());

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词