Spring面试题

1. Spring 用到了哪些设计模式

简单工厂模式:BeanFactory就是简单工厂模式的体现

工厂方法模式:FactoryBean就是典型的工厂方法模式。spring在使用getBean()调用获得该bean时,会自动调用该bean的getObject()方法。每个 Bean 都会对应一个 FactoryBean,如 SqlSessionFactory 对应 SqlSessionFactoryBean。

单例模式:一个类仅有一个实例,提供一个访问它的全局访问点。Spring 创建 Bean 实例默认是单例的。

适配器模式:SpringMVC中的适配器HandlerAdatper。由于应用会有多个Controller实现,如果需要直接调用Controller方法,那么需要先判断是由哪一个Controller处理请求,然后调用相应的方法。当增加新的 Controller,需要修改原来的逻辑,违反了开闭原则(对修改关闭,对扩展开放)。 为此,Spring提供了一个适配器接口,每一种 Controller 对应一种 HandlerAdapter 实现类,当请求过来,SpringMVC会调用getHandler()获取相应的Controller,然后获取该Controller对应的 HandlerAdapter,最后调用HandlerAdapter的handle()方法处理请求,实际上调用的是Controller的handleRequest()。每次添加新的 Controller 时,只需要增加一个适配器类就可以,无需修改原有的逻辑。

常用的处理器适配器:SimpleControllerHandlerAdapter,HttpRequestHandlerAdapter,AnnotationMethodHandlerAdapter。

代理模式:spring 的 aop 使用了动态代理,有两种方式JdkDynamicAopProxy和Cglib2AopProxy。

观察者模式:spring 中 observer 模式常用的地方是 listener 的实现,如ApplicationListener。

模板模式: Spring 中 jdbcTemplate、hibernateTemplate 等,就使用到了模板模式

2. IOC容器初始化过程

从XML中读取配置文件。将bean标签解析成 BeanDefinition,如解析 property 元素, 并注入到 BeanDefinition 实例中。将 BeanDefinition 注册到容器 BeanDefinitionMap 中。BeanFactory 根据 BeanDefinition 的定义信息创建实例化和初始化 bean。单例bean的初始化以及依赖注入一般都在容器初始化阶段进行,只有懒加载(lazy-init为true)的单例bean是在应用第一次调用getBean()时进行初始化和依赖注入。// AbstractApplicationContext // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); 多例bean 在容器启动时不实例化,即使设置 lazy-init 为 false 也没用,只有调用了getBean()才进行实例化。loadBeanDefinitions采用了模板模式,具体加载 BeanDefinition 的逻辑由各个子类完成。#

3. Bean的生命周期

1.调用bean的构造方法创建Bean 2.通过反射调用setter方法进行属性的依赖注入 3.如果Bean实现了BeanNameAware接口,Spring将调用setBeanName(),设置 Bean的name(xml文件中bean标签的id) 4.如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()把bean factory设置给Bean 5.如果存在BeanPostProcessor,Spring将调用它们的postProcessBeforeInitialization(预初始化)方法,在Bean初始化前对其进行处理 6.如果Bean实现了InitializingBean接口,Spring将调用它的afterPropertiesSet方法,然后调用xml定义的 init-method 方法,两个方法作用类似,都是在初始化 bean 的时候执行 7.如果存在BeanPostProcessor,Spring将调用它们的postProcessAfterInitialization(后初始化)方法,在Bean初始化后对其进行处理 8.Bean初始化完成,供应用使用,这里分两种情况: 8.1 如果Bean为单例的话,那么容器会返回Bean给用户,并存入缓存池。如果Bean实现了DisposableBean接口,Spring将调用它的destory方法,然后调用在xml中定义的 destory-method方法,这两个方法作用类似,都是在Bean实例销毁前执行。 8.2 如果Bean是多例的话,容器将Bean返回给用户,剩下的生命周期由用户控制。

4. BeanFactory和FactoryBean的区别?

BeanFactory:管理Bean的容器,Spring中生成的Bean都是由这个接口的实现来管理的。

FactoryBean:通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,直接用xml配置比较麻烦,这时可以考虑用FactoryBean,可以隐藏实例化复杂Bean的细节。当配置文件中bean标签的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是调用FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。如果想得到FactoryBean必须使用 '&' + beanName 的方式获取。

Mybatis 提供了 SqlSessionFactoryBean,可以简化 SqlSessionFactory的配置:

5. Bean注入容器有哪些方式?

1、@Configuration + @Bean

@ComponentScan放置在我们的配置类上,然后可以指定一个路径,进行扫描带有特定注解的bean,然后加至容器中。特定注解包括@Controller、@Service、@Repository、@Component

@Import注解导入

实现BeanDefinitionRegistryPostProcessor进行后置处理。

使用FactoryBean接口

6. Spring自动装配的方式有哪些?

byType(根据类型),byName(根据名称)、constructor(根据构造函数)。

Spring怎么解决循环依赖的问题?

首先,有两种Bean注入的方式。构造器注入和属性注入。对于构造器注入的循环依赖,Spring处理不了,会直接抛出BeanCurrentlylnCreationException异常。 对于属性注入的循环依赖(单例模式下),是通过三级缓存处理来循环依赖的。而非单例对象的循环依赖,则无法处理。 下面分析单例模式下属性注入的循环依赖是怎么处理的: 首先,Spring单例对象的初始化大略分为三步: createBeanInstance:实例化bean,使用构造方法创建对象,为对象分配内存。 populateBean:进行依赖注入。 initializeBean:初始化bean。 Spring为了解决单例的循环依赖问题, 使用了三级缓存: singletonObjects:完成了初始化的单例对象map,bean name --> bean instanceearlySingletonObjects :完成实例化未初始化的单例对象map,bean name --> bean instancesingletonFactories : 单例对象工厂map,bean name --> ObjectFactory, 单例对象实例化完成之后会加入singletonFactories。在调用createBeanInstance进行实例化之后, 会调用addSingletonFactory,将单例对象放到singletonFactories中。

假如A依赖了B的实例对象,同时B也依赖A的实例对象。A首先完成了实例化,并且将自己添加到singletonFactories中接着进行依赖注入, 发现自己依赖对象B,此时就尝试去get(B)发现B还没有被实例化,对B进行实例化然后B在初始化的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects和二级缓存earlySingletonObjects没找到,尝试三级缓存singletonFactories,由于A初始化时将自己添加到了singletonFactories,所以B可以拿到A对象,然后将A从三级缓存中移到二级缓存中B拿到A对象后顺利完成了初始化,然后将自己放入到一级缓存singletonObjects中此时返回A中,A此时能拿到B的对象顺利完成自己的初始化由此看出,属性注入的循环依赖主要是通过将实例化完成的bean添加到singletonFactories来实现的。而使用构造器依赖注入的bean在实例化的时候会进行依赖注入,不会被添加到singletonFactories中。比如A和B都是通过构造器依赖注入,A在调用构造器进行实例化的时候,发现自己依赖B,B没有被实例化,就会对B进行实例化,此时A未实例化完成,不会被添加到singtonFactories。而B依赖于A,B会去三级缓存寻找A对象,发现不存在,于是又会实例化A,A实例化了两次,从而导致抛异常。

总结:1、利用缓存识别已经遍历过的节点; 2、利用Java引用,先提前设置对象地址,后完善对象。

相关推荐
不能再留遗憾了3 小时前
【SpringCloud】Sentinel
spring·spring cloud·sentinel
whltaoin4 小时前
AI 超级智能体全栈项目阶段五:RAG 四大流程详解、最佳实践与调优(基于 Spring AI 实现)
java·人工智能·spring·rag·springai
心勤则明4 小时前
Spring AI 文档ETL实战:集成text-embedding-v4 与 Milvus
人工智能·spring·etl
艾菜籽5 小时前
Spring Web MVC入门补充1
java·后端·spring·mvc
艾菜籽7 小时前
Spring MVC入门补充2
java·spring·mvc
为java加瓦11 小时前
Spring 方法注入机制深度解析:Lookup与Replace Method原理与应用
java·数据库·spring
无名客011 小时前
SpringCloud中的网关(Gateway)的作用是什么?
spring·spring cloud·gateway
hrrrrb13 小时前
【Spring Security】Spring Security 概念
java·数据库·spring
小信丶13 小时前
Spring 中解决 “Could not autowire. There is more than one bean of type“ 错误
java·spring
hello 早上好20 小时前
深入 Spring 依赖注入底层原理
数据库·sql·spring