上期 30 道 Spring 面试题解答

ConstXiong

共 31839字,需浏览 64分钟

 · 2021-04-02

写代码,看版本!不提版本的都是在耍流氓...

所以第一点要指出的就是,以下都是基于 Spring 5.2.2 的版本去查资料的。


1、Spring 是什么?有什么作用?

  • Spring Framework 诞生于 2002 年,最早的作者是 Rod Johnson。已经发展到 5.x 版,支持到 JDK 8-11 及 Java EE 8。

  • 设计理念:力争让选择无处不在、海纳百川、保持向后兼容、专注 API 设计、追求严苛的代码质量

  • 一个轻量级 IoC 和 AOP 容器框架,为 Java 应用程序提供基础性服务,用于简化开发,使开发者只关注业务需求。为构建企业级应用,提供轻量级一站式解决方案。


2、Spring Framework 有哪些主要模块?

文档地址:

https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/spring-framework-reference/index.html


主要模块及其细分模块


5.2.x 的 github:

https://github.com/spring-projects/spring-framework/tree/5.2.x


总结一下就是:

  • Core - 核心模块

  • Testing - 测试相关

  • Data Access - 数据获取

  • Web Servlet - web servlet 规范相关

  • Web Reactive - 响应式 web 编程

  • Integration - 集成相关

  • Languages - 多语言


其中 Core 模块的中细分模块,不管是直接还是间接,都会被使用到,也是我们最为熟知的。

  • IoC Container,控制反转容器

  • Events,事件编程

  • Resources,资源加载

  • i18n,国际化

  • Validation,校验

  • Data Binding,数据绑定

  • Type Conversion,类型转换

  • SpEL,Spring 表达式

  • AOP,面向切面编程


Spring 运行时的结构图


3、Spring 框架有哪些优点?

看看官网怎么说?why spring?


不看官网吹牛逼,Spring 实实在在的优点确实很多

  • 低侵入式设计,

  • IoC 容器将对象之间的依赖关系交由框架处理,减低组件的耦合性

  • AOP 支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,可以很好的复用

  • 对主流的应用框架提供了集成支持,只需要引入对应版本的 jar 包,集成方便,且生态非常庞大

  • Spring 的 flexible 做得很好,模块支持可插拔,添加和移除模块依赖即可引入和删除对应的功能

  • 社区活跃,版本更新及时

  • 基本已是 Java 后端开发标准


4、什么是 IoC?Spring 如何实现的?

IoC,Inversion of Control(控制反转)。
IoC 是一种设计思想,在开发中,将你设计好的对象交给容器控制,而不是由程序员显示地用代码进行对象的创建。
具体实现就是,把创建和查找依赖对象的控制权交给 IoC 容器,由 IoC 容器进行注入、组合对象。这样对象与对象之间是松耦合、便于测试、功能可复用(减少对象的创建和内存消耗),使得程序的整个体系结构可维护性、灵活性、扩展性变高。


提到 IoC 就不得不提 DI,(Dependency Injection)依赖注入,是 IoC 容器装配、注入对象的一种方式。
通过依赖注入机制,简单的配置即可注入需要的资源,完成自身的业务逻辑,不需要关心资源的出处和具体实现。


Spring 中 IoC 容器的底层实现就是 BeanFactory,BeanFactory 可以通过配置文件(xml、properties)、注解的方式加载 bean;提供根据 bean 的名称或类型类型查找 bean 的能力。功能最全的一个 BeanFactory 实现就是 DefaultListableBeanFactory。


5、什么是 AOP?Spring 如何实现的?

AOP,Aspect Oriented Programming(面向切面编程)。


通过预编译和运行期动态代理实现程序功能的统一维护。
利用一种称为横切的技术,剖开对象的封装,并将影响多个类的公共行为封装到一个可重用模块,组成一个切面,即 Aspect 。
"切面" 就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,利于可操作性和可维护性。


AOP 中所涉及的概念:

  • Join point(连接点):指程序运行过程中所执行的方法。Spring AOP 中,一个连接点代表一个方法的执行。
    Aspect(切面):被抽取出来的公共模块,可以用来会横切多个对象。Aspect 切面可以看成 Pointcut 和 Advice 的结合,一个切面可以由多个切点和通知组成。

  • Pointcut(切点):切点用于定义要对哪些 Join point 进行拦截。切点分为 execution 方式和 annotation 方式。

  • Advice(通知,也有翻译为"增强"的):指要在 Join Point 上执行的动作,即增强的逻辑,比如权限校验和、日志记录等。通知的类型包括 Around、Before、After、After returning、After throwing。

  • Target(目标对象):包含连接点的对象,也称作被 Advice 的对象。

  • Weaving(织入):通过动态代理,在 Join point 中执行增强逻辑(Advice)的过程。

  • Introduction(引入):添加额外的方法或者字段到被通知的类。Spring 允许引入新的接口以及对应的实现到被代理的对象。


AOP 是 Spring 中非常重要的一个功能。


Spring 中 AOP 的实现方式有两大类:

  • 采用动态代理技术,利用拦截方法的方式,对该方法进行装饰,以增强原有对象的方法。具体实现技术有 JDK 动态代理基于接口代理和 cglib 基于类代理的字节码提升。

  • 采用静态织入的方式,引入特定的语法创建"切面",从而使得编译器可以在编译期间织入有关"切面"的代码。具体实现是 Spring 对 AspectJ 进行了适配。


6、Spring 有哪些常用的注入方式?

Spring 的注入,是一个挺大话题。谈注入之前,先要说明 bean 的申明。
bean 的声明

  • 可以在外部化的配置文件中,比如 xml 和 properteis 这样的配置文件

  • 也可以通过注解的方式,比如 @Component 及其派生出来的注解 @Controller、@Service、@Repository、@Configuration 等

  • 通过指定工厂方法也能申明 bean 的创建

  • 还可以通过 API 的方式进行注册,如 BeanDefinitionRegistry#registerBeanDefinition 方法注册 bean 的定义



bean 的注入方式可以分为

  • setter 方法注入,如 xml 中

    节点配置属性
  • 构造器注入,如 xml 中

    节点配置属性
  • Aware 系列接口回调注入,如 ApplicationContextAware、BeanFactoryAware、BeanNameAware、EnvironmentAware 等

  • 注解,如 @Autowired、@Autowired + @Qualifier、@Resource、@Inject、@Value

  • BeanDefinition 相关 API,如 AbstractBeanDefinition#setConstructorArgumentValues(设置构造方法参数)、AbstractBeanDefinition#setPropertyValues(设置属性参数)


代码示例可以参见我博客这篇
https://javanav.com/val/a835e6c709ea439ebb21a80f1bf77926.html


7、BeanFactory 和 ApplicationContext 有什么区别?

BeanFactory 是 Spring IoC 容器的底层实现
ApplicationContext 拥有 BeanFactory 的所有能力,还提供了

  • Easier integration with Spring’s AOP features

  • Message resource handling (for use in internationalization)

  • Event publication

  • Application-layer specific contexts such as the WebApplicationContext for use in web applications

摘自:

https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE

即更易集成 aop 特性、消息资源处理(国际化)、事件发布、应用程序层面特定的上下文如 WebApplicationContext。

除了以上,细节上还包括:

  • BeanFactory 在启动的时候不会去实例化 bean,从容器中拿 bean 时才会去实例化;ApplicationContext 在启动时就把所有的 bean 全部实例化了

  • BeanPostProcessor、BeanFactoryPostProcessor 接口的注册:BeanFactory 需要手动注册,ApplicationContext 则是自动
    等…

总之,ApplicationContext 是具备应用特性的 BeanFactory 超集。


8、Spring 中的 bean 是线程安全的吗?

Spring 容器本身并没有提供 bean 的线程安全策略,可以说 Spring 容器中的 bean 本身不具备线程安全的特性
是否线程安全,需要看 bean 的使用场景中是否存在竞态条件。比如

  • singleton 作用域的 bean,没有存储和修改属性或类变量的操作,属于无状态 bean,这样就是线程安全的

  • prototype 作用域的 bean,在方法体内被获取,即每次创建的都是新的 bean 对象,也不存在竞态条件,也是线程安全的

  • 使用线程本地变量绑定 ThreadLocal 可以解决 bean 的线程安全问题

  • 使用锁,也可以解决 bean 的线程安全问题


9、Spring 中 BeanFactory#getBean 方法是否线程安全的吗?

getBean 的默认实现的入口是在 AbstractBeanFactory#doGetBean 方法
结合源码来看,创建 bean 的线程安全是通过可并发容器 + 加锁 synchronized 保证的
比如列举几个可说的点:

  • 根据 beanName 获取是否存在早期已缓存的单例 bean,存在 get、判空、put、remove 操作,所以加了锁。如码1

  • 合并 BeanDefinition 成 RootDefinition 时,AbstractBeanFactory#getMergedBeanDefinition 方法也加了锁。如源码 2

类似之处还有很多,可结合源码进行查看

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


源码2:protected RootBeanDefinition getMergedBeanDefinition(
            String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)

            throws BeanDefinitionStoreException 
{

        
synchronized (this.mergedBeanDefinitions) {
            RootBeanDefinition mbd = null;
            RootBeanDefinition previous = null;



10、Spring bean 支持哪些作用域?

5.2.2 官方文档(每个版本可能有所差别)


Spring bean 的作用域包含

  • singleton

  • prototype


web 应用中再加上

  • request

  • session

  • application

  • websocket


也可以实现 Scope 接口自定义作用域,BeanFactory#registerScope 方法进行注册


11、Spring bean 的自动装配方式有哪些?

Spring 中自动装配 autowire 机制是指,由 Spring Ioc 容器负责把所需要的 bean,自动查找和赋值到当前在创建 bean 的属性中,无需手动设置 bean 的属性。


1、基于 xml 配置 bean 的装配方式:

  • no:默认的方式是不进行自动装配的,需要通过手工设置 ref 属性来进行装配 bean。

  • byName:通过 bean 的名称进行自动装配,如果一个 bean 的 property 与另一 bean 的 name 相同,就进行自动装配。

  • byType:通过参数的数据类型进行自动装配。

  • constructor:通过构造函数进行装配,并且构造函数的参数通过 byType 进行装配。

  • autodetect:自动探测,如果有构造方法,通过 construct 的方式自动装配,否则使用 byType 的方式自动装配。( 已弃用)

方式的定义在 AutowireCapableBeanFactory.AUTOWIRE_NO

AUTOWIRE_BY_NAME

AUTOWIRE_BY_TYPE

AUTOWIRE_CONSTRUCTOR

AUTOWIRE_AUTODETECT


2、基于注解完成 bean 的装配

  • @Autowired、@Resource、@Inject 都可以实现 bean 的注入
    @Autowired 是 Spring 推出的,功能最为强大,可以作用于 构造方法、setter 方法、参数、成员变量、注解(用于自定义扩展注解)
    @Resource 是 JSR-250 的规范推出
    @Inject 是 JSR-330 的规范推出

  • @Value 可以注入配置信息


@Autowired、@Inject、@Value 的解析工作是在 AutowiredAnnotationBeanPostProcessor 内,如何源码 1


@Resource 的解析工作是在 CommonAnnotationBeanPostProcessor 内,如何源码 2

源码 1
public AutowiredAnnotationBeanPostProcessor() {
    this.autowiredAnnotationTypes.add(Autowired.class);
    this.autowiredAnnotationTypes.add(Value.class);
    try {
        this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
                ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
        logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}

源码 2
static {
    ...
    resourceAnnotationTypes.add(Resource.class);
    ...
}


12、Spring 中 ObjectFactory 与 BeanFactory 的区别是什么?

  • BeanFactory 是 IoC 底层容器,提供了 bean 的管理

  • FactoryBean 是创建 Bean 的一种方式,帮助实现复杂的初始化逻辑


重点说下 FactoryBean,该接口包含 3 个方法 getObject、getObjectType、isSingleton,用于构建复杂的 bean。

如,MyBatis 与 Spring 集成,使用了 SqlSessionFactoryBean 继承 FactoryBean,用于构建 SqlSessionFactory bean。

得到 FactoryBean 本身这个 bean,需要在 bean name 前面加上 $


13、介绍一下 Spring bean 的生命周期

Bean 的生命周期按最详细的来说如下(参照小马哥的 Spring 专栏课件),其实细节还远不止如此,都在代码 AbstractBeanFactory#doGetBean 里,可以自己走起!

1、Spring Bean 元信息配置阶段。xmlproperties 配置文件中配置 bean 的信息;代码中使用注解标识 bean;代码中使用 api 设置 BeanDefinition 的属性值或构造方法参数。
2、Spring Bean 元信息解析阶段。BeanDefinitionReader 的三种实现类(XmlBeanDefinitionReaderPropertiesBeanDefinitionReaderGroovyBeanDefinitionReader),将配置信息解析为 BeanDefinitionAnnotatedBeanDefinitionReader 将注解标识的类或方法,解析成 BeanDefinition
3、Spring Bean 注册阶段。将 BeanDefinition 注册到 BeanDefinitionRegistry 中。
4、Spring BeanDefinition 合并阶段。AbstractBeanFactory#getMergedBeanDefinition 方法,将有父子层次关系的 BeanDefinition 合并成 RootBeanDefinition
5、Spring Bean Class 加载阶段。AbstractBeanFactory#resolveBeanClass 方法,若 BeanDefinition 中的 beanClass 不存在,获取类加载器根据包路径+类名加载其 Class 对象,用于后面的实例化。
6、Spring Bean 实例化前阶段。AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation,执行 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
7、Spring Bean 实例化阶段。AbstractAutowireCapableBeanFactory#instantiateBean,执行 InstantiationStrategy#instantiate 方法实例化 bean
8、Spring Bean 实例化后阶段。AbstractAutowireCapableBeanFactory#populateBean,执行 InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation
9、Spring Bean 属性赋值前阶段。AbstractAutowireCapableBeanFactory#populateBean 执行设置属性值,InstantiationAwareBeanPostProcessor#postProcessProperties
10、Spring Bean Aware接口回调阶段。AbstractAutowireCapableBeanFactory#initializeBean -#invokeAwareMethods 方法中执行
11、Spring Bean 初始化前阶段。AbstractAutowireCapableBeanFactory#initializeBean -#applyBeanPostProcessorsBeforeInitialization 方法执行 BeanPostProcessor#postProcessBeforeInitialization
12、Spring Bean 初始化阶段。AbstractAutowireCapableBeanFactory#initializeBean -#invokeInitMethods 方法执行
13、Spring Bean 初始化后阶段。AbstractAutowireCapableBeanFactory#initializeBean -#applyBeanPostProcessorsAfterInitialization 方法执行
14、Spring Bean 初始化完成阶段。在 ApplicationContext 的生命周期里,AbstractApplicationContext#finishBeanFactoryInitialization -DefaultListableBeanFactory#preInstantiateSingletons -SmartInitializingSingleton#afterSingletonsInstantiated
15、Spring Bean 销毁前阶段。AbstractBeanFactory#destroyBean -DisposableBeanAdapter#destroy -DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
16、Spring Bean 销毁阶段。AbstractBeanFactory#destroyBean 执行 @PreDestroy 标注方法、实现DisposableBean 接口的destroy() 方法、自定义销毁方法
17、Spring Bean 垃圾收集。BeanFactory 被置空,所缓存的 bean 可被 gc


14、介绍一下 Spring 容器的生命周期

ApplicationContext 生命周期的入口在 AbstractApplicationContext#refresh 方法(参照小马哥的 Spring 专栏课件)

1、应用上下文启动准备。AbstractApplicationContext#prepareRefresh
    启动时间 startupDate
    状态标识 closed(false) active(true)
    初始化 PropertSources - initPropertySources
    校验 Environment 必须属性
    初始化早期 Spring 事件集合

2、BeanFactory 创建。AbstractApplicationContext#obtainFreshBeanFactory
    已存在 BeanFactory,先销毁 bean、关闭 BeanFactory
    创建 BeanFactory createBeanFactory
    设置 BeanFactory id
    customizeBeanFactory 方法中,是否可以重复 BeanDefinition、是否可以循环依赖设置
    loadBeanDefinitions 方法,加载 BeanDefinition
    赋值该 BeanFactory 到 ApplicationContext 中

3、BeanFactory 准备。AbstractApplicationContext#prepareBeanFactory
    设置 BeanClassLoader
    设置 Bean 表达式处理器
    添加 PropertyEditorRegistrar 的实现对象 ResourceEditorRegistrar
    添加 BeanPostProcessor
    忽略 Aware 接口作为依赖注入的接口
    注册 ResovlableDependency 对象:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
    注册 ApplicationListenerDetector 对象
    注册 LoadTimeWeaverAwareProcessor 对象
    注册单例对象 Environment、Java System Properties、OS 环境变量

4、BeanFactory 后置处理。AbstractApplicationContext#postProcessBeanFactory、invokeBeanFactoryPostProcessors
    postProcessBeanFactory 方法由子类覆盖
    调用 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()) 方法
    注册 LoadTimeWeaverAwareProcessor
    设置 TempClassLoader

5、BeanFactory 注册 BeanPostProcessor。AbstractApplicationContext#registerBeanPostProcessors
    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
        注册 PriorityOrdered 类型的 BeanPostProcessor Beans
        注册 Ordered 类型的 BeanPostProcessor Beans
        注册普通的 BeanPostProcessor Beans(nonOrderedPostProcessors)
        注册 MergedBeanDefinitionPostProcessor Beans(internalPostProcessors)
        注册 ApplicationListenerDetector 对象

6、初始化内建 Bean - MessageSource。AbstractApplicationContext#initMessageSource
    若不存在 messageSource bean,注册单例 bean DelegatingMessageSource
    若存在且需要设置层级,进行设置

7、初始化内建 Bean - Spring 广播器。AbstractApplicationContext#initApplicationEventMulticaster
    若不存在 applicationEventMulticaster bean,注册单例 bean SimpleApplicationEventMulticaster
    存在则设置为当前属性

8、Spring 应用上下文刷新。AbstractApplicationContext#onRefresh
    留给子类覆盖

9、Spring 事件监听器注册。AbstractApplicationContext#registerListeners
    添加 ApplicationListener 对象
    添加 BeanFactory 所注册的 ApplicationListener Beans
    广播早期事件

10、BeanFactory 初始化完成。AbstractApplicationContext#finishBeanFactoryInitialization
    如果存在设置 conversionService Bean
    添加 StringValueResolver
    查找 LoadTimeWeaverAware Bean
    BeanFactory 置空 tempClassLoader
    BeanFactory 解冻 的配置
    BeanFactory 初始化非延迟单例 Bean

11、Spring 应用上下文刷新完成。AbstractApplicationContext#finishRefresh
    清空 ResourceLoader 缓存
    初始化 LifeCycleProcessor 对象
    调用 LifeCycleProcessor#onRefresh 方法
    发布上下文 ContextRefreshedEvent 已刷新事件
    向 MBeanServer 托管 Live Beans

12、Spring 应用上下文启动。AbstractApplicationContext#start
    查找和启动 LifeCycleProcessor
    发布上下文 ContextStartedEvent 已启动事件

13、Spring 应用下文停止。AbstractApplicationContext#stop
    查找和启动 LifeCycleProcessor
    发布上下文 ContextStoppedEvent 已停止事件

14、Spring 应用下文关闭。AbstractApplicationContext#close
    状态标识 closed(true) active(false)
    Live Bean JMX 撤销托管
    发布上下文 ContextClosedEvent 已关闭事件
    查找和关闭 LifeCycleProcessor
    销毁所有 Bean
    关闭 BeanFactory
    onClose 方法回调
    早期事件处理
    移除 ShutdownHook


15、Spring 如何解决 bean 的循环依赖?

  • 原型模式下的循环依赖是无法无法解决的

  • 构造方法注入 + 单例模式,仅可以通过延迟加载解决

  • setter 方法和属性注入 + 单例模式下,可以解决

逻辑如下:

AbstractAutowireCapableBeanFactory.allowCircularReferences=true
通过三级缓存(或者说三个 Map 去解决循环依赖的问题),核心代码在 DefaultSingletonBeanRegistry#getSingleton

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //Map 1,单例缓存
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            //Map 2,早期单例缓存,若没有 ObjectFactory 的 bean,到这级已经可以解决循环依赖
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                //Map 3,单例工厂,解决包含 ObjectFactory 情况的循环依赖
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();//获取 bean 对象
                    this.earlySingletonObjects.put(beanName, singletonObject);//放到早期实例化的 Bean 中
                    this.singletonFactories.remove(beanName);//单例工厂中移除
                }
            }
        }
    }
    return singletonObject;
}


16、Spring Advice 有哪些类型?

  • Before Advice:在连接点(Join point)之前执行

  • After Advice:当连接点退出的时候执行

  • Around Advice:环绕一个连接点的增强,这是最强大的一种增强类型。可以在方法调用前、后完成自定义的行为、是否继续执行连接点、是否进行原逻辑、是否抛出异常来结束执行

  • AfterReturning Advice:在连接点正常完成后执行的增强,如果连接点抛出异常,则不执行

  • AfterThrowing Advice:在方法抛出异常退出时执行的增强


Advice 的 执行顺序:

  • Around Before Advice

  • Before Advice

  • target method 执行

  • Around After Advice

  • After Advice

  • AfterReturning | AfterThrowing & Exception


17、Spring 事务的实现方式有哪些?原理是什么?

事务的实现方式:

  • 编程式事务管理,可以通过 java.sql.Connection、TransactionTemplate 控制事务

  • 基于 TransactionProxyFactoryBean 的声明式事务管理

  • 基于注解 @Transactional 的声明式事务管理

  • 基于 Aspectj AOP 配置(注解)事务


实现原理,Spring 对事务的实现本质是依赖数据库厂商对事务的支持,调用了 JDBC 规范中 Connection#commit、rollback...等 api


编程式就是代码直接或间接手动调用事务提交与回滚


申明式事务,通过 AOP 在方法体执行前开启事务,执行后根据情况提交或回滚事务。


18、Spring 的事务传播特性和隔离级别是什么样的?

Spring 事务的传播机制是用来处理多个事务同时存在的情况。事务传播机制使用 ThreadLocal 实现,如果调用的方法是在新线程调用的,事务传播会失效。

  • PROPAGATION_REQUIRED(默认):如果当前没有事务,就创建一个新事务;如果当前存在事务,就加入该事务

  • PROPAGATION_SUPPORTS:如果当前存在事务,就加入该事务;如果当前不存在事务,就以非事务执行

  • PROPAGATION_MANDATORY:如果当前存在事务,就加入该事务;如果当前不存在事务,就抛出异常

  • PROPAGATION_REQUIRES_NEW:无论当前存不存在事务,都创建新事务进行执行

  • PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起

  • PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常

  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则按 REQUIRED 属性执行

源码见 Propagation 枚举。



隔离级别:

  • ISOLATION_DEFAULT:PlatfromTransactionManager 默认隔离级别,使用数据库默认的事务隔离级别。

  • ISOLATION_READ_UNCOMMITTED:读未提交,允许事务在执行过程中,读取其他事务未提交的数据。

  • ISOLATION_READ_COMMITTED:读已提交,允许事务在执行过程中,读取其他事务已经提交的数据。

  • ISOLATION_REPEATABLE_READ:可重复读,在同一个事务内,任意时刻的查询结果都是一致的。

  • ISOLATION_SERIALIZABLE:最严格的事务,序列化执行。

源码见 Isolation 枚举


19、@Transactional 注解哪些情况下会失效?

  1. @Transactional 作用在非 public 修饰的方法上

  2. @Transactional 作用于接口,使用 CGLib 动态代理

  3. @Transactional 注解属性 propagation 设置以下三种可能导致无法回滚
       SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
       NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
       NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

  4. 同一类中加 @Transactional 方法被无 @Transactional 的方法调用,事务失效

  5. @Transactional 方法内异常被捕获

  6. 默认 RuntimeException 和 Error 及子类抛出,会回滚;rollbackFor 指定的异常及子类发生才会回滚

  7. 数据库不支持事务,如 MySQL 使用 MyISAM 存储引擎

  8. Spring 的配置文件中未配置事务注解生效
    <tx:annotation-driven transaction-manager="transactionManager"/>

  9. Spring Boot 引入 jbdc 或 jpa 包,默认事务注解。若未引入这两个包,需要使用 @EnableTransactionManagement 进行配置

详细分析:

https://avanav.com/article/50bcab2963114693b08a3dbb5bf1071f.html


20、Spring AOP 是如何实现的?

首先,Spring AOP 有一些特性:

  1. 纯 Java 实现,无编译时特殊处理、不修改和控制 ClassLoader

  2. 仅支持方法级别的 Joint Points

  3. 非完整的 AOP 框架

  4. 与 IoC 进行了整合

  5. 与 AspectJ 的注解进行了整合


使用层面,有三种编程模型:

  1. <aop: 开头的 xml 配置。

    • 激活 AspectJ 自动代理<aop:aspectj-autoproxy/>

    • 配置<aop:config/>

    • Aspect <aop:aspect/>

    • Pointcut<aop:pointcut/>

    • Advice<aop:around/>、<aop:before/>、<aop:after-returning/>、<aop:after-throwing/> 和 <aop:after/>

    • Introduction<aop:declare-parents/>

    • 代理 Scope<aop:scoped-proxy/>

  2. 注解驱动

    • 激活 AspectJ 自动代理@EnableAspectJAutoProxy

    • Aspect@Aspect

    • Pointcut:@Pointcut

    • Advice@Before、@AfterReturning、@AfterThrowing、@After、@Around

    • Introduction@DeclareParents

  3. JDK 动态代理、CGLIB 以及 AspectJ 实现的 API

    • 代理:AopProxy

    • 配置:ProxyConfig

    • Join Point:JoinPoint

    • Pointcut:Pointcut

    • Advice:Advice、BeforeAdvice、AfterAdvice、AfterReturningAdvice、 ThrowsAdvice


核心实现类:

AOP 代理对象:AopProxy、JdkDynamicAopProxy、CglibAopProxy

AOP 代理对象工厂:AopProxyFactory、DefaultAopProxyFactory

AOP 代理配置:ProxyConfig

Advisor:PointcutAdvisor、IntroductionAdvisor

Advice:Interceptor、BeforeAdvice、AfterAdvice及子类

Pointcut:StaticMethodMatcher

JoinPoint:Invocation

Advisor 适配器:AdvisorAdapter、AdvisorAdapterRegistry

AdvisorChainFactory

AbstractAutoProxyCreator:BeanNameAutoProxyCreator、DefaultAdvisorAutoProxyCreator、AnnotationAwareAspectAutoProxyCreator

IntroductionInfo

代理目标对象来源:TargetSource


21、如何用 Spring 实现事件驱动编程?

JDK 中事件编程标准接口

  • 事件对象 java.util.EventObject

  • 事件监听器 java.util.EventListener


Spring 中的事件对应的类是 ApplicationEvent,事件的处理方式如下:

1、实现 ApplicationListener 接口,可以在 onApplicationEvent 方法上处理 ApplicationEvent

2、@EventListener 注解加载事件处理的方法上


需要将 ApplicationListener 注册为 Bean 或者

通过 ConfigurableApplicationContext#addApplicationListener 方法添加


事件的发布,可以通过 ApplicationEventPublisher 发布,也可以通过

ApplicationEventMulticaster 进行广播


22、如何用 Spring 实现国际化?

JDK 内一套国际化的标准

  • ResourceBundle 抽象类

  • PropertyResourceBundle propertes 文件获取国际化信息的实现类

  • MessageFormat 可以对文本进行格式化


Spring 在此基础上进行了整合,内建了 ResourceBundleMessageSource、

ReloadableResourceBundleMessageSource、

StaticMessageSource、DelegatingMessageSource


23、如何用 Spring 加载资源?

Spring 中定义了资源接口:

  • 只读资源 Resource

  • 输入流 InputStreamSource

  • 可写资源 WritableResource

  • 带编码的资源 EncodedResource

  • 上下文资源 ContextResource


内建了几种实现:

  • BeanDefinitionResource

  • ByteArrayResource

  • ClassPathResource

  • FileSystemResource

  • UrlResource

  • ServletContextResource


使用层面

  • 可以通过 @Value 注解注入 Resource 对象

  • 注入 ResouceLoader,loader 资源


24、Spring 如何自定义注解?

Spring 中最简单的自定义注解的方式就是使用现有的注解,标注在自定义的注解之上,复用原注解的能力。

/**
 * 自定义注解,继承自 @Component
 * 
 * @author ConstXiong
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Component
public @interface CustomComponent {

    String value() default "";
}

/**
 * 自定义 ComponentScan
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@ComponentScan
public @interface CustomComponentScan {

    /**
     * 别名
     */

    @AliasFor(annotation=ComponentScan.class, value="basePackages")
    String[] v() default {};

}


/**
 * 测试 Spring 自定义注解
 * 
 * @author ConstXiong
 */

@CustomComponentScan(v="constxiong")
public class Test {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test.class);
        System.out.println(context.getBean("u", User.class));
    }
}


25、Spring 框架用到了哪些设计模式?

  • 工厂模式:Spring 使用工厂模式,通过 BeanFactory 来创建对象

  • 单例模式:Bean 默认就是单例模式

  • 策略模式:Resource 的实现类,针对不同的资源文件,实现了不同方式的资源获取策略

  • 代理模式:Spring 的 AOP 功能用到了 JDK 的动态代理和 CGLIB 字节码提升

  • 模板方法模式:父类生成代码骨架,具体实现延迟到子类,如 JdbcTemplate、RestTemplate

  • 适配器模式:Spring AOP 中的 Advice 使用到了适配器模式,Spring MVC 中用到了适配器模式适配 Controller

  • 观察者模式:Spring 事件驱动模型就是观察者模式

    ......


26、Spring MVC 有哪些组件?

  • 前端控制器(DispatcherServlet)

  • 处理器映射器(HandlerMapping)

  • 处理器适配器(HandlerAdapter)

  • 拦截器(HandlerInterceptor)

  • 语言环境处理器(LocaleResolver)

  • 主题解析器(ThemeResolver)

  • 视图解析器(ViewResolver)

  • 文件上传处理器(MultipartResolver)

  • 异常处理器(HandlerExceptionResolver)

  • 数据转换(DataBinder)

  • 消息转换器(HttpMessageConverter)

  • 请求转视图翻译器(RequestToViewNameTranslator)

  • 页面跳转参数管理器(FlashMapManager)

  • 处理程序执行链(HandlerExecutionChain) 


27、Spring MVC 的运行流程是什么样的?

  1. 在 web 项目的 web.xml 文件配置 DispatcherServlet,启动 web 项目完成初始化动作

  2. http 请求到 DispatcherServlet

  3. 根据 HttpServletRequest 查找 HandlerExecutionChain

  4. 根据 HandlerExecutionChain 获取 HandlerAdapter、执行 handler

  5. Handler 执行完成返回 ModelAndView

  6. DispatcherServlet 进行结合异常处理 ModelAndView

  7. DispatcherServlet 进行视图渲染,将 Model 数据在 View 中填充

  8. DispatcherServlet 返回结果



源码查看思路

  • web.xml 配置 DispatcherServlet 是入口

  • DispatcherServlet 继承 FrameworkServlet 继承 HttpServletBean 继承 HttpServlet,web项目启动时自动调用 HttpServletBean 的 init 方法完成初始化动作

  • 当 http 请求过来,是 HttpServlet 根据请求类型(get、post、delete…) 执行 doGet、doPost、doDelete 等方法,被FrameworkServlet重写,统一调用 FrameworkServlet.processRequest 方法处理请求

  • 在 FrameworkServlet.processRequest 方法中,调用了 DispatcherServlet.doService() 方法,顺着这个方法就可以理清楚 spring mvc 处理 http 请求的整体逻辑


28、@RequestMapping 的作用是什么?

用来标识 http 请求地址与 Controller 类的方法之间的映射。

可作用于类和方法上,方法匹配的完整路径是 Controller 类上 @RequestMapping 注解的 value 值加上方法上的 @RequestMapping 注解的 value 值。

/**
 * 用于映射url到控制器类或一个特定的处理程序方法.
 */

//该注解只能用于方法或类型上
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {

    /**
     * 指定映射的名称
     */

    String name(default "";

    /**
     * 指定请求的路径映射,别名为path
     */

    @AliasFor("path")
    String[] value(default {};

    /** 
     * 别名为 value,使用 path 更加形象
     */

    @AliasFor("value")
    String[] path(default {};

    /**
     * 指定 http 请求的类型如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE. 
     */

    RequestMethod[] method(default {};

    /**
     * 指定映射 http 请求的参数
     */

    String[]params() default {};

    /**
     * 指定处理的 http 请求头
     */

    String[] headers(default {};

    /**
     * 指定处理的请求提交内容类型(Content-Type)
     */

    String[] consumes(default {};

    /**
     * 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
     */

    String[] produces(default {};
}


//指定 http 请求的类型使用
public enum RequestMethod {
  GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}


29、@Autowired 的作用是什么?

可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。
@Autowired 默认是按照类去匹配,配合 @Qualifier 指定按照名称去装配 bean。

@Controller
public class TestController {

    //成员属性字段使用 @Autowired,无需字段的 set 方法
    @Autowired
    private TypeService typeService;


    //set 方法使用 @Autowired
    private ArticleService articleService;
    @Autowired
    public void setArticleService(ArticleService articleService) {
        this.articleService = articleService;
    }

    //构造方法使用 @Autowired
    private TagService tagService;
    @Autowired
    public TestController(TagService tagService) {
        this.tagService = tagService; 
    }

}


30、项目中如何用 Spring 和 Spring MVC 框架的?

如果你在企业的项目中用过 Struts2 框架,那说明你搞 Java 可能在 5 年以上了。

在 Spring MVC 火之前,Struts2 + Spring + Hibernate 就是传说中的 SSH 框架,也有 Struts2 + Spring + MyBatis 即 SSM。后来渐渐就演化到 Spring + SpringMVC + MyBatis 成为了主流。再后来大家就都知道了。

Spring 成为后端开发框架的标准早已是事实。使用 Spring 最大的好处它的 IoC 和 AOP 功能,项目中一般通过 xml 配置文件 + 注解的方式,把 Bean 的管理交给 Spring 的 IoC 容器;日志、统计耗时次数、事务管理都交由 AOP 实现,xml 和 注解申明的方式都会使用到。

Spring MVC 也基本是必用的,通过 web.xml 的配置、@Controller、@Service、@Repository,完成 http 请求到数据库的 crud 再到 view 层展示,整个调用链。其中还要配置对象转 json 的 Converter、登录拦截器、文件上传大小限制、数据源及连接池相关等等…


浏览 15
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报