跳至主要內容

Spring-SpringBean

holic-x...大约 11 分钟JAVA框架

Spring-SpringBean

学习核心

  • SpringBean概念基础核心
  • 注入Bean的方式、注解有哪些?
  • 注解的区别对比:
    • @Component VS @Bean
    • @Autowire VS @Resource
  • Bean的作用域

学习资料

SpringBean概念核心

1.BeanDefinition

Spring IoC 容器本身,并不能识别配置的元数据。为此,要将这些配置信息转为 Spring 能识别的格式——BeanDefinition 对象

BeanDefinition 是 Spring 中定义 Bean 的配置元信息接口,它包含:

  • Bean 类名
  • Bean 行为配置元素(作用域、自动绑定的模式、生命周期回调等)
  • 其他 Bean 引用,也可称为合作者(Collaborators)或依赖(Dependencies)
  • 配置设置(Bean 属性(Properties)等)

BeanDefinition元信息

BeanDefinition 元信息如下:

属性(Property)说明
Classopen in new window全类名(必须是具体类,不能用抽象类或接口)
Nameopen in new windowBean 的名称或者 ID
Scopeopen in new windowBean 的作用域(如:singletonprototype 等)
Constructor argumentsopen in new windowBean 构造器参数(用于依赖注入)
Propertiesopen in new windowBean 属性设置(用于依赖注入)
Autowiring modeopen in new windowBean 自动绑定模式(如:通过名称 byName)
Lazy initialization modeopen in new windowBean 延迟初始化模式(延迟和非延迟)
Initialization methodopen in new windowBean 初始化回调方法名称
Destruction methodopen in new windowBean 销毁回调方法名称

BeanDefinition构建

构建方式:

  • 通过BeanDefinitionBuilder
  • 通过AbstractBeanDefinition以及派生类

2.SpringBean命名

命名规则

​ 每个 Bean 拥有一个或多个标识符(identifiers),这些标识符在 Bean 所在的容器必须是唯一的。通常,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名(Alias)来扩充。

​ 在基于 XML 的配置元信息中,开发人员可以使用 id 属性、name 属性或来指定 Bean 标识符。通常,Bean 的标识符由字母组成,允许出现特殊字符。如果要想引入 Bean 的别名的话,可在 name 属性使用半角逗号(“,”)或分号(“;”) 来间隔。

​ Spring 中,为 Bean 指定 idname 属性不是必须的。如果不指定,Spring 会自动为 Bean 分配一个唯一的名称。尽管 Bean 的命名没有限制,不过官方建议采用驼峰命名法来命名 Bean

SpringBean命名生成器

Spring 提供了两种 Spring Bean 命名生成器:

  • DefaultBeanNameGenerator:默认通用 BeanNameGenerator 实现。
  • AnnotationBeanNameGenerator:基于注解扫描的 BeanNameGenerator 实现。
public interface BeanNameGenerator {
   String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
}

Spring Bean别名

Spring 支持通过 <alias> 属性为 Bean 设置别名

Bean 别名(Alias)的作用:

  • 复用现有的 BeanDefinition
  • 更具有场景化的命名方法,比如:
    • <alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
    • <alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
<bean id="user" class="io.github.dunwu.spring.core.bean.entity.person.User">
  <!-- 属性略 -->
</bean>
<alias name="user" alias="aliasUser" />

SpringBean生命周期

核心概念:实例化 =》属性赋值 =》 初始化 =》 使用 =》 销毁(记忆核心,展开扩展)

阶段说明
实例化(Instantiation)Spring根据配置文件、注解等方式加载并创建Bean实例,将其存储到容器中
属性赋值(Populate Properties)Spring将Bean的属性值从配置文件、注解等方式加载并注入到Bean实例
初始化(Initialization)Spring调用Bean实例的init-method方法,完成一些初始化操作(建立数据库连接等)
使用(In User)Bean实例初始化完成可以供程序正常调用
销毁(Destruction)Spring调用destory-method方法,完成资源释放和清理操作(例如关闭数据库链接)

​ 具体的实现方式可以通过实现BeanPostProcessor和BeanFactoryPostProcessor接口来进行扩展

​ 其中,BeanPostProcessor接口定义了两个方法postProcessBeforelnitialization和postProcessAfterlnitialization,分别在Bean的初始化前后被调用,用于扩展Bean初始化的过程

​ BeanFactoryPostProcessor 接口则定义了一个方法postProcessBeanFactory,用于在Bean工厂实例化Bean定义后对其进行修改

扩展版

​ Spring的通过上述阶段进行管理,开发者可以通过实现相关接口和方法来扩展和定制Bean的创建和销毁过程,以满足各种业务需求。本质上就是扩展接口作相应的方法增强。

  • 在Bean初始化操作前后扩展Bean的初始化过程:实现BeanPostProcessor接口的接口方法

    • postProcessBeforelnitialization:Bean初始化前执行
    • postProcessAfterlnitialization:Bean初始化后执行
  • 扩展Bean的销毁过程

    • 实现DispostbleBean接口,Spring会调用其destory方法(在 Bean 实例销毁前执行的方法)(作用与在配置文件中对 Bean 使用 destory-method 属性的作用一样)

    image-20240605104946490

​ 理解记忆,先掌握流程核心,然后细化实现:

  • 流程核心:实例化、属性赋值、初始化、使用、销毁
  • 细化实现:
    • 【初始化】阶段涉及到一些扩展项(例如对初始化方法的一些增强)
    • 【销毁】阶段:注册相关销毁回调接口,然后通过DisposableBeandestory-method 进行销毁

SpringBean注册

​ SpringBean的注册实际上就是将BeanDefinition注册到IOC容器中(被IOC容器管理),传统有3种注册方式

1.XML配置

方式1:XML配置(XML配置元信息)

​ XML配置是Spring的传统配置方式,通过<bean>标签配置元数据内容。其缺点在于如果JavaBean过多,则配置文件会让人眼花缭乱

​ 使用时可通过ApplicationContext加载配置文件并使用

参考配置:创建HelloService接口、HelloServiceImpl接口实现

【1】接口定义

public interface HelloServie{
	public void sayHello();
}

public class HelloServieImpl implements HelloService{
	private String info;	
	public String getInfo() {
		return info;
	}
	public void setInfo(String info) {
		this.info = info;
	}
	@Override
	public void sayHello() {
		System.out.println("hello"+info);
	}
}

【2】构建配置文件:resources/spring/applicationContext.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">
	<!--1.快速入门 ========================================================= -->
	<!-- - bean:就是定义一个具体的类 可以是一个对象 id 属性 就是一个合法的标识符 class 是当前类的全名称 -->
	<bean id="helloService" class="com.noob.spring.a_quickstart.HelloServiceImpl">
		<property name="info" value="hello"></property>
	</bean>
	<!--1.end快速入门 ====================================================== -->
</beans>

【3】使用对象(区分传统方式和Spring方式)

public class HelloServiceTest {
  public static void main(String[] args) {
    /**
		 * 1.传统方式
		 */
    HelloServiceImpl helloServiceImpl = new HelloServiceImpl();
    helloServiceImpl.setInfo("hello普通测试......");
    helloServiceImpl.sayHello();
    /**
		 * 2.spring方式(代码中加载配置文件并引用对象)
		 * 需要在核心配置文件中先配置相关内容
		 * Spring中使用工厂 +反射 +配置文件的方式 实例化这个对象  
		 */
    ApplicationContext applicationContext =new ClassPathXmlApplicationContext("spring/applicationContext.xml");
    HelloService  service =(HelloService) applicationContext.getBean("helloService");
    service.sayHello();
  }
}

2.注解配置

方式2:注解配置(注解配置元信息)

​ 通过使用@Bean@Component@Import等注解注册SpringBean

​ 类似地还有@Repository@Service@Controller(基于代码分层引入)

@Bean VS @Component

结合使用方式、作用范围、灵活性等方面阐述

  • @Component作用于@Bean作用于方法

  • 使用方式

    • @Component结合@ComponentScan使用:从指定扫描路径标识并自动装配到Spring的bean容器中;
    • @Bean则是在标有该注解的方法中定义产生bean,并告诉Spring这是某个类的实例,当需要用的时候还给我
  • 灵活性

    • @Bean注解比@Component注解的自定义性更强。一些场景只能通过@Bean注解来注册bean(例如引用第三方库中的类需要装配到Spring容器中时)

参考样例

# @Bean样例
@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }

}

# 上述配置等价于xml配置
<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
      
      
# @Bean作用于方法样例
@Bean
public OneService getService(status) {
    case (status)  {
        when 1:
                return new serviceImpl1();
        when 2:
                return new serviceImpl2();
        when 3:
                return new serviceImpl3();
    }
}

3.Java API配置

方式3:Java API配置元信息

  • 命名方式:BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
  • 非命名方式:BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition,BeanDefinitionRegistry)
  • 配置类方式:AnnotatedBeanDefinitionReader#register(Class...)

SpringBean实例化

Spring Bean 实例化方式:

  • 常规方式

    • 通过构造器(配置元信息:XML、Java 注解和 Java API)
    • 通过静态方法(配置元信息:XML、Java 注解和 Java API)
    • 通过 Bean 工厂方法(配置元信息:XML、Java 注解和 Java API)
    • 通过 FactoryBean(配置元信息:XML、Java 注解和 Java API)
  • 特殊方式

    • 通过 ServiceLoaderFactoryBean(配置元信息:XML、Java 注解和 Java API )
    • 通过 AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
    • 通过 BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)

SpringBean初始化和销毁

使用 @PostConstruct@PreDestroy 注解分别指定相应的初始化方法和销毁方法

实现 InitializingBean 接口的 afterPropertiesSet() 方法来编写初始化方法;实现 DisposableBean 接口的 destroy() 方法来编写销毁方法

  • InitializingBean 接口包含一个 afterPropertiesSet 方法,可以通过实现该接口,然后在这个方法中编写初始化逻辑
  • DisposableBean接口包含一个 destory 方法,可以通过实现该接口,然后在这个方法中编写销毁逻辑

自定义初始化方法

  • XML 配置:<bean init-method="init" destroy="destroy" ... />
  • Java 注解:@Bean(initMethod = "init", destroyMethod = "destroy")
  • Java API:AbstractBeanDefinition#setInitMethodName(String)AbstractBeanDefinition#setDestroyMethodName(String) 分别定义初始化和销毁方法

注意:如果同时存在,执行顺序会按照序列执行

Bean 的延迟初始化

  • xml 方式:<bean lazy-init="true" ... />
  • 注解方式:@Lazy

SpringBean初始化前后的方法增强:实现BeanPostProcessor 接口的postProcessBeforeInitializationpostProcessAfterInitialization

  • postProcessBeforeInitialization 在组件的初始化方法调用之前执行
  • postProcessAfterInitialization 在组件的初始化方法调用之后执行

SpringBean注入

​ 区分注册概念:注册可以理解为定义(将对象注册到SpringIOC容器中,交由SpringIOC管理),注入理解为要引用对象的时候实例化

注入SpringBean的注解

Spring 内置的 @Autowired 以及 JDK 内置的 @Resource@Inject 都可以用于注入 Bean,其中@Autowired@Resource使用的比较多一些

AnnotationPackageSource
@Autowiredorg.springframework.bean.factorySpring 2.5+
@Resourcejavax.annotationJava JSR-250
@Injectjavax.injectJava JSR-330

@Autowired VS @Resource

结合注解归属包、注入方式、使用范围

分类\注解@Autowired@Resource
注解归属包Spring 内置的注解JDK 提供的注解
注入方式默认的注入方式为byType(根据类型进行匹配)
优先根据接口类型去匹配并注入 Bean (接口的实现类)
默认注入方式为 byName
如果无法通过名称匹配到对应的 Bean 的话,注入方式会变为byType
使用范围支持在构造函数、方法、字段和参数上使用主要用于字段和方法上的注入,不支持在构造函数或参数上使用

byType方式可能存在的问题:当一个接口存在多个实现类的话,byType这种方式就无法正确注入对象了,因为此时Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。

这种情况下,注入方式会变为 byName(根据名称进行匹配),这个名称通常就是类名(首字母小写)

【1】@Autowired@Qualifier结合使用

// 场景:定义SsmService接口、接口实现类:SsmServiceImpl1、SsmServiceImpl2
// 因为SmsService有多个接口实现类,因此通过byType方式会获取到多个选择,这种情况下注入方式会转为byName方式

// 案例1:无法通过byType、byName获取(byType会发现有多个选择不知道用哪个转而使用byName,而此时对应实现默认名称为首字母小写应该是ssmServiceImpl1、ssmServiceImpl2)
@Autowired
private SmsService smsService;

// 案例2:正确注入(byType =》byName,此处根据变量名称匹配到了ssmServiceImpl1则正常注入)
@Autowired
private SmsService smsServiceImpl1;

// 案例3:正确注入,结合@Qualifier显式指定名称(而不依赖于变量的名称)
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;

【2】@Resource使用

@Resource常用属性:name(名称)、type(类型)

  • 如果仅指定name属性则注入方式为byName
  • 如果仅指定type属性则注入方式为byType
  • 如果指定name和type,注入方式为byName+byType(一般不建议)
// 报错,byName 和 byType 都无法匹配到 bean(默认先用byName无法找到,则转byTpe发现有多个选择则不符合)
@Resource
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean(默认先用byName发现可以匹配到)
@Resource
private SmsService smsServiceImpl1;
// 正确注入 SmsServiceImpl1 对象对应的 bean(比较推荐这种方式)(这种方式明确指定name属性)
@Resource(name = "smsServiceImpl1")
private SmsService smsService;

SpringBean垃圾回收

SpringBean垃圾回收步骤

  • 关闭Spring容器(应用上下文)
  • 执行GC操作
  • SpringBean覆盖的finalize()方法被回调

SpringBean作用范围

spring支持的bean作用域

  • singleton(单例、默认):使用该属性定义Bean时,IOC容器仅创建一个Bean实例,IOC容器每次返回的是同一个Bean实例
  • prototype:使用该属性定义Bean时,IOC容器可以创建多个Bean实例,每次返回的都是一个新的实例
  • request(仅Web应用可用):该属性仅对HTTP请求产生作用,使用该属性定义Bean时,每次HTTP请求都会创建一个新的Bean,适用于WebApplicationcontext环境
  • session(仅Web应用可用):该属性仅用于HTTP Session,会话级别的对象,同一个Session共享一个Bean实例。不同Session使用不同的实例
  • application/global-session(仅Web应用可用):该属性仅用于HTTP Session,同session作用域不同的是,所有的Session共享一个Bean实例
  • websocket(仅Web应用可用):每一次 WebSocket 会话产生一个新的 bean

其中request、session、global-session不是基础的Spring程序支持的,而是基于Web程序开发。Spring会整合SpringMVC进行web开发

Bean作用域的配置

【1】方式1:xml方式

<bean id="..." class="..." scope="singleton"></bean>

【2】方式2:注解方式

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
    return new Person();
}
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3