Java面试题关于Spring

80 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情

Spring相关面试题

第一题:BeanFactory和ApplicationContext有什么区别?

  •         BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。
  •         BeanFactory是Spring里面最底层的接口,是IoC的核心,定义了IoC的基本功能,包含了各种Bean的定义、加载、实例化,依赖注入和生命周期管理。 ApplicationContext接口作为BeanFactory的子类,除了提供BeanFactory所具有的功能外,还提供了更完整的功能扩展。
  •         BeanFactroy采用的是延迟加载形式来注入Bean的,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能提前发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
  •         ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。
  •         ApplicationContext启动后预载入所有的单实例Bean,所以在运行的时候速度比较快,因为它们已经创建好了。相对于BeanFactory,ApplicationContext 唯一的不足是占用内存空间,当应用程序配置Bean较多时,程序启动较慢。
  •         BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

第二题:Spring Bean的生命周期?

  •         简单来说,Spring Bean的生命周期只有四个阶段:实例化 Instantiation --> 属性赋值 Populate --> 初始化 Initialization --> 销毁 Destruction
  •         但具体来说,Spring Bean的生命周期包含:

1. 实例化Bean:

        对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。

2. 设置对象属性(依赖注入):

        实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成属性设置与依赖注入。

3. 处理Aware接口:

        Spring会检测该对象是否实现了xxxAware接口,通过Aware类型的接口,可以让我们拿到Spring容器的一些资源:

  •         如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,传入Bean的名字;
  •         如果这个Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。
  •         如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
  •         如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;

4. BeanPostProcessor前置处理:

        如果想对Bean进行一些自定义的前置处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。

5. InitializingBean:

        如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法。

6. init-method:

        如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

7. BeanPostProcessor后置处理:

        如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

8. DisposableBean:

        当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;

9. destroy-method:

        最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。


第三题:Spring中bean的作用域

1.singleton:

        默认作用域,单例bean,每个容器中只有一个bean的实例。

2.prototype:

        为每一个bean请求创建一个实例。

3.request:

        为每一个request请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。

4.session:

        与request范围类似,同一个session会话共享一个实例,不同会话使用不同的实例。

5.global-session:

        全局作用域,所有会话共享一个实例。如果想要声明让所有会话共享的存储变量的话,那么这全局变量需要存储在global-session中。


第四题:Spring框架中的Bean是线程安全的么?如果线程不安全,那么如何处理?

1.对于prototype作用域的Bean,每次都创建一个新对象,也就是线程之间不存在Bean共享,因此不会有线程安全问题。

2.对于singleton作用域的Bean,所有的线程都共享一个单例实例的Bean,因此是存在线程安全问题的。

  • 有状态Bean(Stateful Bean) : 就是有实例变量的对象,可以保存数据,是非线程安全的。
  • 无状态Bean(Stateless Bean): 就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。

3.对于有状态的bean,就需要自行保证线程安全

  • 将有状态的bean的作用域由“singleton”改为“prototype”。
  • 采用ThreadLocal解决线程安全问题,为每个线程提供一个独立的变量副本,不同线程只操作自己线程的副本变量。

第五题:Spring 框架中都用到了哪些设计模式?

1.工厂模式:

        Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象。

2.单例模式:

        Bean默认为单例模式。

3.策略模式:

        例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略。

4.代理模式:

        Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术。

5.模板方法:

        可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate, JmsTemplate, JpaTemplate。

6.适配器模式:

        Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller。

7.桥接模式:

        可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库。