概述
1、 Spring的优点和缺点
a) 方便解耦,简化开发 集中管理对象,对象和对象之间的耦合度减低,方便维护对象。
b) AOP编程的支持 在不修改代码的情况下可以对业务代码进行增强,减少重复代码,提高开发效率,维护方便。
c) 声明事物的支持 提高效率,只需要一个简单的注解@Transactional
d) 方便程序的测试 Spring实现测试,可以结合junit非常方便测试Spring Bean
e) 方便集成各种优秀的框架 拥有非常强大的粘合度,集成能力非常强。
f) 降低Java EE API的使用难度 简化开发,封装了非常多的功能性代码。
g) Java源码是经典学习范例 学习到了Spring底层的实现、反射设计模式都是值得学习的。
h) 明明是一个很轻量级的框架,给人的感觉却是大而全。
2、 SpringIOC的作用、优点
a) 控制反转,它把传统上由程序代码直接操控的对象的调用权,交给容器,通过容器来实现对象组件的装配和管理。也就是控制权的转移。
b) IOC做的事有:创建、管理、装配、配置对象。生命周期也是IOC控制。
c) 作用:解耦,统一管理对象。
d) 优点:降低耦合度,集中管理对象,方便维护。最小的代价和最小的侵入性使松散耦合得以实现。IOC容器支持饿汉式初始化和懒加载。
3、 SpringIOC的实现机制
a) 工厂+反射的机制。Beanfactory.getBean(简单工厂的体现)
i. 放射比如baseService=(BaseService) Class.forName(className).newInstance。
4、 IOC和DI的区别是什么
a) IOC是一个理念,思想
b) DI是IOC的实现,是实现中重要的一环
5、 紧耦合和松耦合的区别
a) 紧耦合:紧密耦合是指类之间的高度依赖
b) 松耦合:是通过单一的职责和关注点分离、依赖倒置的设计原则来实现的。
c) IOC也是实现了这几个原则去送耦合。单一职责原则、接口分离原则、依赖倒置原则(比如说主干代码(上层),次要功能(下层)缺失,上层依旧可以正常启动)
6、 BeanFactory的作用
a) BeanFactory是Spring中非常核心的一个顶层接口
b) 职责:生产Bean
c) 简单工厂的设计模式,通过调用getBean传入标识生产一个Bean
d) 它有非常多的实现类,每个工厂都有不同的职责(单一职责)功能,最强大的工厂是:DefaultListableBeanFactory Spring底层就是使用的该实现工厂进行生产Bean
e) BeanFactory它也是容器 Spring容器(管理着Bean(对象)的生命周期),比如最常听说的ApplicationContext
7、 FactoryBean和ApplicationContext有什么区别
a) ApplicationContext实现了FactoryBean,ApplicationContext不生产,而是通知BeanFactory生产。ApplicationContext做的事情更多,比如说会自动把我们配置的Bean注册进来。FactoryBean需要自己手动注册。 ApplicationContext更多的还有,加载环境变量、支持多语言、实现事件监听、注册很多对外扩展点。
b) 共同点:都可以作为容器。
8、 BeanFactory和FactoryBean有什么区别
a) BeanFactory是一个工厂,也就是一个容器,是用来管理和生产bean的
b) FactoryBean是一个Bean,但是它是一个特殊的Bean,所以也是由BeanFactory管理的。不过它是一个接口,
c) 不过BeanFactory不是一个普通的Bean,它会表现出工厂模式的样子,是一个能产生或者修饰对象生成的工厂Bean。当一个类继承BeanFactory获得Bean将调用getBean方法。
9、 BeanDefinition的作用
a) Bean的图纸,定义了Bean生产的要素,比如类型、是否抽象,单例,名字,加载方式、装配方式。
b) 多个BeanDefinition放入一个BeanDefinitionMap中,key就是Bean的名字,value就是BeanDefinition对象。
10、 SpringIOC的加载过程,可以参考上图。
a) 概念态:配置形态
b) 定义态:封装bean的指标属性,判断是否符合生产标准,比如单例,非懒加载。
c) 纯净态:实例化结束。
d) 成熟态:属性赋值、初始化
初始化结束,调用方法,addSingleton(),一级缓存,单例缓存池,用于保存我们所有的单实例bean。
Private final Map<String,Object> singletonObjects=new ConcurrentHashMap<>;
11、 SpringIOC的加载过程,详细
概念态->定义态
a) 实例化一个ApplicationContext的对象
b) 调用bean工厂后置处理器完成扫描
c) 循环解析扫描出来的类信息
d) 实例化一个BeanDefinition对象来存储解析出来的信息
e) 把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来,以便后面实例化bean
f) 再次调用其他bean工厂后置处理器
定义态->纯净态
g) 当然spring还会干很多事情,比如国际化,比如注册BeanPostProcessor等等,如果我们只关心如何实例化一个bean的话那么这一步就是spring调用finishBeanFactoryInitialization方法来实例化单例的bean,实例化之前spring要做验证,需要遍历所有扫描出来的类,依次判断这个bean是否Lazy(懒加载),是否prototype,是都abstract等等。
h) 如果验证完成spring在实例化一个bean之前需要推断构造方法,因为spring实例化对象是通过构造方法反射,故而需要知道用哪个构造方法
i) 推断完构造方法之后spring调用构造方法反射实例化一个对象;注意我这里说的是对象、对象、对象;这个时候对象已经实例化出来了,但是并不是一个完整的bean,最简单的体现是这个时候实例化出来的对象属性是没有注入,所以不是一个完整的bean;
纯净态->成熟态
j) Spring处理合并后的beanDefinition
k) 判断是否需要完成属性注入
l) 如果需要完成属性注入,则开始注入属性
初始化
m) 判断bean的类型回调Aware接口,如果需要AOP,则创建AOP
n) 调用生命周期回调方法
o) 如果需要代理则完成代理
创建完成
p) Put到单例池——bean完成——存在spring容器当中
12、 Spring的扩展点,在什么时候调用
a) 执行BeanFactoryPostProcessor的postProcessBeanFactory方法
在注册BeanDefinition的时候对BeanFactory进行扩展
IOC加载时注册BeanDefinition的时候会调用
b) 执行BeanDefinitionRegisteryPostProcessor的postProcessorDefinitionRegistry方法
动态注册BeanDefinition(定义)
IOC加载时注册BeanDefinition的时候会调用,这个方法比上面那个方法先调用
BeanDefinitionRegisteryPostProcessor继承BeanFactoryPostProcessor
c) Bean在生命周期中会调用bean的后置处理器9次
d) BeanNameAware表示,接口实现的参数会有BeanName,可以实现对BeanName进行操作,不同的Aware提供了不同的API,调用时机初始化的时候。
e) 生命周期的回调接口:初始化、销毁
BeanPostProcessor实现类的postProcessBefoureInitialization方法 可实现指定bean的初始化方法。
13、 JavaBean和SpringBean的区别。
a) Bean组件
b) 由SpringIOC容器管理的对象就是springBean,实例化则为Bean
14、 配置Bean有哪几种方式
a) Xml,配置文件
b) 注解:@Component(@Controller,@Service,@Repostory),反射机制
c) javaConfig:@Bean,可以自己实例化过程
d) @import 3种方式
i. ImportSelector,通过这种方式返回一个数组String[]
传入完整类路径,可以是数组,可以一次性传入多个
ii. ImportBeanDefinitionRegistrar
Bean定义注册器
iii. ImportResource
直接导入 类名.class
15、 Spring实例化bean方式的几种方式
a) 构造器方式(反射,@Component),需要类名
b) 静态工厂的方式;类名+静态方法
c) 实例工厂的方式(@Bean),需要类名和方法
d) FactoryBean方式,类继承FactoryBean方法,重写方法
16、 Bean有哪几种作用域
a) 单例
b) 多例
c) Request,一个请求创建一个对象
d) Session,一个会话创建一个对象
e) Application
17、 单例bean的优势
a) 性能:内存,jvm,获取速度快(缓存获取)
b) Jvm:jvm判断机制是一个个对象判断过去看是否还有用,如果对象少了,jvm效率也就高了
18、 Spring的单例bean是线程安全的么
a) 如果在类中声明成员变量,并且有读写操作(有状态),就会线程不安全
b) 如果成员变量是声明在方法中,,单例bean是线程安全的
19、 spring如何处理线程并发问题
a) 设置为多例
b) 将成员变量放在ThreadLocal中
Private ThreadLocal username=new ThreadLocal<>();
c) 在方法上设置同步锁(会影响服务器吞吐量)
20、 什么是bean的装配,什么是bean的自动装配
a) 装配,根据bean名,自己装配
b) @AutoWrite,自动装配,找到合适的,自动装入。
21、 自动装配有哪些限制
a) 一定要声明set方法
b) 基本数据类型不能自动装配,比如String
c) 自动装配不如显式装配精确
22、 自动装配的方式有几种
a) no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
b) byName,通过bean的名称进行自动装配(set方法的方法名字后缀)
c) byType,根据类型
d) constructor,根据构造方法
23、 Bean的生命周期回调方法和顺序
a) 初始化和销毁各有三种方式,
b) 三种方式为:注解,接口,xml配置,顺序为从左到右,注解最先执行
24、 Spring框架中bean的生命周期
a) 实例化
i. 通过反射去推断构造函数进行实例化
ii. 实例工厂、静态工厂
b) 属性赋值
i. 解析自动装配(byname bytype constraintor none @Autowrired )DI的体现
ii. 循环依赖
c) 初始化
i. 调用XXXAware回调方法
ii. 调用初始化生命周期回调(三种)
iii. 如果bean实现aop创建动态代理
d) 销毁
i. 在spring容器关闭的时候进行调用
ii. 调用销毁生命周期的回调
25、 Spring如何解决bean循环依赖的
a) 采用三级缓存,也就是三个Map
i. 一级缓存:存储完整的bean
ii. 二级缓存:避免多重循环依赖的情况,重复创建动态代理。比如说A里面有B,C。B里面有A,C里面有A。
iii. 三级缓存:
- 缓存是函数接口:通过方法(把bean的实例和bean名字传进去aop创建)
- 不会立即调用,如果立即调用,那么将会导致bean是否循环依赖,都调用
- 会在ABA(第二次getBean(A)才会去调用三级缓存(如果实现了aop才会创建动态代理,如果没有依然返回的Bean实例))
- 放入二级缓存(避免重复创建),在第二次getBean的时候,如果二级缓存有,就不会到三级缓存,也就避免了重复创建动态代理。
b) 二级缓存能不能解决循环问题
i. 如果只是死循环问题:一级缓存就能解决:无法避免在并发下获取不完整的bean。
ii. 二级缓存也可以解决死循环问题:只不过如果出现重复循环依赖,会多次创建动态代理
c) Spring有没有解决多例Bean的循环依赖
i. 多例不会使用缓存(每次使用都需要重新创建)
ii. 不缓存早期对象就无法解决循环
d) Spring有没有解决构造函数参数Bean的循环依赖
i. 构造函数的循环依赖也会报错
ii. 可以通过人工进行解决:@Lazy
就不会立即创建依赖的bean了,而是等到用到才通过动态代理进行创建。
26、 Spring如何避免在并发情况下获取不完整的bean
a) 实例化之后,如果没有经过属性赋值和初始化获取到的bean将是不完整的
b) 解决方式:两个同步锁,两次检查一级缓存,ABA(存在循环依赖)
i. 假设线程一运行,线程一给2,3级缓存加锁,线程二第一次读取一级缓存,取到的为空,这个时候无法读取2,3缓存,线程2被阻塞。当线程一执行完毕,线程一把得到的bean装入一级缓存,缓存2,3倍线程一置为空,线程二被唤醒,这个时候缓存2,3执行得到的依旧为空,接着再次读取一级缓存,读取成功。得到的则为完整的bean。
27、 BeanDefinition的加载过程
a) BeanDefinition:用来描述(定义)Bean的生产信息,决定Bean如何进行生产,定义态的Bean。
b) New AnnotationConfigApplicationContext 生成一个容器
c) 解析配置类
i. 读取配置 BeanDefinitionReader
ii. 解析 Config @Bean @Import @Component - 配置类的解析器 ConfigurationClassParser
iii. 扫描 ClassPathBeanDefinitionSanner#doScan
根据包路径找到所有.class文件,判断类是不是标准@Component注解
排除接口,抽象类
注册BeanDefinition
iv. 注册成功放入BeanDefinitionMap中
d) 生产Bean
28、 BeanDefinition注册完后做拓展
a) 让类继承BeanFactoryPostProcessor 实现postProcessorBeanFactory回调方法。在所有的BeanDefinition注册完后调用
b) 在纯净态的时候进行回调,扩展
29、 在Spring所有Bean创建完后做扩展
a) Bean创建完:new ApplicationContext()->refresh()->finishBeanFactoryInitialization(循环所有的BeanDefinition,通过BeanFactory.getBean()生成所有的Bean)这个循环结束之后所有的bean也就创建完了。
b) 使用监听器
i. @EvenListener(ContextRefreshedEven.class)
c) 实现接口 SmartInitializingSingleton,实现方法afterSingletonsInstantiated
30、 PostProcessor 后置处理器
31、 Spring容器启动时,为什么先加载BeanFactoryPostProcess
a) 因为BeanDefinition会在ioc容器加载的先注册,而BeanFactoryPostProcess就是在所有的BeanDefinition注册完后做扩展的,所以要先加载BeanFactoryPostProcess。
b) 不少的解析配置类的组件需要继承BeanFactoryPostProcess
32、 Bean的创建顺序是什么样的
a) Bean的创建顺序是由BeanDefinition的注册顺序来决定的,当然依赖关系也会影响Bean的创建顺序
b) BeanDefinition的注解的解析顺序来决定
i. @Configuration
ii. @Component
iii. @import
iv. @Bean
33、 Spring的配置方式
a) 注解
b) 配置
c) 基于Java的配置 JavaConfig配置 Spring3.0+的版本
34、 JavaConfig是如何替代Spring.xml的
a) 以前Xml,读取配置文件,解析配置文件
i. Spring容器ClassPathXmlApplicationContext(“xml”)
ii. Spring.xml
iii.
iv. 扫描包
v. 引入外部属性配置文件
vi.
vii. 指定其他配置文件
b) JavaConfig,读取配置类,解析配置类(注解)
i. Spring容器:AnnotationConfigApplicationtext(javaconfig.class)
ii. 配置类@Configration
iii. @Bean
iv. 扫描包@ComponentScan
v. 引入外部配置文件@ProperSource
vi. @Value
vii. @Import 使用比较灵活
35、 @Import的用法,三种
a) 直接指定类
b) 通过ImportSelector接口可以一次性注册多个,返回一个String[]每一个值就是类的完整路径
c) 通过ImportBeanDefinitionRegistrar可以一次性注册多个,通过BeanDefinitionRegistry来动态注册BeanDefinition
36、 如何让自动注入没有找到依赖Bean时不报错
a) @Autowried(required=false)
b) 注入的规则,先类型,后名字,不需要额外提供get,set方法
37、 @Autowried和@Resource
a) @Autowried是Spring提供的,先类型后名字
b) @Resource是JDK提供的,先名字后类型
38、 使用@Autowired注解自动装配的过程
a) @Autowired通过Bean的后置处理器进行解析的
b) 在创建一个Spring上下文的时候再构造函数中的进行注册AutowiredAnnotationBeanPostProcessor
c) 在Bean的创建过程中进行解析
i. 在实例化后预解析(解析@Autowired标注的属性、方法 比如:把属性的类型、名称、属性所在的类…元数据存起)
ii. 在属性注入真正的解析(拿到上一步缓存的元数据 去ioc容器帮进行查找,并且返回注入)
- 首先根据预解析的元数据拿到 类型去容器中进行查找
- 如果查询结果刚好一个,就将该bean装配给@Autowired指定的数据
- 如果查询的结果不止一个,那么@Autowired会根据名称来查找
- 如果上述查找的结果为空,那么会抛出异常。解决方案时,使用required=false。
39、 @Configration的作用于解析
a) @Configration用来代替xml配置方式spring.xml配置文件
b) 没有@Configration也是可以配置Bean
c) @加了@Configration会为配置类创建cglib动态代理(保证配置类中@Bean方法调用Bean单例)
40、 @Bean之间的方法调用是怎么保证单例的
a) 如果希望@Bean的方法返回是对象是单例,需要在类上面加上@Configration
b) Spring会在invokeBeanFactoryPostProcessor通过内置BeanFactoryPostProcessor中会cglib生成代理
c) 当@Bean方法进行互调时,则会通过cglib进行增强,通过调用的方法名作为bean的名称去ioc容器中获取,进而保证了@Bean方法的单例
41、 要将一个第三方类配置成Bean有哪些方式(不是自己写的类)
a) @Bean
b) @Import
c) ImportBeanDefinitionRegistrar接口方法实现
d) BeanDefinitionRegistryPostProcessor 接口方法实现
42、 @ComponentScan不设置basePsckage也会扫描
a) 因为Spring在解析@ComponentScan的时候,拿到basePackage,如果没有设置会将你的类所在的包的地址作为扫描包的地址。
43、 什么是AOP
a) 和业务功能无关的,一般用户日志、权限、事务处理,使用的是Spring的动态代理
b) AOP,一般称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个可用于权限验证、日志、事务处理等。
c) AOP、OOP等字面上非常相似,但却是面向不同的领域的两种设计思想。OOP(面向对象编程)针对业务处理过程的实体和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。。而AOP作为面向对象的一种补充,则是针对业务处理过程中的切面进行提取,已达到业务代码和公共行为代码之间低耦合的隔离效果。这两种设计思想在目标上有着本质的差异。
44、 Spring AOP里面的几个名词
a) 切面(Aspect):在Spring Aop指定就是“切面类”,切面类会管理着切点、通知
b) 连接点:指定就是被增强的业务方法
c) 通知:就是需要增加到业务方法中的公共代码,通知有很多种类型分别可以在需要增加的业务方法不同位置进行执行(前置通知、后置通知、异常通知、返回通知、环绕通知)
d) 切点:由他决定哪些方法需要增强、哪些不需要增强,结合切点表达式进行实现
e) 织入:Aspectj独有的
45、 Spring有哪些通知类型
a) 前置、后置、返回、异常、环绕
b) 正常:前置->后置->返回
c) 异常:前置->后置->异常
46、 SpringAOP 和AspectJ AOP的区别
a) Spring Aop提供了AspectJ的支持,但只用到AspectJ的切点解析和匹配。 @Aspect、@Before这些注解都是AspectJ发明的
b) SpringAop使用的是动态代理。基于JDK动态代理实现
c) Aspect是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强。
47、 JDK动态代理和CGLIB动态代理的区别
a) JDK动态代理只支持接口的动态代理,不支持类的动态代理
i. JDK运行时生成一个动态代理类
ii. 该代理类是实现了目标类接口的一个类,并且会实现接口所有的方法增强代码
iii. 调用时先去调用处理类进行增强,再通过反射的方式进行调用方法。从而实现AOP
b) 如果代理类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类
i. 底层通过ASM在运行时动态的生成目标类的一个子类
ii. 并且会重写父类所有的方法增强代码
iii. 调用时先通过代理类进行增强,再直接调用父类对应的方法进行调用目标方法。从而实现AOP
48、 AOP的实现方式
a) 接口、schema-based配置文件、@Aspect注解(最方便)
49、 AOP内部调用失效原因,解决方式
a) 方法是private也会失效,解决:改成public
b) 没有配置Bean
c) 切点表达式没有配置正确
d) 必须走代理,重新拿到代理对象再次执行方法才能进行增强
i. 在本类中自动注入当前的bean
ii. 设置暴露当前代理对象到本地线程,可以通过AopContext.currentProxy()拿到当前正在调用的动态代理对象。
@EnableAspectJAutoProxy(exposeProxy=true)
50、 Spring的AOP是在哪里创建的动态代理
a) 在Bean的生命周期的“初始化”之后,通过BeanPostProcess.postProcessAfterInitialization创建aop的动态代理
b) 还有一种特殊的情况:Bean的生命周期中,“属性注入”存在循环依赖的情况下,也会为循环依赖的Bean通过MergedBeanDefinitionPostProcess.postProcessMergedBeanDefinition创建aop
51、 Spring的AOP的完整实现流程 javaConfig
a) 解析切面:在Bean创建之前的第一个Bean后置处理器会去解析切面
b) 创建动态代理
c) 调用:拿到动态代理对象,调用方法就会判断当前方法是否增强的方法,就会通过调用链的方式依次去执行通知。
52、 事务的四大特性
a) 原子性、一致性、隔离性、持久性
53、 Spring支持的事务管理类型,spring事务实现方式有哪些
a) Spring支持的两种类型的事务管理
i. 编程式事务管理:这意味着你通过编程的方式管理事务,给你带来极大的灵活性,但是极难维护
ii. 声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和MXL配置来管理事务
54、 实现声明式事务的四种方式
a) 基于接口
i. TransactionInterceptor
ii. TransactionProxyFactoryBean,上面接口的改进版本
b) 基于命名空间的声明式事务管理
c) 基于@Transactional的全注解方式:将声明式事务管理简化到极致
d) 通过aop增强,管理事务
55、 事务的传播行为
a) 事务的传播特性指的是当一个事务方法被另一个事务方法调用时,这个事务应该如何进行
b) REQUIRED默认:开启新事务(外部不存在事务)、融合到外部事务中(外部存在事务),适用于增删改查
c) SUPPORTS:不开始新事务、融合到外部事务中,适用查询
d) REQUIRES_NEW:开启新的事务、不用外部事务,创建新的事务,适用于事务不存在关联的情况,如日志
56、 Spring的事务隔离
a) 脏读
本事务读取了其他事务没有提交的数据 (锁行)
b) 不可重复读
一个事务中,,多次读取数据,但是读取的结果不一样 (锁行)
c) 幻影读
一个事务中,多次读取多行数据,但是读取的结构不一样 (锁表)
57、 Spring事务实现的基本原理
a) 解析切面:bean的创建前第一个bean的后置处理器进行解析
b) 创建动态代理:bean的初始化后调用bean的后置处理器进行创建动态代理。
c) 调用:动态代理
58、 Spring事务传播行为实现
a) Spring的事务信息是存在ThreadLocal中的,所以一个线程永远只能有一个事务。
b) 融入:当传播行为是融入外部事务则拿到ThreadLocal中的Connection,有的话就说明是一个内嵌事务,判断当前事务的传播行为。
c) 创建新事务:当传播行为是创建行为是创建新事务,会将嵌套新事务存入ThreadLocal、再将外部事务暂存起来;当嵌套事务提交、回滚后,会将暂存的事务信息恢复到ThreadLocal中。
59、 Spring多线程事务能否保证事务一致性(同时提交、同时回滚)
a) Spring的事务信息是存在ThreadLocal中的,所以一个线程只能有一个事务
b) 所以Spring的事务是无法实现事务一致性的
c) 可以通过编程式事务,或者通过分布式事务的思路:二阶段提交方式
60、 Spring事务的失效原因,基本等同于aop的失效原因,因为Spring的事务基本就是基于aop是实现的。或者是多线程,传播行为。
61、 Spring事件监听的核心机制是什么
a) 原理:观察者模式
b) 事件:负责对应相应监听器 事件源发生某事件是特定事件监听器被触发的原因
c) 监听器:对应于观察者模式中的观察者。监听器监听特定事件,并在内部定义了事件发生后的响应逻辑。
d) 事件发布器:对应观察者模式中的被观察者/主题,负责通知观察者对外提供发布事件和增删事件监听器的接口,维护事件和事件监听器之间的映射关系,并在事件发生时负责通知相关监听器。
62、 Spring源码用到了哪些设计模式
a) 简单模式——BeanFactory
b) 工厂方法——FactoryBean
c) 单例模式——Bean实例
d) 适配器模式——SpringMVC中的HandlerAdapter
e) 装饰器模式——BeanWrapper
f) 代理模式——AOP底层
g) 观察者模式——Spring的事件监听
h) 策略模式——excludeFilters、includeFilters
i) 模板方法模式——Spring几乎所有的外接扩展都采用这种模式
j) 责任链模式——Aop的方法调用
63、 Spring是如何整合Mybatis将Mapper接口注册为Bean的原理
a) 首先Mybatis的Mapper接口核心是JDK动态代理
b) Spring会排除接口,无法注册到IOC容器中
c) Mybatis实现了BeanDefinitionRegistryPostProcess可以动态注册BeanDefinition
d) 需要自定义扫描器(继承Spring内部扫描器ClassPathBeanDefinitionScanner(负责扫描.class))重写排除接口的方法
e) 但是接口虽然注册成了BeanDefinition但是无法实例化Bean因为接口无法实例化
f) 需要将BeanDefinition的BeanClass替换成JDK动态代理的实例(偷天换日)
g) Mybatis通过FactoryBean的工厂方法设计模式可以自由控制Bean的实例化过程,可以在getObject方法中创建JDK动态代理
64、 解决get、post乱码问题
a) Web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8
b) 把每个环节设置成统一编码
c) 数据库也可能乱码,设置数据库问题
65、 SpringMVC是单例模式,Bean单例存在的问题,它也会存在,比如多线程。
66、 SpringMVC的工作流程也就是DispatcherServlet的工作流程
a) 用户发送请求至前端控制器DispatcherServlet
b) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handler
c) 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有生成)一并返回给DispatherServlet
d) DispatcherAdapter经过适配调用具体处理器(Handler,也叫后端处理器)
e) Handler执行完成返回ModelAndView
f) HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet
g) DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析
h) ViewResolver解析后返回具体View
i) DispatcherServelet对View进行渲染视图(即将模型数据填充至视图中)
j) DispatcherServlet响应用户
k)
67、 SpringMvc怎么和AJAX相互调用的
a) 加入Jackson.jar
b) 在配置文件中配置json的消息转换器。(jackson不需要配置HttpMessageConverter)
c) 在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody。
d) 处理和转化json的时候就是在HandlerAdapter和Handler处理器(后端处理方法)之间解析json的,使用MappingJackson2HttpMessageConverter
68、 Spring和SpringMVC使用父子容器
a) SpringBoot就是使用一个容器
b) 就功能性来说不用父子容器也可以完成
c) 所以父容器的主要作用应该就是划分框架边界。有点单一职责的味道。Service、dao层我们一般使用Spring框架来管理
d) 规范整体架构。父容器无法访问子容器,子容器可以访问父容器
e) 方便子容器的切换。如果现在我们想把web层从springmvc替换成struts,那么只需将springmvc.xml替换成struts的配置文件struts.xml即可,而spring-core.xml不需要改变。
69、 是否可以把SpringMVC所有的Bean都交给Spring容器来管理
a) 目前是不行的,因为SpringMVC在加载的时候,HandlMapping处理器映射器是去SpringMVC容器中查找Bean的,除非把SpringMVC加载方式修改了,比如,当在子容器查找失败,选择去父容器中查找。
70、 是否可以把Spring的Bean交给SpringMVC管理
a) 部分是可以的但是没有意义
71、 实现零配置的SpringMVC,也就是@Config,同时也是javaConfig
a) 省略web.xml
i. servlet3.0之后提供了SPI扩展
ii. SpringMVC通过实现ServletContainerInitialization接口
iii. 动态注册ContextLocalListener和DispatcherServlet并创建父容器接口
b) 省略spring.xml和spring-mvc.xml
i. 实现了一个继承AbstractAnnotationConfigDispatcherServletInitializer的类
ii. 该类实现了ServletContainerInitializer,它会创建ContextLocalListener和DispatcherServlet
iii. 还会创建父子容器,创建容器时传入父子容器配置类则可以替代spring.xml和spring-mvc.xml
72、 SpringMVC的拦截器和过滤器有什么区别?执行顺序
a) 拦截器不依赖Servlet容器,过滤器依赖于servlet容器。
b) 拦截器只能对action请求起作用,而过滤器则可以对所有的请求起作用
c) 拦截器可以访问action上下文、值栈里面的对象,而过滤器不能访问(基于spring注册的过滤器也可以访问bean)
d) 执行顺序:Tomcat容器->Filter->Servlet->Interceptor->Controller
73、 SpringBoot的优点
a) SpringBoot的用来快速开发spring应用的一个脚手架、其设计目的是用来简化新Spring应用的初始搭建以及开发过程
b) SpringBoot提供了很多内置的starter结合自动配置,对主流的框架无配置集成,开箱即用
c) SpringBoot简化了开发,采用了JavaConfig的方式可以使用零xml的方式进行开发
d) SpringBoot内置Web容器无需依赖外部Web服务器,省略了Web.xml,直接运行jar文件文件就可以启动web应用
e) SpringBoot帮我管理了常用的第三方依赖的版本,减少了版本冲突的问题
f) SpringBoot自带了监控功能,可以监控应用程序的运行状态,或者内存、线程池、Http请求统计等,同时还提供了优雅关闭应用程序等功能。
74、 Spring和SpringBoot的关系和区别
a) 他们都是Spring生态的产品
b) Spring Framework是一个容器框架
c) SpringBoot它不是一个框架,它是一个可以快速基于Spring的脚手架(里面包含了Spring的各种框架),为开发Spring生态其他框架铺平道路
d) 不是一个层次的东西,没有可比性
75、 SpringBoot的核心注解
a) @SpringBootApplication注解:这个注解标识了一个SpringBoot工程,它实际上是另外三个注解的组合
b) @SpringBootConfiguration:这个注解实际就是一个@Configration,表示启动类也是一个配置类
c) @EnableAutoConfigration:向Spring容器中导入一个Selector,用来加载ClassPath下SpringFactories中所定义的自动配置类,将这些自动加载为配置Bean
d) @Condition也很关键,如果没有它我们无法在自定义中进行定制开发
i. @ConditionOnBean
ii. @ConditionOnClass
iii. @ConditionOnExpression
76、 Springboot的自动配置原理
a) 通过@SpringBootConfiguration引入了一个@EnableAutoConfiguration(负责启动自动配置功能)
b) @EnableAutoConfiguration引入了@Import
c) Spring容器启动时:加载IOC容器时会解析@Import注解
d) @Import导入了一个deferredImportSelector,它会使SpringBoot的自动配置类的顺序在最后,这样方便我们扩展和覆盖,deferred(延迟加载)
e) 然后读取所有AutoConfigrationClass类型的类
f) 最后通过@Condition排除无效的自动配置类
77、 为什么SpringBoot的jar可以直接运行
a) SpringBoot提供了一个插件spring-boot-maven-plugin用于把程序打包成一个可执行的jar包
b) SpringBoot应用打包之后,生成一个Fatjar(jar包中包含jar),包含了应用依赖的jar包和SpringBoot loader相关的类
c) Java -jar会去找jar中的manifest文件,在那里面找到真正的启动类(Main-class)
d) Fat jar的启动Main函数是JarLauncher,它负责创建一个LaunchedURLClassLoader来 加载boot-lib下面的jar,并以一个新线程启动应用的启动类的Main函数(找到manifest中的start-class)
78、 SpringBoot的启动原理
a) 运行main方法:初始化SpringApplication 从Spring.factories 读取listener ApplicationContextInitializer
b) 运行run方法
c) 读取环境变量 配置信息
d) 创建springApplication上下文ServletWebServletApplicationContext
e) 预初始上下文:读取启动类->BeanDefinition
f) 调用refresh加载ioc容器
i. InvokeBeanFactoryPostProcessor—解析@Import:加载所有的自动配置类
ii. onRefresh 创建(内置)Servlet容器
g) 在这个过程中springboot会调用很多监听器对外进行扩展
79、 SpringBoot内置Tomcat启动原理
a) 当依赖Spring-boot-start-web依赖时会在SpringBoot中添加:ServletWebServerFactoryAutoConfigration ,Servlet容器自动配置类
b) 该自动配置类通过@Import导入了可用(通过@ConditionalOnClass判断决定使用哪一个)的Web容器工厂(默认Tomcat)
c) 在内嵌Tomcat类中配置一个TomcatServletWebServerFactory的Bean(Web容器工厂)
d) 它会在SpringBoot启动时 加载ioc容器(refresh)OnRefersh创建内嵌的tomcat并启动
80、 SpringBoot外部Tomcat启动原理
a) 打包方式jar改成war
b) 排除内部tomcat启动依赖
c) 当tomcat启动时就会调用configure方法,从而springboot启动类的基础启动SpringBoot,写个类继承SpringBootServletInitializer,重写方法configure然后在方法中启动,Springboot的启动类
81、 自定义Starter(启动类),大概实现过程
82、 Springboot读取配置文件的原理是什么?加载顺序是怎么样的
a) 读取配置文件的原理是:事件监听机制
b) 加载顺序,优先级从高到底,高优先级的配置覆盖低优先级的配置,所有配置会形成互补配置,高到低分别为
i. File:./config
ii. File:./config/任意文件夹/
iii. File:./
iv. Classpath:config
v. Classpath:
83、 Springboot默认日志实现框架是什么,如何切换成别的
a) 默认:logback
b) 切换:利用Slf4j的桥接器和适配器,桥接器只能指向一个
c) 比如想用log4j2日志
i. 将logback的场景启动器排除(slf4j只能运行有1个桥接器)
ii. 添加log4j2的场景启动器,SpringBoot自带桥接器(自动配置)
iii. 添加log4j2的配置文件
84、 开发的时候在SpringBoot的基础上作扩展
a) 首先确认扩展到技术点
b) 比如说SpringMVC,让类继承某个接口,可以实现添加功能,另一个接口可以实现全面接管功能
85、 为什么要使用微服务架构,用过之后有哪些不好的地方
a) 从单体应用演变而来
b) 初期评估起手就上微服务(很少)
c) 分工协作
i. 单体:影响开发效率,发布和迭代性差;项目启动慢,每个人对整体的项目都要有所把握;业务缩减后如果语言不一致开发人员面临流失
ii. 拆分:提高开发效率和敏捷性;单个服务启动快,专人处理专事专注自己的服务;充分利用项目开发人员(哪怕是不同的语言不同框架、不同存储技术、也可以)
d) 并发能力
i. 单体:整体集群,易造成系统资源浪费;之前下单功能要去集群无法准确评测最大并发量,因为所有功能都在一起,无法准确预估扩容的服务器
ii. 拆分:服务集群,充分利用服务器资源;现在只需要针对下单服务进行压测就可以得到,下单功能具体能承受的并发量最高水位,从而更准确的进行扩容。
e) 业务(维护困难)
i. 单体:随着业务量增加,应用慢慢膨胀,后续可能会变得牵一发而动全身,难以维护
ii. 拆分:根据功能垂直拆分,责任更加分明,维护更加精确。
f) 容错
i. 单体:单点故障,一个功能OOM导致整个应用都不可用
ii. 拆分:弱依赖的服务出现故障,可以进行熔断(隔离)依然不影响主业务正常使用
g) 扩展
i. 单体:难以技术升级
ii. 拆分:新的服务采用任意新技术(技术多样性)
h) 缺点
i. 分布式:分布式系统较难编程,因为远程调用速度很慢,并且总是面临失败的风险
ii. 最终一致性:对于分布式系统而言,保持强一致性非常困难,这意味着每个人都必须管理最终一致性
iii. 运维复杂性:微服务必定带来开发、上线、运维的复杂度的提高,如果说单体应用复杂度为10,实施了微服务后的复杂度将是100,配备了相应的工具和平台后,可以将复杂度降到50,但仍然比单体复杂的多
iv. 隐式接口:服务和服务之间通过接口来“联系”,当某一个服务更改接口格式时,可能涉及到此接口的所有服务都需要做调整
v. 重复劳动:在很多服务中可能都会使用到同一个功能,而这一功能点没有足够大到提供一个服务的程度,这个时候可能不同的服务团队都会单独开发这一功能,重复的业务逻辑,这违背了良好的软件工程中的很多原则
86、 SOA、分布式、微服务之间有什么关系和区别
a) 分布式架构是指单体架构中的各个部分拆分,然后部署不同的机器或进程中去,SOA和微服务基本上都是分布式架构的
b) SOA是一种面向服务的架构,系统的所有服务都注册在总线上,当调用服务时,从总线上查找服务信息,然后调用
c) 微服务是一种更为彻底的面向服务的架构,将系统中各个功能个体抽成一个个小的应用程序,基本保持一个应用对应的一个服务的架构
d) SOA是微服务中不完整的一种形态,SOA和微服务都是分布式架构
87、 怎么拆分微服务、拆分时机是什么
a) 怎么拆
i. 高内聚低耦合,职责单一,服务粒度适中,服务不要太细(有的团队甚至一个接口一个服务)
ii. 以业务模型切入:比如产品,用户,订单为一个模型来切入
iii. 演进式拆分:刚开始不要划分太细,可以随着迭代过程来逐步优化
b) 拆分时机
i. 首先,如果是预估到业务在飞速增长,那就别犹豫,一定要提前考虑微服务的拆分
ii. 其次,如果在设计架构的时候,发现需要很多异构的技术栈,那也要考虑下微服务
iii. 最后,如果公司技术基础设施非常完备,对应的业务起初就设计的非常复杂,那么也别犹豫,起手就上微服务
88、 常用微服务组件及作用
a) 注册中心:管理服务
b) 负载均衡:客户端的负载均衡器
c) 服务调用:使远程服务调用更加优雅
d) 配置中心:管理服务的配置
e) 服务熔断:保证应用高可用,防止出现服务器雪崩,防止激增流量打垮冷系统
f) 分布式事务:Seata
g) 网关:为客户提供统一的服务,一些跟业务本身功能无关的公共逻辑都可以放在网关实现:鉴权、日志、限流、跨域、路由转发
h) 链路追踪:实时追踪服务的监控状况,协助快速恢复
89、 注册中心的核心功能
a) 服务注册:当服务启动 通过Rest请求的方式向Nacos Server注册自己的服务
b) 服务心跳:Nacose Client会维护一个定时心跳持续通知Nacose Server,默认5S一次,如果nacose Client超过了15秒没有接受心跳,会将服务健康状态设置为false(拉取的时候会忽略),如果nacose Client超过了30秒没有接收心跳 剔除服务
c) 服务发现:nacose client 会有一个定时任务,实时去Nacose server拉取健康服务
d) 服务停止:nacose client 会主动通过Rest请求nacose server发送一个注销的请求
90、 Nacos 配置中心
a) 集中管理服务的配置、提高维护性、时效性、安全性
b) 哪些东西可以作为配置?比如:数据库连接URL,缓存连接url字符串,数据库的用户名,密码。动态调整的参数。
c) 原理:nacos服务端创建了相关的配置项之后,客户端就可以进行监听了。客户端是通过一个定时任务来检查自己监听的配置项的数据的,一旦服务端的数据发生变化时,客户端将会获取到最新的数据,并将最新的数据保存在一个CacheData对象中,然后会重新计算CacheData的md5属性的值,此时就会对该CacheData所绑定的Listener触发receiveConfigInfo(接收配置信息)回调
d) 拉的优势:客户端拉取服务端的数据与服务端推送数据给客户端相比,优势在哪呢。推的方式需要维持与客户端的长连接,因为服务端不知道有哪些客户端,但是客户端请求只需要客户端申请连接,服务端通过请求即可,而且拉的方式,客户端只需要通过一个无状态的http请求即可获取到服务端的数据。减轻了服务器端的压力
91、 服务网关可以做什么
a) 流量网关:全局性流控、日志统计、防止SQL注入、防止Web攻击、屏蔽工具扫描、黑白IP名单、证书/加解密处理
b) 服务网关:服务级别流控,服务降级与熔断,路由与负载均衡、灰度策略,服务过滤、聚合与发现,权限验证与用户等级策略,业务规则与参数校验、多级缓存策略。针对具体的后端业务系统,或者是服务和业务有一定关联的部分,并且一般被直接部署在业务服务的前面,流量网关的后面。
92、 什么事服务雪崩、什么事服务限流
a) 服务器雪崩效应:因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程,就叫服务器雪崩
b) 解决方式:
i. 通过熔断机制:当一个服务挂了,被影响的服务能够及时熔断,使用Fallback数据保证流程在非关键服务不可用的情况下,仍然可以进行
ii. 通过线程池和消息队列机制实现异步化:允许服务快速失败,当一个服务因为过慢而阻塞,被影响服务可以在超时后快速失败,不会影响整个调用链路
iii. 服务限流:是指在高并发请求下,为了保护系统,可以对访问服务的请求进行数量上的限制,从而防止系统不被大量请求压垮,在秒杀中,限流是非常重要的
93、 什么是服务熔断?什么是服务降级?区别是什么
a) 服务熔断(终止交易):当服务A调用的某个服务B不可用时,上游服务A为了保证自己不受影响,及时切断与服务B的通讯。以防服务雪崩
b) 服务降级(执行B计划):提前预想好另外一种兜底措施,可以进行后期补救。直到服务B恢复,再恢复和B服务的正常通讯
94、 说说seata的实现原理
a) 在应用中Seata整体事务逻辑基于两阶段提交的模型,核心概念包含三个角色:
i. TC:事务协调者,即独立运行的seata-server,用于接收事务注册,提交和回滚
ii. TM:事务发起者。用来告诉TC全局事务的开始,提交,回滚(微服务端,客户端)
iii. RM:事务资源,每一个RM都会作为一个分支事务注册在TC(微服务端,客户端)
b) Auto Transaction模式
c) 第一阶段
i. 过程:TM向TC申请开启一个全局事务,全局事务创建并生成一个全局唯一的XID。
ii. XID在微服务调用链路的上下文中传播
iii. 假设运行:update product set name=“GTS” where name=“TXC”;//id=1
iv. 解析SQL:得到SQL的类型(UPdate),表(product),条件(where name=“TXC”)等相关信息。
v. 查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。Select * from product where name=“TXC”
vi. 执行业务SQL:更新这条记录的name为“GTS”
vii. 查询后镜像:根据前镜像的结果,通过主键定位数据 Select * from product where name=“TXC”
viii. 插入回滚日志:把前后镜像数据以及业务SQL相关的信息组成一条回滚日志记录,插入到UNDO—LOG表中
ix. 提交前,RM向TC注册分支:申请product表中,主键值等于1的记录的全局锁
x. 本地事务提交:业务数据的更新和前面步骤中生成的UNDO LOG一并提交
xi. TM向TC发起针对XID的全局提交或回滚决议。将本地事务提交的结果上报给TC
d) 第二阶段
i. TC调度XID下管辖的全部分支事务完成提交或回滚请求
ii. 收到TC的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给TC
iii. 异步阶段的分支:提交请求,将异步和批量地删除相应UNDO LOG记录
e) 二阶段-回滚
i. TC调度XID下管辖的全部分支事务完成提交或回滚请求
ii. 收到TC的分支回滚请求,开启一个本地事务,执行如下操作
iii. 通过XID和Branch ID查找到相应的UNDO LOG记录
iv. 数据校验:拿UNDO LOG中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改(出现脏写),转人工处理(Seata因为无法感知这个脏写如何发生,此时只能打印日志和触发异常通知,告知用户需要人工介入)
v. 人工没有脏写就简单了:根据UNDO LOG中的前镜像和业务SQL的相关信息生成并执行回滚的语句
vi. 提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给TC
95、 微服务快速定位排错
a) 界面显示、报错通知、问题定位
96、 Ribbon有哪些负载均衡策略
a) 实现IRule接口
b) RandomRule 随机选择一个服务实例
c) RoundRobinRule 轮询负载均衡策略
d) RetryRule 在轮询的基础上带有重试功能
e) WeightedResponseTimeRule 权重,如果一个服务的平均相应时间越短则权重越大,那么该服务实例被选中执行任务的概率也就越大
f) BestAvailableRule 过滤掉失效的服务实例功能,然后顺便找出并发请求最小的服务实例来使用
g) ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
h) AvailableFilterRule 先过滤掉故障实例,再选择并发较小的实例
97、 项目哪些场景用到了限流、降级?怎么配的?
a) 服务降级的预案:在进行降级之前要对系统进行梳理,提前将一些不重要或不紧急的服务或任务进行服务的延迟使用或暂停使用,看看哪些服务是必须誓死保护,哪些系统是能够弃车保帅;具体可以参考日志级别设置预案。
b) 一般:有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级
c) 警告:有些服务在一段时间内成功率有波动(95%~100%之间),可以自动降级或人工降级,并发送警告
d) 错误:可用率低于90%,或者连接池被占用了,或者访问量突然猛增到系统能承受的最大阈值,此时可以根据情况自动降级或者人工降级
e) 严重错误:因为特殊原因数据错误了,此时需要紧急人工降级
98、 SpringBoot和Spring相比约定大于配置
最后
以上就是明亮奇迹为你收集整理的跳槽神器!阿里巴巴内部出品“Spring全线笔记”,不止是全家桶,理论与实战结合,挑战年薪80w!的全部内容,希望文章能够帮你解决跳槽神器!阿里巴巴内部出品“Spring全线笔记”,不止是全家桶,理论与实战结合,挑战年薪80w!所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复