【spring】面试必问

Spring IoC 的容器构建流程(8分)

Spring bean 的生命周期(10分)

BeanFactory 和 FactoryBean 的区别(6分)

BeanFactory 和 ApplicationContext 的区别(6分)

Spring 的 AOP 是怎么实现的(5分)

多个AOP的顺序怎么定(6分)

Spring 的 AOP 有哪几种创建代理的方式(9分)

Spring 中的 AOP 目前支持 JDK 动态代理和 Cglib 代理。

通常来说:如果被代理对象实现了接口,则使用 JDK 动态代理,否则使用 Cglib 代理。另外,也可以通过指定 proxyTargetClass=true 来实现强制走 Cglib 代理。

JDK 动态代理和 Cglib 代理的区别(9分)

1、JDK 动态代理本质上是实现了被代理对象的接口,而 Cglib 本质上是继承了被代理对象,覆盖其中的方法。

2、JDK 动态代理只能对实现了接口的类生成代理,Cglib 则没有这个限制。但是 Cglib 因为使用继承实现,所以 Cglib 无法代理被 final 修饰的方法或类。

3、在调用代理方法上,JDK 是通过反射机制调用,Cglib是通过FastClass 机制直接调用。FastClass 简单的理解,就是使用 index 作为入参,可以直接定位到要调用的方法直接进行调用。

4、在性能上,JDK1.7 之前,由于使用了 FastClass 机制,Cglib 在执行效率上比 JDK 快,但是随着 JDK 动态代理的不断优化,从 JDK 1.7 开始,JDK 动态代理已经明显比 Cglib 更快了。

JDK 动态代理为什么只能对实现了接口的类生成代理(7分)

根本原因是通过 JDK 动态代理生成的类已经继承了 Proxy 类,所以无法再使用继承的方式去对类实现代理。

Spring 的事务传播行为有哪些(6分)

1、REQUIRED:Spring 默认的事务传播级别,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。

2)REQUIRES_NEW:每次都会新建一个事务,如果上下文中有事务,则将上下文的事务挂起,当新建事务执行完成以后,上下文事务再恢复执行。

3)SUPPORTS:如果上下文存在事务,则加入到事务执行,如果没有事务,则使用非事务的方式执行。

4)MANDATORY:上下文中必须要存在事务,否则就会抛出异常。

5)NOT_SUPPORTED :如果上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。

6)NEVER:上下文中不能存在事务,否则就会抛出异常。

7)NESTED:嵌套事务。如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。

Spring 的事务隔离级别(6分)

Spring 的事务隔离级别底层其实是基于数据库的,Spring 并没有自己的一套隔离级别。

DEFAULT:使用数据库的默认隔离级别。

READ_UNCOMMITTED:读未提交,最低的隔离级别,会读取到其他事务还未提交的内容,存在脏读。

READ_COMMITTED:读已提交,读取到的内容都是已经提交的,可以解决脏读,但是存在不可重复读。

REPEATABLE_READ:可重复读,在一个事务中多次读取时看到相同的内容,可以解决不可重复读,但是存在幻读。

SERIALIZABLE:串行化,最高的隔离级别,对于同一行记录,写会加写锁,读会加读锁。在这种情况下,只有读读能并发执行,其他并行的读写、写读、写写操作都是冲突的,需要串行执行。可以防止脏读、不可重复度、幻读,没有并发事务问题。

Spring 的事务隔离级别是如何做到和数据库不一致的?(5分)

比如数据库是可重复读,Spring 是读已提交,这是怎么实现的?

Spring 的事务隔离级别本质上还是通过数据库来控制的,具体是在执行事务前先执行命令修改数据库隔离级别,命令格式如下:

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED

Spring 事务的实现原理(8分)

Spring 事务的底层实现主要使用的技术:AOP(动态代理) + ThreadLocal + try/catch。

动态代理:基本所有要进行逻辑增强的地方都会用到动态代理,AOP 底层也是通过动态代理实现。

ThreadLocal:主要用于线程间的资源隔离,以此实现不同线程可以使用不同的数据源、隔离级别等等。

try/catch:最终是执行 commit 还是 rollback,是根据业务逻辑处理是否抛出异常来决定。

Spring 事务的核心逻辑伪代码如下:

public void invokeWithinTransaction() {
    
    
    // 1.事务资源准备
    try {
    
    
        // 2.业务逻辑处理,也就是调用被代理的方法
    } catch (Exception e) {
    
    
        // 3.出现异常,进行回滚并将异常抛出
    } finally {
    
    
        // 现场还原:还原旧的事务信息
    }
    // 4.正常执行,进行事务的提交
    // 返回业务逻辑处理结果
}

详细流程如下图所示:
在这里插入图片描述

Spring 怎么解决循环依赖的问题(9分)

Spring 能解决构造函数循环依赖吗(6分)

Spring 三级缓存(6分)

@Resource 和 @Autowire 的区别(7分)

1、@Resource 和 @Autowired 都可以用来装配 bean

2、@Autowired 默认按类型装配,默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false。

3、@Resource 如果指定了 name 或 type,则按指定的进行装配;如果都不指定,则优先按名称装配,当找不到与名称匹配的 bean 时才按照类型进行装配。

@Autowire 怎么使用名称来注入(6分)

配合 @Qualifier 使用,如下所示:

@Component
public class Test {
    
    
    @Autowired
    @Qualifier("userService")
    private UserService userService;
}

@PostConstruct 修饰的方法里用到了其他 bean 实例,会有问题吗(5分)

bean 的 init-method 属性指定的方法里用到了其他 bean 实例,会有问题吗(5分)

要在 Spring IoC 容器构建完毕之后执行一些逻辑,怎么实现(6分)

Spring 中的常见扩展点有哪些(5分)

1、ApplicationContextInitializer

initialize 方法,在 Spring 容器刷新前触发,也就是 refresh 方法前被触发。

2、BeanFactoryPostProcessor

postProcessBeanFactory 方法,在加载完 Bean 定义之后,创建 Bean 实例之前被触发,通常使用该扩展点来加载一些自己的 bean 定义。

3、BeanPostProcessor

postProcessBeforeInitialization 方法,执行 bean 的初始化方法前被触发;postProcessAfterInitialization 方法,执行 bean 的初始化方法后被触发。

4、@PostConstruct

该注解被封装在 CommonAnnotationBeanPostProcessor 中,具体触发时间是在 postProcessBeforeInitialization 方法。

5、InitializingBean

afterPropertiesSet 方法,在 bean 的属性填充之后,初始化方法(init-method)之前被触发,该方法的作用基本等同于 init-method,主要用于执行初始化相关操作。

6、ApplicationListener,事件监听器

onApplicationEvent 方法,根据事件类型触发时间不同,通常使用的 ContextRefreshedEvent 触发时间为上下文刷新完毕,通常用于 IoC 容器构建结束后处理一些自定义逻辑。

7、@PreDestroy

该注解被封装在 DestructionAwareBeanPostProcessor 中,具体触发时间是在 postProcessBeforeDestruction 方法,也就是在销毁对象之前触发。

8、DisposableBean

destroy 方法,在 bean 的销毁阶段被触发,该方法的作用基本等同于

destroy-method,主用用于执行销毁相关操作。

Spring中如何让两个bean按顺序加载?(8分)

1、使用 @DependsOn、depends-on

2、让后加载的类依赖先加载的类

@Component
public class A {
    
    
    @Autowire
    private B b;
}

3、使用扩展点提前加载,例如:BeanFactoryPostProcessor

@Component
public class TestBean implements BeanFactoryPostProcessor {
    
    
  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory 
          configurableListableBeanFactory) throws BeansException {
    
    
      // 加载bean
      beanFactory.getBean("a");
  }

使用 Mybatis 时,调用 DAO接口时是怎么调用到 SQL 的(7分)

简单点说,当我们使用 Spring+MyBatis 时:

详细的解析见:《面试题:mybatis 中的 DAO 接口和 XML 文件里的 SQL 是如何建立关系的?》

1、DAO接口会被加载到 Spring 容器中,通过动态代理来创建

2、XML中的SQL会被解析并保存到本地缓存中,key是SQL 的 namespace + id,value 是SQL的封装

3、当我们调用DAO接口时,会走到代理类中,通过接口的全路径名,从步骤2的缓存中找到对应的SQL,然后执行并返回结果


参考:
《面试必问的 Spring,你懂了吗?》

猜你喜欢

转载自blog.csdn.net/m0_45406092/article/details/115128616