- Spring的特性?Spring有哪几个模块?
Spring特性:
Spring是一个企业级开发框架,用以简化软件开发和配置。通过控制翻转(IoC)技术将创建对象的控制权交由Spring框架来实现。
- Spring的低侵入设计,对业务代码的污染比较少。
- Spring的DI机制将对象的创建交由Spring框架来实现,降低了代码的耦合性。
- Spring的AOP功能将公用功能统一管理,提升了代码复用的效率。
- Spring对主流框架提供了很好的结合和支撑。
Spring模块:
- Spring core:core、context、bean、SpEL
- AOP:AOP、Aspect
- Database:JDBC、ORM、transaction
- Web:SpringMVC、servlet
- Spring如何解决循环依赖?
解决方案:
- Spring对于属性/setter注入的是通过三级缓存的方式来避免循环依赖,但也需要在单例模式下才能生效;
- Spring对于其他自身无法解决的循环依赖可以通过其他方式来解决,详细请参考:《spring解决循环依赖的方案》
三级缓存解决循环依赖原理:
Spring通过三级缓存和提前暴露的思路来解决循环依赖。主要思想是将循环依赖的对象实例放到缓存中,让对方先实例化再进行初始化。三级缓存包括:SingletonObjects、EarlySingletonObjects、SingletonFactories。
- SingletonObjects:已经实例化和初始化的bean放到该map中;用于保存beanName和bean实例之间的关系。
- EarlySingletonObjects:已经实例化,但是没有初始化的bean放到该map中;也是用以保存beanName和bean实例之间的关系,和SingletonObjects区别是他是通过SingletonFactories生成的,为解决循环依赖,而没有进行初始化的bean。
- SingletonFactories:创建bean的factory放到该map中;用以保存beanName和BeanFactory类之间观念的。
如果对象A和对象B相互循环依赖,会通过以下步骤解决:
- 对象A实例化前会去查询SingletonObjects,如果没有,将其工厂类后放置到SingletonFactories中,再进行下一步属性赋值,由于依赖对象B,所以需要对象B实例化;
- 对象B实例化前会去查询SingletonObjects和EarlySingletonObjects,如果没有,将其工厂类后放置到SingletonFactories中,再进行下一步属性赋值,由于依赖对象A,会去查询SingletonFactories,同个getObject()方产生的bean添加到二级缓存EarlySingletonObjects中,同时对象B实例化成功;
- 对象A在EarlySingletonObjects了,继续进行属性赋值,能够正常属性赋值对象B;
- 依赖注入成功后会将SingletonFactories和EarlySingletonObjects中的对象删除。
需要三级缓存而非二级缓存的原因?
对象放入SingletonFactories后可能会进行AOP操作,由于AOP是初始化之后实现的,所以有必要在此之前将其放入到EarlySingletonObjects,待初始化完成后在将其移至SingletonObjects,这样才能保障注入依赖的对象和AOP后的对象保持一致。
- Spring 是如何解决循环依赖的?:https://segmentfault.com/a/1190000039091691
- Spring中的循环依赖及解决:https://juejin.cn/post/6985337310472568839
- Spring IOC 容器源码分析 - 循环依赖的解决办法:https://www.tianxiaobo.com/2018/06/08/Spring-IOC-容器源码分析-循环依赖的解决办法/
- 面试必杀技,讲一讲Spring中的循环依赖:https://developer.aliyun.com/article/766880
- https://www.cnblogs.com/java-chen-hao/p/11137571.html
- spring本身解决不了的循环依赖类型?
- 通过构造方法注入,如果有循环依赖的话,就会报错;原因是bean在进行实例化的时候第一步就是调用构造方法,执行构造方法时候还不能得到实例化后的对象,所有这个阶段无法解决循环依赖;
- 通过setter方法注入,并且作用域是prototype是会报错的; 原因是spring的单例bean在spring容器启动的时候创建,这个时候spring有解决循环依赖的机制,对于多例他在spring容器运行过程中随时都会创建,所以这个时候很难保障。
- spring中运用了哪些设计模式?
- BeanFactory和ApplicationContext应用了工厂模式。
- 在 Bean 的创建中,Spring 也为不同 scope 定义的对象,提供了单例和原型等模式实现。
- AOP 领域则是使用了代理模式、装饰器模式、适配器模式等。
- 各种事件监听器,是观察者模式的典型应用。
- 类似 JdbcTemplate 等则是应用了模板模式。
- Spring容器和Bean的扩展点?
针对容器级别:
- BeanFactoryPostProcessor:当BeanFactory初始化后会执行该接口;
- SmartLifeCycle:会监听容器启动的各个阶段,可以对对应的阶段进行操作;
针对Bean级别:
- BeanPostProcessor:每次实例化Bean后都会调用该接口;
- InitializingBean:初始化前会调用该接口;
- DisposableBean:销毁后会调用该接口;
针对方法级别:
- @PostConstruct:构造方法执行完后会执行该方法
- @PreDestroy:销毁前会执行该方法;
Aware类接口:
- BeanFactoryAware/ApplicationContextAware:会获取BeanFactory、ApplicationContext,可以对其操作;
- BeanNameAware:会获取BeanName,可以对其操作;
#IoC/DI#
- spring的IoC和DI分别指什么?
- IoC是一种编程范式,指的是对象的依赖关系在编译期就已经确定好,但是实例是在运行期来动态生成,例如普通程序的对象是需要new出来的,而spring基于依赖注入(DI)的方式,提前定义好依赖关系,在对象实例化后将依赖关系注入。
- DI是IoC的一种实现方式,就指通过将依赖对象在实例化的时候动态生成和引用的方式来实现对象的控制倒置。
IoC容器的优点:
- IoC容器实现了Bean实例依赖的解耦,就是相互依赖的Bean不必等待谁先谁后创建,直接从IoC容器中取即可。
- 但需要创建多个bean的时候,使用单例模式可以避免多次创建带来的资源浪费。
IoC的实现思想:
其核心思路是将Bean的配置信息放到一个Map中,但需要使用的时候再从这个Map中获取;具体实现是在IoC容器启动时候会解析Bean的配置信息,将其组装成BeanDefinition放到BeanDefinitionRegistry中,这个BeanDefinitionRegistry就是个Map,在掉用getBean()方法时候,从BeanDefinitionRegistry中获取BeanDefinition的定义,根据这个定义实例化正式的Bean,最终初始化后的对象保存在SingletonObject中,其中保存了BeanName和ObjectInstance的映射关系。
ApplicationContext context= new AnnotationConfigApplicationContext("cn.wxxlamp.spring.ioc"); Bean bean = context.getBean(Bean.class); bean.use(); |
- Spring IoC容器启动的过程?
Spring的IoC容器的启动过程,核心流程是将bean的配置项从不同渠道,包括XML、注解或者配置文件中读取和解析后,生成BeanDefinition的过程,在这过程中IoC容器会进行refresh操作,这个过程可以设置一些BeanPostProcesser的前置或后置操作,在执行完这些操作后,BeanDefinition就会被注册到BeanDefinitionRegistry容器中。
整体IoC容器的启动过程分为3个阶段:定位—>加载—>注册
- 定位: 通过ResourceLoader来完成资源的定位,后续的Bean配置项透视通过Resource资源文件来读取和解析的。
- 加载:ApplicationContext容器调用refresh()方法,解析Bean配置信息将其解析成BeanDefinition,然后调用一系列容器、Bean级别的前置后置处理器,包括调用BeanFactoryPostProcessor 的前置操作。
- 注册:BeanDefinition实例注册,将生成的BeanDefinition放到容器的缓存池BeanDefinitionRegistry中。
总结:
对于单例bean。在getBean()调用的时候创建,对于多例在使用的时候创建。
spring5 源码深度解析-----ApplicationContext容器refresh过程:https://www.cnblogs.com/java-chen-hao/p/11579591.html
- Spring Bean的生命周期?
- Bean的生命周期包括:实例化-->属性赋值-->初始化-->销毁
- 实例化:在Bean实例被调用或被依赖的实例被创建,该Bean实例会被创建,利用该类的构造方法来实例化该类。
- 属性赋值:当该Bean的属性依赖其他对象时候,比如属性中有被@Autowired注解的属性,会将其他对象的引用赋予给他。
- 初始化:初始化主要用以进行一些预处理和后处理。
- 容器级别方法:主要包括BeanPostProcessor一系列方法,在容器启动的时候就调用的方法;
- Bean级别方法:主要是通过Aware类的方法,比如BeanNameAware方法等,它能够拿到BeanName等资源,该级别方法是在初始化之前完成;
- 单个Bean生命周期级别方法:主要通过重写InitializingBean接口中的一些方法来是吸纳,包括初始化和销毁这两个方法;
- 销毁:当容器被关闭时该Bean会被销毁,销毁前的操作看其时候有定义。
总结:
如果需要AOP,则该bean是代理类实例化后得到的对象,代理实例是在容器初始化时生成的。
- 一文读懂 Spring Bean 的生命周期:https://segmentfault.com/a/1190000040365130
- spring5 源码深度解析----- IOC 之 bean 创建:https://www.cnblogs.com/java-chen-hao/p/11139157.html
- spring的常见IoC容器?
spring中有BeanFactory和ApplicationContext容器。其区别是:
- BeanFactory是所有Bean集合的工厂类,也是所有容器类的父类,其定义了IoC容器的主要方法。
- ApplicationContext容器面向开发者使用的容器类,其丰富了方法,包括支持国际化、支持统一资源文件读取、支持监听器等。
applicationContext的实现有哪几种:
- FileSystemXmlApplicationContext:此容器从一个XML文件中加载beans的定义,XMLBean配置文件的全路径名必须提供给它的构造函数。
- ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。
- WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。
- Spring 依赖注入(DI)是什么?有哪几种实现方式?
依赖注入(DI)就是不需要创建对象,只需要描述对象的依赖关系,spring IoC容器读取配置来注入对象。
- 构造方法注入;
- setter方法注入;
- 对象属性注入;
- spring中Bean自动织入(autowire)的概念?
bean放入到context容器中,spring需要将其他对象组合到一起,称为bean的自动织入。有两种自动织入的方式:
- byType
- byName
自动织入的注解:
- @Autowire:根据byType的方式自动注入,是spring自带的注解;
- @Qualifier:可以根据byName方式自动注入
- @Resource:默认按照byName注入,没有找到按照byType注入,是J2EE自带的注解;
备注:@Autowired注解相当于XML中的autowire属性的注解方式的替代,即通过@Autowired注解进行依赖注入不需要再进行配置。
- spring中@Autowire和@Resource的区别?
- @Autowire和@Resource都是Spring中进行依赖注入的注解。
- @Autowire是Spring中以byType方式进行依赖注入的,结合@Qualifier注解可以以byName方式注入。
- @Resource是J2EE中以byName方式进行依赖注入的,如果byName找不到才会以byType方式进行依赖注入。
- Spring中配置Bean的几种方法?
- 基于XML配置,一般不用了;
- 基于Java Config配置,使用@Configuration、@Bean和@ComponentScan注解;
- 基于注解配置,使用@Component、@Autowire、@Resource等注解;
- Spring中BeanFactory和FactoryBean的区别?
BeanFactory是生产bean实例的工厂类,可以通过getBean(beanName)方法获取bean实例。
FactoryBean是由于某些bean的生产并不适用默认BeanFactory来实现,可以通过实现FactoryBean接口来自定义生产bean的工厂类xxxBeanFactory。
#AOP#
- spring AOP中的概念?
- 切面(Aspect):一个关注点的模块化;
- 切点(pointCut): 切面在程序中的关注点,就是切面逻辑执行的地方,常见的是方法,可以理解为切面执行的where;
- 通知(Advice):切面对切点可以产生多个动作,比如切入前、切入后等,通知做出具体执行时间的规定,可以理解为切面执行的when;
- 连接点(JointPoint):程序运行过程中的某一行为,比如方法中的某个过程的参数;
- spring AOP的原理?
AOP的应用:
- 讲到AOP必须讲到他的三个组件,包括切片、切点和通知。Spring AOP有两种配置方式,分别是基于xml和基于注解的形式;
- Spring AOP的底层原理主要是基于动态代理的,有两种方式,一种是基于jdk的动态代理,一种是基于cglib的动态代理;
AOP是利用动态代理来创建对象的,根据实现是否需要接口类分为以下两种:
- 对于目标类有对应的接口类,Spring AOP通过JDK proxy去实现;
- 对于目标类没有对应的接口类,Spring AOP通过CGLib去实现;
- Spring AOP集成了AspectJ框架,所以复用了AspectJ注解等使用方式;
#SpringMCV#
- Spring MVC的原理?
Spring MVC的原理可以通过三个步骤来简单讲述:
- 配置阶段:这个阶段主要就是配置web.xml和ditspatcher-servlet.xml这两个配置文件,前者配置ditspatcher-servle.xml路径,同时在webApplicationContext里面配置spring的配置文件路径;
- 初始化阶段:这一阶段主要是webApplicationContext的初始化操作,还有就是spring mvc的几大组件的初始化;
- 请求响应阶段:一个request请求发送给dispatherservlet,这个是整个springmvc的中心,负责调配各种请求,根据这个请求,或者说根据这个url匹配到一个handlerMapping,这个mapping就是映射url到具体的方法的,根据具体参数不同选择不同的handlerAdapter进行处理,处理完了后返回给dispacher一个modelAndView,modelAndView再去请求ViewResolver,请求合适的viewResolver,经过处理再返回给dispatherservlet,这个view和model结合渲染成html给前台;
其中dispatcherServlet是SpringMVC的核心组件,几乎所有的操作都是通过dispatcherServlet来做中转的。
#事务#
- spring 事务管理?
Spring的事务管理的作用是保证每一次和数据库的交互都是可靠的,在Spring中有两种事务管理方式。
- 编程式事务管理:这个是在代码中实现事务管理,通过transactionManager,transactionDefination,transactionStatus这三个组件来构建;通过调用transactionManager里的commit()和rollback()方法来提交和回滚; 可以直接调用Spring JDBC中的transactionTemplate来操作。
- 命令式事务管理:申明式事务通过Spring AOP技术实现,实现原理是在方法调用之前调用transactionIntercepter把其拦截,给他事务开始,方法执行后,在调用给他事务结束,申明式事务通常用配置形式来实现的,在需要事务管理的方法钱加上@Transactional注解就好了;
- Spring事务的隔离级别?
- 未提交读;
- 已提交读;
- 可重复读;
- 串行化读;
总结:和数据库一样;
- Spring事务的传播级别?
Spring的事务规定了7种事务的传播级别,默认的传播机制是 REQUIRED
● required,如果不存在事务则开启一个事务,如果存在事务则加入之前的事务,总是只有一个事务在执行
● required_new,每次执行新开一个事务
● supported,有事务则加入事务,没有事务则普通执行
● supported_not,有事务则暂停该事务,没有则普通执行
● mandatory,强制有事务,没有事务则报异常
● never,有事务则报异常
● nested,如果之前有事务,则创建嵌套事务,嵌套事务回滚不影响父事务,反之父事务影响嵌套事务
失败总结:
Spring中比较容易失效的就是通过 @Transactional 定义的声明式事务,他在以下几个场景中会导致事务失效:
1、@Transactional 应用在非 public 修饰的方法上
2、@Transactional 注解属性 propagation 设置错误
3、@Transactional 注解属性 rollbackFor 设置错误
4、同一个类中方法调用,导致@Transactional失效
5、异常被catch捕获导致@Transactional失效
6、数据库引擎不支持事务
- JDBC进行数据库操作的步骤?
利用JDBC进行数据库操作要以下几个步骤:
(1)加载驱动;
(2)建立连接,建立JDBC和数据库间的连接Connection;
(3)创建语句、执行语句,创建Statement或者PrepareStatement;
(4)执行结果,关闭连接;
JDBC的六大步骤:https://blog.csdn.net/gbdlacxzdmg/article/details/125562195
#其他#
- Spring中的Bean是否是线程安全的?
Spring中的Bean不是线程安全的,其没有提供保障线程安全的策略。有以下集中方法来保障线程安全。
- Bean的作用范围为Singleton时候线程不安全,因为容器中只有一个Bean实例,如果使用Prototype不存在多线程访问,每次使用的时候都会重新创建一个实例。
- 可以通过将Bean的作用范围设定为prototype多例形式,这样就不存在线程的问题;
- 通过ThreadLocal来保障线程安全,为为每个线程创建一个变量副本,各自不影响。