Spring系列之Bean的作用域、生命周期

概述

作用域

Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式。Spring Bean的常见作用域,后3种作用域,只适用于Spring MVC框架:

  • singleton:单例作用域
  • prototype:原型作用域(多例作用域)
  • request:请求作用域
  • session:会话作用域
  • application:也叫global,全局作用域

singleton

(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.

描述:该作用域下的 Bean 在 IoC 容器中只存在一个实例:获取 Bean(即通过 applicationContext.getBean等方法获取)及装配 Bean(即通过 @Autowired 注入)都是同一个对象。
场景:通常无状态的 Bean 使用该作用域。无状态表示 Bean 对象的属性状态不需要更新。
备注:Spring 默认选择该作用域。singleton 单例作用域,就表示 Bean 在整个 Spring 中只有一份,它是全局共享的,当有人修改了这个值之后,那么另一个人读取到的就是被修改后的值。

prototype

Scopes a single bean definition to any number of object instances.

描述:每次对该作用域下的 Bean 的请求都会创建新的实例:获取 Bean(即通过applicationContext.getBean等方法获取)及装配 Bean(即通过 @Autowired 注入)都是新的对象实例。
场景:通常有状态的 Bean 使用该作用域。

request

Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.

描述:每次 Http 请求会创建新的 Bean 实例,类似于 prototype。
场景:一次 Http 的请求和响应的共享 Bean。
备注:限定 Spring MVC 框架中使用。

session

Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.

描述:在一个 Http Session 中,定义一个 Bean 实例。
场景:用户会话的共享 Bean, 比如:记录一个用户的登陆信息。
备注:限定 Spring MVC 框架中使用。

application

Scopes a single bean definition to the lifecycle of a global HTTP Session. Typically only valid when used in a portlet context. Only valid in the context of a web-aware Spring ApplicationContext.

描述:在一个 Http Servlet Context 中,定义一个 Bean 实例。
场景:Web 应用的上下文信息,比如:记录一个应用的共享信息。
备注:限定 Spring MVC 框架中使用。

websocket:bean被定义为在websocket的生命周期中复用一个单例对象,不太常见。

可通过@Scope设置Bean的作用域:

@Scope("prototype")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Scope(WebApplicationContext.SCOPE_APPLICATION)

singleton & prototype

如果一个bean被声明为单例的时候,在处理多次请求的时候在Spring容器里只实例化出一个bean,后续的请求都公用这个对象,这个对象会保存在一个map里面。当有请求来的时候会先从缓存(map)里查看有没有,有的话直接使用这个对象,没有的话才实例化一个新的对象,所以这是个单例的。但是对于原型(prototype)bean来说当每次请求来的时候直接实例化新的bean,没有缓存以及从缓存查的过程。

AbstractBeanFactory.doGetBean()源码找到证据:

// Create bean instance.
if (mbd.isSingleton()) {
	sharedInstance = getSingleton(beanName, () -> {
		try {
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			// Explicitly remove instance from singleton cache: It might have been put there
			// eagerly by the creation process, to allow for circular reference resolution.
			// Also remove any beans that received a temporary reference to the bean.
			destroySingleton(beanName);
			throw ex;
		}
	});
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
	// It's a prototype -> create a new instance.
	Object prototypeInstance = null;
	try {
		beforePrototypeCreation(beanName);
		prototypeInstance = createBean(beanName, mbd, args);
	}
	finally {
		afterPrototypeCreation(beanName);
	}
	bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
	String scopeName = mbd.getScope();
	if (!StringUtils.hasLength(scopeName)) {
		throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
	}
	Scope scope = this.scopes.get(scopeName);
	if (scope == null) {
		throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
	}
	try {
		Object scopedInstance = scope.get(beanName, () -> {
			beforePrototypeCreation(beanName);
			try {
				return createBean(beanName, mbd, args);
			}
			finally {
				afterPrototypeCreation(beanName);
			}
		});
		bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
	}
	catch (IllegalStateException ex) {
		throw new BeanCreationException(beanName,
				"Scope '" + scopeName + "' is not active for the current thread; consider " +
				"defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex);
	}
}

源码跟进去,DefaultSingletonBeanRegistry里面定义有如下Map:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

singleton bean优势,也是Spring为啥默认把bean设计成单例:

  1. 减少新生成实例的消耗
    新生成实例消耗包括两方面,第一,spring会通过反射或者cglib来生成bean实例这都是耗性能的操作,其次给对象分配内存也会涉及复杂算法。
  2. 减少JVM垃圾回收
    由于不会给每个请求都新生成bean实例,所以自然回收的对象少了。
  3. 可以快速获取到bean
    因为单例的获取bean操作除了第一次生成之外其余的都是从缓存里获取的所以很快。

劣势
不能保证线程安全。所有请求都共享一个bean实例,有状态bean在并发场景下有线程安全问题。原型bean,因为给每个请求都新创建实例,则不会有这样问题(但也有例外,如被单例bean依赖)。

生命周期

Bean的生命周期是指:Bean 在 Spring(IoC)中从创建到销毁的整个过程,主要包含5部分:

  • 实例化:为 Bean 分配内存空间;
  • 设置属性:将当前类依赖的 Bean 属性,进行注入和装配;
  • 初始化:
    • 执行各种通知;
    • 执行初始化的前置方法;
    • 执行初始化方法;
    • 执行初始化的后置方法。
  • 使用 Bean:在程序中使用 Bean 对象;
  • 销毁 Bean:将 Bean 对象进行销毁操作。

完整涉及到接口和类的详细版的生命周期:

  • Bean容器找到配置文件中Spring Bean的定义
  • Bean容器利用Java Reflection API创建一个Bean的实例
  • 如果涉及到一些属性值,利用set方法设置一些属性值
  • 如果Bean实现BeanNameAware接口,调用setBeanName()方法,传入Bean的名字
  • 如果Bean实现BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例
  • 如果Bean实现了BeanFactoryAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例
  • 与上面的类似,如果实现其他*Aware接口,就调用相应的方法
  • 如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法
  • 如果Bean实现InitializingBean接口,执行afterPropertiesSet()方法
  • 如果Bean在配置文件中的定义包含init-method属性,执行指定的方法
  • 如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessAfterInitialization()方法
  • 当要销毁Bean时,如果Bean实现DisposableBean接口,执行destroy()方法
  • 当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法

实例化只是给 Bean 分配内存空间,而初始化则是将程序的执行权,从系统级别转换到用户级别,并开始执行用户业务代码。

singleton bean被定义为一个单例对象,该对象的生命周期是与Spring IOC容器一致的(但在第一次被注入时才会创建)

方法:
setup,在容器加载bean时被调用,teardown在容器卸载类时被调用。bean标签有两个重要属性(init-method和destroy-method),可用于定制初始化和注销方法,有相应的注解(@PostConstruct和@PreDestroy)。

Aware

有时需要在Bean的初始化中使用Spring框架自身的一些对象来执行一些操作,如获取ServletContext的参数,ApplicaitionContext中的BeanDefinition的名字,Bean在容器中的名字等。为了让Bean可以获取到框架自身的一些对象,Spring提供一组名为*Aware的接口。
这些接口均继承于org.springframework.beans.factory.Aware标记接口,并提供一个将由Bean实现的set*方法,Spring通过基于setter的依赖注入方式使相应的对象可以被Bean使用。这些接口是利用观察者模式实现的,类似于servlet listeners。
Aware接口:

  • ApplicationContextAware:获得ApplicationContext对象,可以用来获取所有Bean definition的名字
  • BeanFactoryAware:获得BeanFactory对象,可以用来检测Bean的作用域
  • BeanNameAware:获得Bean在配置文件中定义的名字
  • ResourceLoaderAware:获得ResourceLoader对象,可以获得classpath中某个文件
  • ServletContextAware:在一个MVC应用中可以获取ServletContext对象,可以读取context中的参数
  • ServletConfigAware:在一个MVC应用中可以获取ServletConfig对象,可以读取config中的参数

代码:

@Slf4j
public class DemoService implements ApplicationContextAware,
        ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware,
        BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware{
    @Override
    public void setBeanClassLoader(ClassLoader classLoader){
        log.info("执行setBeanClassLoader,ClassLoader Name = " + classLoader.getClass().getName());
    }
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("执行setBeanFactory,setBeanFactory:: giraffe bean singleton=" +  beanFactory.isSingleton("giraffeService"));
    }
    @Override
    public void setBeanName(String s) {
        log.info("执行setBeanName:: Bean Name defined in context="    + s);
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("执行setApplicationContext:: Bean Definition Names=" + Arrays.toString(applicationContext.getBeanDefinitionNames()));
    }
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        log.info("执行setApplicationEventPublisher");
    } 
    @Override
    public void setEnvironment(Environment environment) {
        log.info("执行setEnvironment");
    }
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        Resource resource = resourceLoader.getResource("classpath:spring-beans.xml");
        log.info("执行setResourceLoader:: Resource File Name="  + resource.getFilename()); 
    }
    @Override
    public void setImportMetadata(AnnotationMetadata annotationMetadata) {
        log.info("执行setImportMetadata");
    }
}

BeanPostProcessor
上面的*Aware接口是针对某个实现这些接口的Bean定制初始化的过程,Spring同样可以针对容器中的所有Bean,或者某些Bean定制初始化过程,只需提供一个实现BeanPostProcessor接口的类即可。 该接口中包含两个方法,postProcessBeforeInitialization和postProcessAfterInitialization。 postProcessBeforeInitialization方法会在容器中的Bean初始化之前执行, postProcessAfterInitialization方法在容器中的Bean初始化之后执行:

@Slf4j
@Componet
public class DemoBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        log.info("执行BeanPostProcessor的postProcessBeforeInitialization方法,beanName=" + beanName);
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("执行BeanPostProcessor的postProcessAfterInitialization方法,beanName=" + beanName);
        return bean;
    }
}

问题:
能不能先执行初始化再执行设置属性呢?步骤2和3的执行顺序交换一下?

参考

posted @   johnny233  阅读(65)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示