面试题-Spring

1. 什么事Spring?谈谈你对IOC和AOP的理解。

Spirng:是一个企业级Java应用框架,他的作用主要是简化软件的开发以及配置的过程,简化项目部署环境。

Spring的优点:

  1. Spring低入侵设计,对业务代码的污染非常低。
  2. Spirng的DI机制将对象之间的关系交由框架处理,减少组件的耦合。
  3. Spring提供了AOP技术,支持将一些通用的功能进行集中式管理,从而提供更好的复用。
  4. Spring对于主流框架提供了非常好的支持。

IOC就是控制反转,指创建对象的控制权转移给Spirng来进行管理。简单来说,就是应用不用去 new 对象了,而全部交由Spring自动生成。

IOC有三种注入方式:

  1. 构造器注入
  2. setter 方法注入
  3. 根据注解注入

AOP面向切面。用于将那些与业务无关,但却对多个对象产生影响的公共行为,抽取并封装成一个可重用的模块。

AOP的核心就是动态代理:

  • JDK的动态代理
  • CGLIB的动态代理

2.Spring容器的启动流程是怎么样的

使用 AnnotationConfigApplicationContext 来跟踪一下启动流程:

  1. this() 初始化 readerscanner
  2. this.scan(basePackages) 使用 scanner 组件扫描 basePackage 下的所有对象,将配置类的 BeanDefinition 注册到容器中。
  3. this.refresh() 刷新容器:
    1. prepareRefresh():刷新前的预处理
    2. obtainFreshBeanFactory():获取在容器初始化时创建的 BeanFactory
    3. prepareBeanFactory(beanFactory)BeanFactory 的预处理工作,会向容器中添加一些组件
    4. postProcessBeanFactory(beanFactory):子类重写该方法,可以实现在 BeanFactory 创建并预处理完成后做进一步的设置
    5. invokeBeanFactoryPostProcessors(beanFactory):在 BeanFactory 初始化之后执行 BeanFactory 的后置处理器
    6. registerBeanPostProcessors(beanFactory):向容器中注册 Bean 的后置处理器,它的主要作用就是干预Spring初始化 Bean 的流程,完成代理、自动注入、依赖循环等功能
    7. initMessageSource():初始化 messagesource 组件,主要用于国际化
    8. initApplicationEventMulticaster():初始化事件分发器
    9. onRefresh():留给子容器,子类重写的方法,在容器刷新的时候可以自定义一些逻辑
    10. registerListeners():注册监听器
    11. finishBeanFactoryInitialization(beanFactory):完成 BeanFactory 的初始化,主要作用是初始化所有剩下的单例Bean
    12. finishRefresh():完成整个容器的初始化,发布 BeanFactory 容器刷新完成的事件

3、Spring框架中Bean的创建过程是怎样的?

首先简单来说,Spring框架中的Bean经过四个阶段:实例化 -> 属性赋值 -> 初始化 -> 销毁

然后具体来说,Spring中Bean经过了以下几个步骤:

  1. 实例化:new xxx(); 两个时机:
  2. 设置对象属性(依赖注入):Spring通过 BeanDefinition 找到对象依赖的其他对象,并将这些对象赋予当前对象。
  3. 处理 Aware 接口:Spring会检测对象是否实现了 xxxAware 接口,如果实现了,就会调用对应的方法。
    BeanNameAware、BeanClassLoaderAware、 BeanFactoryAware、ApplicationContextAware
  4. BeanPostProcessor :前置处理:调用 BeanPostProcessorpostProcessBeforeInitialization 方法
  5. InitializingBean :Spring检测对象如果实现了这个接口,就会执行他的 afterPropertiesSet() 方法,定制初始化逻辑。
  6. init-method<bean init-method=xxx> 如果Spring发现Bean配置了这个属性,就会调用他的配置方法,执行初始化逻辑。
  7. BeanPostProcessor 后置处理:调用 BeanPostProcessorpostProcessAfterInitialization 方法

到这里,这个Bean的创建过程就完成了,Bean就可以正常使用了

  1. DisposableBean :当Bean实现了这个接口,在对象销毁前就会调用 destory() 方法
  2. destroy-method<bean destroy-method=xxx> @PreDestroy

4、Spring框架中的Bean是线程安全的吗?如果线程不安全,要如何处理?

Spring容器本身没有提供Bean的线程安全策略,因此也可以说Spring容器中的Bean不是线程安全的。

要如何处理线程安全问题,就要分情况来分析。

Spring中的作用域:

  • sington
  • prototype:为每个Bean请求创建一个实例。
  • request:为每个request请求创建一个实例,请求完成后失效
  • session:与request是类似的
  • golbal-session:全剧作用域

对于线程安全问题:

  1. 对于 prototype 作用域,每次都是生成一个新的对象,所以不存在线程安全问题
  2. sington 作用域:默认就是线程不安全的。但是对于开发中的大部分Bean,其实是无状态的,不需要保证线程安全。所以在平常MVC开发中,是不会有线程安全问题的。

无状态表示这个实力没有属性对象,不能保存数据,是不变的类。比如:controller、service、dao
有状态表示实例是有属性对象的,可以保存数据,是线程不安全的,比如pojo

但是如果要保证线程安全,可以将Bean的作用域改为 prototype 比如 ModelView

另外还可以采用 ThreadLocal 来解决线程安全问题。 ThreadLocal 为每个线程保存一个副本变量,每个线程指操作自己的副本变量

5、Spring如何处理依赖循环问题

依赖循环:多个对象之间存在循环的引用关系,在初始化过程中,就会出现“先有鸡还是先有蛋”的问题

使用 @Lazy 注解

解决构造方法造成的循环依赖问题

使用三级缓存

  • 一级缓存:缓存最终的单例池对象
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
  • 二级缓存:缓存初始化的对象
    private final Map<String, Object> earlySingletonObjects = new HashMap(16);
  • 三级缓存:缓存对象的 ObjectFactory
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);

对于对象之间的普通引用关系,二级缓存会保存 new 出来的不完整对象,这样当单例池中找到不依赖的属性时,就可以先从二级缓存中获取到不完整的对象,完成对象创建,在后续的依赖注入过程中,将单例池中对象的引用关系调整完成。

如果引用的对象配置了 AOP ,那在单例池中最终就会需要注入动态代理对象,而不是原对象。而生成动态代理是要在对象初始化完成之后才开始的。于是Spring增加三级缓存,保存所有对象的动态代理配置信息。在发现有循环依赖时,将这个对象的动态代理信息获取出来,提前进行 AOP ,生成动态代理。

核心代码就带 DefaultSingletonBeanRegistrygetSingleton 方法当中。

    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            synchronized(this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }

6、Spring如何处理事务?

Spring当中支持编程式事务管理和声明式事务管理:

  • 编程式事务可以使用 TransactionTemplate
  • 声明式事务是Spring在 AOP 基础上提供的事务实现机制。他最大的优点就是不需要在业务代码中添加事务管理的代码,只需要在配置文件中做相关的事务规则声明就可以了。但是声明式事务只能针对方法级别,无法控制代码级别的事务管理。

Spring中对事务定义了不同的传播级别:Propagation

  1. PROPAGATION_REQUIRED :默认传播行为。如果当前没有事务,就创建一个新事物,如果当前存在事务,就加入到事务中。
  2. PROPAGATION_SUPPORTS :如果当前存在事务,就加入到该事务。如果当前不存在事务,就以非事务方式运行。
  3. PROPAGATION_MANDATORY :如果当前存在事务,就加入该事务。如果当前不存在事务,就抛出异常。
  4. PROPAGATION_REQUIRES_NEW :无论当前存不存在事务,都创建新事物进行执行。
  5. PROPAGATION_NOT_SUPPORTED :以非事务方式运行。如果当前存在事务,就将当前事务挂起。
  6. PROPAGATION_NEVER :以非事务方式运行。如果当前存在事务,就抛出异常。
  7. PROPAGATION_NESTED :如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则按 REQUEIRED 属性执行。

Spring中事务的隔离级别:

  1. ISOLATION_DEFAULT :使用数据库默认的事务隔离级别。
  2. ISOLATION_READ_UNCOMMITTED :读未提交。允许事务在执行过程中,读取其他事务未提交的数据。
  3. ISOLATION_READ_COMMITTED :读已提交。允许事务在执行过程中,读取其他事务已提交的数据。
  4. ISOLATION_REPEATABLE_READ :可重复读。在同一个事务内,任意时刻的查询结果是一致的。
  5. ISOLATION_SERIALIZABLE :所有事务依次执行。

7、SpringMVC中的控制器是不是单例模式?如果是,如何保证线程安全?

控制器是单例模式。

单例模式下就会有线程安全问题。

Spring中保证线程安全的方法:

  1. scop 设置成非 singleton 。像 prototyperequest
  2. 最好的方式是将控制器设计成无状态模式。在控制器中,不要携带数据。但是可以引用无状态的 servicedao
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值