Spring面试题
SpringIOC的核心是配置解析、容器管理、依赖注入;
spring中bean的生命周期顺序是实例化 --> 依赖注入 --> 初始化 --> 销毁;
Spring面试题
什么是Spring
首先,Spring是一个生态,但是呢,这个生态里面又有个Spring Framework框架。
所以从Spring生态来说,它包含了Spring Framework、Spring MVC、Spring Boot,以及Spring Cloud,和一些其他的框架如Spring Gateway、Spring Security、Spring Data,还有最新出来的Spring AI。
从Spring Framework来说呢,它是一个IOC的容器框架,并包含了AOP切面和Rest Template接口请求等很多组件工具的框架。
它最核心的功能是IOC和AOP,其中IOC就是负责处理解决代码层跟层(例如:controller层和service层)之间的,对象的解耦问题。AOP切面是针对现有业务的一个没有痕迹的增强,面向切面编程。
Spring可以没有AOP等功能,但是一定不能没有IOC,这个是所有Spring生态的一个基础。
Spring中BeanFactory的作用
BeanFactory是Spring框架最核心的IOC容器接口,是Spring的一个顶级类,用的简单工厂设计模式,负责Bean对象的创建。
在实际使用中,一般通过构造方法、@Autowired等注解进行自动注入。
其getBean方法,参数一般是Bean的名称,通过Bean名称获取到对应的BeanDefinition,通过BeanDefinition里存储的信息,去通过反射创建实例。
BeanDefinition的信息,是由自动配置指定类文件信息,然后通过解析获取到类的注解信息、class等信息(包括单例、多例模式,懒加载等配置)、类之间的依赖关系。
在BeanFactory的基础上还有一个ApplicationContext,ApplicationContext提供了更丰富的接口与功能,包括AOP和Event事件。
Spring中BeanDefinition的作用是什么?
主要就是用来存储IOC容器所管理的Bean对象的定义信息,包括Bean对象对应的类路径,用到的定义注解(懒加载、作用域等),类之间的依赖关系。
说一说Spring的优缺点?
版本一
拥有非常完善的社区生态,且可以较为方便的集成各类中间件。
Spring最核心的是控制反转(IOC容器),实现了依赖注入(DI),统一管理Bean对象,降低了代码的耦合性。
Spring的AOP面向切面,可以将代码公共代码进行剥离,例如可以剥离代码里的日志记录,权限过滤,数据库事务管理等相关代码,提高了代码的可维护性与灵活性。
Spring有缺点吗?
有,Spring功能很完善,提供了很多的扩展接口,但也正因为如此,他的底层非常的复杂,如果想要深入学习存在一定的困难。
版本二-精简
Spring的优点很突出,主要有三点:
- IoC容器,用依赖注入管理对象,让代码解耦,特别好做单元测试。
- AOP,能把事务、日志这些横切关注点从业务里抽离,代码更干净。
- 生态和集成,社区活跃,能像胶水一样很方便地整合各种中间件。
至于缺点,最明显的就是:功能太强大导致底层非常复杂,学习曲线比较陡峭,想深入理解和排查问题有难度。”
版本三-详细
Spring的优点
集成了很多的实用组件,例如 AOP 切面、Rest Template 接口请求工具(不用额外引入OkHttp和HttpClient)、异步调用。
IOC容器管理对象,使我们对象与对象之间的耦合度极大程度的降低,也方便了我们去维护对象,例如:
我们需要将一个对象设置为单例,不用 Spring 呢,我们就需要去额外使用单例模式自己实现,自己维护,而在 Spring 里,只需要将这个对象设置为 bean (用@Component、@Service等)托管给IOC容器就行,因为Spring的IOC默认就是单例的。(DefaultListableBeanFactory类工厂里存在一个命名为beanDefinitionMap的Map集合,专门用来存放bean的定义信息,key --> beanName,value --> BeanDefinition对象)
如果想将对象设为多例的,也很简单,只需要设置@Scope为prototype。
想要将对象配置为懒加载,则只需要给它加上@lazy注解。
Spring针对bean提供了很多的配置操作。
Spring提供的AOP切面工具,可以在不改动原有业务代码的情况下,去对其做一个增强,可以大量减少我们的重复代码,同时也会提高我们的开发效率,和便于维护。
Spring提供了一个声明式事务,@Transactional注解,可以让我们从繁杂的事务管理代码里面脱离出来。
Spring集成了很多的开发框架,拥有很好的粘合度,集成能力非常强,只需要简单的配置一下即可。
提供了很多的底层扩展接口,供外部扩展。底层源码写的非常好,用到了很多的设计模式(工厂模式、单例模式等)和反射,看了之后受益匪浅。
Spring的缺点
在应用层面感觉没有缺点。
鸡蛋里挑骨头,Spring大而全,集成了这么多的框架、功能,是需要提供非常非常多的扩展点,这也就导致它的底层会非常的复杂,代码量也会非常的庞大,对于深入学习源码带来了很大的困难。
上层对外越简单,下层内部就会越复杂。
说一说SpringBean的生命周期
spring中bean的生命周期顺序是实例化-->依赖注入-->初始化-->销毁。
实例化:在启动容器时,IOC容器会通过反射实例化一个初始的bean对象。
依赖注入:通过构造方法、注解将bean对象注入到使用地方。
初始化:执行@PostConstruct注解或InitializingBean接口的实现方法。以及如果有实现BeanPostProcessor接口,那么还会执行其执行前方法、执行后方法的实现代码,AOP的动态代理就是用这个实现的,可用来处理公共的业务逻辑。
销毁:ioc容器在关闭时(也就是停用服务),会去执行@DisposableBean朱姐或Disposablebean接口的实现方法。
SpringIOC的实现机制是什么
SpringIOC的核心是配置解析、容器管理、依赖注入
配置解析:
解析预先设置的配置,xml、java config、ComponentScan注解,解析类信息后组装为BeanDefinition,用于存储类的元数据信息。
容器管理:
将解析得到的Bean元数据信息缓存到实例池 beanDefinitionMap进行统一管理。
依赖注入DI:
在业务代码里使用@AutoWrite、@Resources等方式获取Bean对象时,会先从beanDefinitionMap实例池里获取到对应Bean的类信息,然后使用反射机制来创建一个实例,最后返回给业务代码使用。
也就是业务系统将创建Bean对象的权利交给了Spring,由SpringIOC去帮忙创建Bean对象,这就是控制反转,而依赖注入是控制反转的一种实现方式。
Spring事务的传播行为有哪几种
分为三个派系共7种
协同派
REQUIRED(默认),如果没有事务则新建一个事务,有则直接加入。
SUPPORTS,如果有事务则加入,没有事务也会继续执行。适合查询场景操作。
NESTED,在父事务中创建子事务,父事务失败了,子事务一起回滚,但是子事务失败了,可以单独只回滚子事务。适合复杂流程中的允许局部失败的场景。
独立派
REQUIRES_NEW,无论外部是否已经存在事务,都会新建一个事务,并挂起外部事务。适合日志记录类似的功能场景,无论核心业务是否成功,操作记录的事务都需要可以独立提交。
NOT_SUPPORTED,完全不支持事务,如果外部有事务,也会挂起。适合无事务控制需求的查询操作。
约束派
MANDATORY,强制必须在事务中执行,否则报错。适合关键的业务链。
NEVER,当前操作如果有事务,直接报错,不执行操作。没想到适合的场景。
说一下Spring事务的失效原因
- 调用内部类
- 事务的传播行为设置不当
- 设置了rollbackFor指定特定的生效异常类型
- 用try cache自行捕获了异常
情况一:
主要情况有在定义了@Transaction注解的方法里,调用本类的其他方法。
不生效的原因是Spring的事务是使用AOP实现的,切入点是bean对象,是目标类的代理对象,而使用this.xxx()调用本类方法时,用的不是代理对象,而是目标类对象的本身,所以是绕过了代理对象,也就导致事务的AOP切面介入不了这个方法的执行。
情况二:
还有事务的传播行为设置不当也会导致事务失效,例如设置为 @Transactional(propagation = Propagation.NOT_SUPPORTED) ,不支持事务,无事务执行。
情况三:
在@Transactional注解上设置了rollbackFor指定特定的生效异常类型,当抛出异常不匹配时,自然不会触发事务。
情况四:
在代码里使用try cache自行捕获了异常,这就表示这个方法不会再捕获到异常了,也自然就不会触发事务。
Spring的AOP一般用它来做啥,你怎么实现的
Spring的AOP是专门用来做现有业务功能增强的,在不改动现有业务代码的情况下,添加额外的公共的扩展功能,例如接口请求的日志记录,数据参数安全校验,Spring的事务管理Transaction也是通过AOP实现的。
实现的前提需要先引入spring-aop依赖,然后在切面实现类上面添加@Aspect注解,表示这是一个切面功能,然后还需要定义切入点,用注解@Pointcut,他的切入点可以是注解,也可以是类的方法。
有了切入点后,还需要定义一个@Around标识的方法,对该方法进行一个环绕,就是在这个方法执行前应该做什么增强操作,这个方法执行结束后应该执行什么增强操作。还有其他的@Before执行方法前执行、@AfterReturning方法执行成功返回后执行、@AfterThrowing在方法抛出异常后执行、@After在方法执行结束后执行。
