后端面试每日一题 聊聊Spring

  • (3)request 作用域:每次 Http 请求时都会创建一个新的 Bean,该作用域仅适应于 WebApplicationContext 环境。

XML 的配置方式如下:

Java 注解的配置方式如下:

@Scope(WebApplicationContext.SCOPE_REQUEST)

或是:

@RequestScope(WebApplicationContext.SCOPE_REQUEST)

  • (4)session 作用域:同一个 Http Session 共享一个 Bean 对象,不同的 Session 拥有不同的 Bean 对象,仅适用于 WebApplicationContext 环境。

XML 的配置方式如下:

Java 注解的配置方式如下:

@Scope(WebApplicationContext.SCOPE_SESSION)

或是:

@RequestScope(WebApplicationContext.SCOPE_SESSION)

  • (5)application 作用域:全局的 Web 作用域,类似于 Servlet 中的 Application。

XML 的配置方式如下:

Java 注解的配置方式如下:

@Scope(WebApplicationContext.SCOPE_APPLICATION)

或是:

@RequestScope(WebApplicationContext.SCOPE_APPLICATION)

Bean 生命周期

对于 Spring Bean 来说,并不是启动阶段就会触发 Bean 的实例化,只有当客户端通过显式或者隐式的方式调用 BeanFactory 的 getBean() 方法时,它才会触发该类的实例化方法。当然对于 BeanFactory 来说,也不是所有的 getBean() 方法都会实例化 Bean 对象,例如作用域为 singleton 时,只会在第一次,实例化该 Bean 对象,之后会直接返回该对象。但如果使用的是 ApplicationContext 容器,则会在该容器启动的时候,立即调用注册到该容器所有 Bean 的实例化方法。

getBean() 既然是 Bean 对象的入口,我们就先从这个方法说起,getBean() 方法是属于 BeanFactory 接口的,它的真正实现是 AbstractAutowireCapableBeanFactory 的 createBean() 方法,而 createBean() 是通过 doCreateBean() 来实现的,具体源码实现如下:

@Override

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)

throws BeanCreationException {

if (logger.isTraceEnabled()) {

logger.trace(“Creating instance of bean '” + beanName + “'”);

}

RootBeanDefinition mbdToUse = mbd;

// 确定并加载 Bean 的 class

Class<?> resolvedClass = resolveBeanClass(mbd, beanName);

if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {

mbdToUse = new RootBeanDefinition(mbd);

mbdToUse.setBeanClass(resolvedClass);

}

// 验证以及准备需要覆盖的方法

try {

mbdToUse.prepareMethodOverrides();

}

catch (BeanDefinitionValidationException ex) {

throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),

beanName, “Validation of method overrides failed”, ex);

}

try {

// 给BeanPostProcessors 一个机会来返回代理对象来代替真正的 Bean 实例,在这里实现创建代理对象功能

Object bean = resolveBeforeInstantiation(beanName, mbdToUse);

if (bean != null) {

return bean;

}

}

catch (Throwable ex) {

throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,

“BeanPostProcessor before instantiation of bean failed”, ex);

}

try {

// 创建 Bean

Object beanInstance = doCreateBean(beanName, mbdToUse, args);

if (logger.isTraceEnabled()) {

logger.trace(“Finished creating instance of bean '” + beanName + “'”);

}

return beanInstance;

}

catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {

throw ex;

}

catch (Throwable ex) {

throw new BeanCreationException(

mbdToUse.getResourceDescription(), beanName, “Unexpected exception during bean creation”, ex);

}

}

doCreateBean 源码如下:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)

throws BeanCreationException {

// 实例化 bean,BeanWrapper 对象提供了设置和获取属性值的功能

BeanWrapper instanceWrapper = null;

// 如果 RootBeanDefinition 是单例,则移除未完成的 FactoryBean 实例的缓存

if (mbd.isSingleton()) {

instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);

}

if (instanceWrapper == null) {

// 创建 bean 实例

instanceWrapper = createBeanInstance(beanName, mbd, args);

}

// 获取 BeanWrapper 中封装的 Object 对象,其实就是 bean 对象的实例

final Object bean = instanceWrapper.getWrappedInstance();

// 获取 BeanWrapper 中封装 bean 的 Class

Class<?> beanType = instanceWrapper.getWrappedClass();

if (beanType != NullBean.class) {

mbd.resolvedTargetType = beanType;

}

// 应用 MergedBeanDefinitionPostProcessor 后处理器,合并 bean 的定义信息

// Autowire 等注解信息就是在这一步完成预解析,并且将注解需要的信息放入缓存

synchronized (mbd.postProcessingLock) {

if (!mbd.postProcessed) {

try {

applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);

} catch (Throwable ex) {

throw new BeanCreationException(mbd.getResourceDescription(), beanName,

“Post-processing of merged bean definition failed”, ex);

}

mbd.postProcessed = true;

}

}

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&

isSingletonCurrentlyInCreation(beanName));

if (earlySingletonExposure) {

if (logger.isTraceEnabled()) {

logger.trace(“Eagerly caching bean '” + beanName +

“’ to allow for resolving potential circular references”);

}

// 为了避免循环依赖,在 bean 初始化完成前,就将创建 bean 实例的 ObjectFactory 放入工厂缓存(singletonFactories)

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

}

// 对 bean 属性进行填充

Object exposedObject = bean;

try {

populateBean(beanName, mbd, instanceWrapper);

// 调用初始化方法,如 init-method 注入 Aware 对象

exposedObject = initializeBean(beanName, exposedObject, mbd);

} catch (Throwable ex) {

if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {

throw (BeanCreationException) ex;

} else {

throw new BeanCreationException(

mbd.getResourceDescription(), beanName, “Initialization of bean failed”, ex);

}

}

if (earlySingletonExposure) {

// 如果存在循环依赖,也就是说该 bean 已经被其他 bean 递归加载过,放入了提早公布的 bean 缓存中

Object earlySingletonReference = getSingleton(beanName, false);

if (earlySingletonReference != null) {

// 如果 exposedObject 没有在 initializeBean 初始化方法中被增强

if (exposedObject == bean) {

exposedObject = earlySingletonReference;

} else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {

// 依赖检测

String[] dependentBeans = getDependentBeans(beanName);

Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);

for (String dependentBean : dependentBeans) {

if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {

actualDependentBeans.add(dependentBean);

}

}

// 如果 actualDependentBeans 不为空,则表示依赖的 bean 并没有被创建完,即存在循环依赖

if (!actualDependentBeans.isEmpty()) {

throw new BeanCurrentlyInCreationException(beanName,

“Bean with name '” + beanName + “’ has been injected into other beans [” +

StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +

"] in its raw version as part of a circular reference, but has eventually been " +

"wrapped. This means that said other beans do not use the final version of the " +

"bean. This is often the result of over-eager type matching - consider using " +

“‘getBeanNamesOfType’ with the ‘allowEagerInit’ flag turned off, for example.”);

}

}

}

}

try {

// 注册 DisposableBean 以便在销毁时调用

registerDisposableBeanIfNecessary(beanName, bean, mbd);

} catch (BeanDefinitionValidationException ex) {

throw new BeanCreationException(

mbd.getResourceDescription(), beanName, “Invalid destruction signature”, ex);

}

return exposedObject;

}

从上述源码中可以看出,在 doCreateBean() 方法中,首先对 Bean 进行了实例化工作,它是通过调用 createBeanInstance() 方法来实现的,该方法返回一个 BeanWrapper 对象。BeanWrapper 对象是 Spring 中一个基础的 Bean 结构接口,说它是基础接口是因为它连基本的属性都没有。

BeanWrapper 接口有一个默认实现类 BeanWrapperImpl,其主要作用是对 Bean 进行填充,比如填充和注入 Bean 的属性等。

当 Spring 完成 Bean 对象实例化并且设置完相关属性和依赖后,则会调用 Bean 的初始化方法 initializeBean(),初始化第一个阶段是检查当前 Bean 对象是否实现了 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 等接口,源码如下:

private void invokeAwareMethods(final String beanName, final Object bean) {

if (bean instanceof Aware) {

if (bean instanceof BeanNameAware) {

((BeanNameAware) bean).setBeanName(beanName);

}

if (bean instanceof BeanClassLoaderAware) {

ClassLoader bcl = getBeanClassLoader();

if (bcl != null) {

((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);

}

}

if (bean instanceof BeanFactoryAware) {

((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);

}

}

}

其中,BeanNameAware 是把 Bean 对象定义的 beanName 设置到当前对象实例中;BeanClassLoaderAware 是将当前 Bean 对象相应的 ClassLoader 注入到当前对象实例中;BeanFactoryAware 是 BeanFactory 容器会将自身注入到当前对象实例中,这样当前对象就会拥有一个 BeanFactory 容器的引用。

初始化第二个阶段则是 BeanPostProcessor 增强处理,它主要是对 Spring 容器提供的 Bean 实例对象进行有效的扩展,允许 Spring 在初始化 Bean 阶段对其进行定制化修改,比如处理标记接口或者为其提供代理实现。

在初始化的前置处理完成之后就会检查和执行 InitializingBean 和 init-method 方法。

InitializingBean 是一个接口,它有一个 afterPropertiesSet() 方法,在 Bean 初始化时会判断当前 Bean 是否实现了 InitializingBean,如果实现了则调用 afterPropertiesSet() 方法,进行初始化工作;然后再检查是否也指定了 init-method,如果指定了则通过反射机制调用指定的 init-method 方法,它的实现源码如下:

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)

throws Throwable {

// 判断当前 Bean 是否实现了 InitializingBean,如果是的话需要调用 afterPropertiesSet()

boolean isInitializingBean = (bean instanceof InitializingBean);

if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod(“afterPropertiesSet”))) {

if (logger.isTraceEnabled()) {

logger.trace(“Invoking afterPropertiesSet() on bean with name '” + beanName + “'”);

}

if (System.getSecurityManager() != null) { // 安全模式

try {

AccessController.doPrivileged((PrivilegedExceptionAction) () -> {

((InitializingBean) bean).afterPropertiesSet(); // 属性初始化

return null;

}, getAccessControlContext());

} catch (PrivilegedActionException pae) {

throw pae.getException();

}

} else {

((InitializingBean) bean).afterPropertiesSet(); // 属性初始化

}

}

// 判断是否指定了 init-method()

if (mbd != null && bean.getClass() != NullBean.class) {

String initMethodName = mbd.getInitMethodName();

if (StringUtils.hasLength(initMethodName) &&

!(isInitializingBean && “afterPropertiesSet”.equals(initMethodName)) &&

!mbd.isExternallyManagedInitMethod(initMethodName)) {

// 利用反射机制执行指定方法

invokeCustomInitMethod(beanName, bean, mbd);

}

}

}

初始化完成之后就可以正常的使用 Bean 对象了,在 Spring 容器关闭时会执行销毁方法,但是 Spring 容器不会自动去调用销毁方法,而是需要我们主动的调用。

如果是 BeanFactory 容器,那么我们需要主动调用 destroySingletons() 方法,通知 BeanFactory 容器去执行相应的销毁方法;如果是 ApplicationContext 容器,那么我们需要主动调用 registerShutdownHook() 方法,告知 ApplicationContext 容器执行相应的销毁方法。

生命周期

img

IoC 和 DI 的理解

IoC(Inversion of Control,翻译为“控制反转”)不是一个具体的技术,而是一种设计思想。与传统控制流相比,IoC 会颠倒控制流,在传统的编程中需要开发者自行创建并销毁对象,而在 IoC 中会把这些操作交给框架来处理,这样开发者就不用关注具体的实现细节了,拿来直接用就可以了,这就是控制反转。

IoC 很好的体现出了面向对象的设计法则之一——好莱坞法则:“别找我们,我们找你”。即由 IoC 容器帮对象找到相应的依赖对象并注入,而不是由对象主动去找。

举个例子,比如说传统找对象,先要设定好你的要求,如身高、体重、长相等,然后再一个一个的主动去找符合要求的对象,而 IoC 相当于,你把这些要求直接告诉婚介中心,由他们直接给你匹配到符合要求的对象,理想情况下是直接会帮你找到合适的对象,这就是传统编程模式和 IoC 的区别。

DI(Dependency Injection,翻译为“依赖注入”)表示组件间的依赖关系交由容器在运行期自动生成,也就是说,由容器动态的将某个依赖关系注入到组件之中,这样就能提升组件的重用频率。通过依赖注入机制,我们只需要通过简单的配置,就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心资源来自哪里、由谁实现等问题。

IoC 和 DI 其实是同一个概念从不同角度的描述的,由于控制反转这个概念比较含糊(可能只理解成了容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以 2004 年被开发者尊称为“教父”的 Martin Fowler(世界顶级专家,敏捷开发方法的创始人之一)又给出了一个新的名字“依赖注入”,相对 IoC 而言,“依赖注入”明确描述了“被注入对象依赖 IoC 容器配置依赖对象”。

Spring IoC 的优点

IoC 的优点有以下几个:

  • 使用更方便,拿来即用,无需显式的创建和销毁的过程;

  • 可以很容易提供众多服务,比如事务管理、消息服务等;

  • 提供了单例模式的支持;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?

掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
img-TWIQOa5o-1713584579928)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?

掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。

[外链图片转存中…(img-9ItbQrEr-1713584579929)]

[外链图片转存中…(img-vJ5wxeuF-1713584579929)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值