框架

115 阅读8分钟

框架

Spring框架中的单例Bean是线程安全的吗?

不一定是线程安全的,spring的bean默认是单例模式, spring并没有对单例bean进行多线程的封装处理,

线程安全主要是看bean是否有无状态, 如果bean中定义了可修改的成员变量,变成了有状态的bean,就要考虑线程安全的问题了, 一般的解决方案就是将singleton改为prototype,prototype每次都是新建一个对象,所以不要在bean中声明任何有状态的实例变量或类变量, 如果非要用,就使用ThreadLocal把变量变成线程私有

什么是AOP?

面向切面编程,用于将那些业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取出公共模块复用,降低耦合

你们项目中有没有使用AOP

记录操作日志: 使用AOP中的环绕通知+切点表达式(找到要记录日志的方法),通过环绕通知的参数获取请求方法的参数(类,方法,注解,请求方式等),获取到这些参数后,保存到数据库

Spring中的事务是如何实现的呢?

本质是通过AOP功能,对方法前后进行拦截,在执行方法之前开启事务,在执行完目标方法之后根据情况提交或回滚事务

Spring中事务失效的场景

情况一: 异常捕获处理

原因:事务通知只有捉到了目标抛出的异常,才能进行回滚处理,如果目标自己处理掉了异常,事务无法知悉,就不会回滚

解决: 在catch块添加throw new RuntimeException(e)抛出

情况二: 抛出检查异常

原因: Spring 默认只会回滚非检查异常(RuntimeException)

解决: 配置 rollbackFor属性 @Transaction(rollbackFor=Exception.class)

情况三: 非public方法导致的事务失效

原因: Spring为方法创建代理,添加事务通知, 前提条件都是该方法是public的, 如果不是public的无法代理

解决: 必须要加public

Spring的Bean的生命周期

image.png

  • 通过BeanDefinition获取bean的定义信息

  • 调用构造函数实例化bean

  • bean的依赖注入(set注入)

  • 处理Aware接口(BeanNameAware,BeanFactoryAware,ApplicationContextAware)

  • 执行BeanPostProcessor前置处理器

  • 初始化方法(initalizingBean, init-method)

  • 执行BeanPostProcessor后置处理器

  • 销毁Bean

Spring中的循环依赖

什么是循环依赖

自己依赖自己,或者和别的bean相互依赖,就是 A实例化的时候,发现依赖于B,创建B实例, 创建B实例的时候发现需要A, 创建A实例,无限循环,就形成了循环依赖的问题了

什么情况下循环依赖可以被处理?

前提: 出现循环依赖的Bean必须要是单例; 依赖注入方式不能全是构造器注入的方式

  1. AB都采用构造器注入,不支持;

  2. AB都采用Setter方法注入,支持;

  3. A中注入B的方式采用Setter方法,B中注入A的方式为构造器,支持

  4. B中注入A的方式采用Setter方法,A中注入B的方式为构造器,不支持

第4种不支持的原因: spring在创建bean时默认会根据自然排序进行创建,所以A会先于B进行创建

Spring是如何解决的循环依赖

Spring通过三级缓存解决了循环依赖

  • 一级缓存(singletonObjects):单例池缓存已经经历了完整的生命周期,已经初始化完成的Bean对象

  • 二级缓存(earlySingletonObject): 缓存早期的Bean对象(生命周期还没走完)

  • 三级缓存(singletonFactories): 缓存的是ObjectFactory,表示对象工厂,用来创建某个对象

image.png

当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂, 然后将这个工厂对象放入到三级缓存中, 然后A需要注入B,如果B对象不存在,就会去实例化B, 实例化B后,它也会生成一个对象工厂,也放到三级缓存中, 然后B需要注入A,这时A也没有,它就会从三级缓存中获取A对象的工厂对象,通过A的工厂对象创建代理对象, 然后把产生后的对象放到二级缓存中(半成品), 将生成后的代理对象A注入给B, B就创建成功,放入单例池中, 然后将B注入给A, A也就创建成功了,最后A也要存入到单例池中,只不过它是一个代理对象

构造方法出现了循环依赖怎末解决?

A依赖于B,B依赖于A,注入的方式是构造函数

原因: 由于bean的生命周期中构造函数是第一个执行的,spring框架并不能解决构造函数的依赖注入

解决: 使用@Lazy进行懒加载,什么时候需要对象再进行bean对象的创建

SpringMVC的执行流程

  • 用户发送请求到前端控制器DispatcherServlet

  • DispatcherServlet收到请求收调用HandleMapping(处理器映射器)

  • 处理器映射器找到具体的处理器,生成处理器对象及拦截器(如果有),一起返回给前端控制器

  • 前端控制器调用HandlerAdapter(处理器适配器)

  • 处理器适配器经过适配调用具体的处理器(Handler/Controller)

  • 处理完之后返回ModelAndView对象给前端控制器

  • 前端控制器将ModelAndView传给ViewReslover(视图解析器)

  • 视图解析器解析后返回具体的视图(View)

  • 前端控制器根据视图进行渲染数据

  • 前端控制器响应用户

但是我们现在大部分的开发都是前后端分离,Restful风格接口,后端只需要返回json就行了,使用@ResponseBody在方法上添加, 通过HttpMessageConverter来返回结果转换为JSON并响应

SpringBoot自动配置原理

在Spring Boot项目中的启动类上有一个注解@SpringBootApplication,这个注解对三个注解进行了封装,分别是:

@SpringBootConfiguration 标志是一个配置类

@EnableAutoConfiguration 开启自动配置

ComponentScan 包扫描

主要是@EnableAutoConfiguration这个注解.该注解是通过@import注解导入对应的配置选择器(AutoConfigurationImportSelector类),内部就是读取了该项目和项目引用的jar包的classpash路径下MEAT-INF/spring.foctories文件中的所配置的类的全类名. 在这些类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中, 条件判断会有像@ConditionalOnClass这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用

Spring常见的注解

一般都是Bean实例化, 依赖注入的注解

image.png

SpringMVC常见的注解

一般都是请求响应的注解

image.png

SpringBoot常见的注解

image.png

Mybatis执行流程

image.png

  • 读取Mybatis的核心配置文件: mybatis-config.xml加载运行环境和映射文件

  • 构建会话工厂SqlSessionFactory

  • 会话工厂创建SqlSession对象(包含了执行SQL语句的所有方法)

  • 通过Executor执行器将SqlSession传递的参数 动态的生成需要执行的SQL语句(真正执行数据库操作接口),同时负责查询缓存的维护(一级缓存,二级缓存)

  • Executor接口的执行方法中有一个MappedStartement类型的对象,封装了映射信息

  • 对输入参数的类型进行处理,并预编译

  • 对返回结果的类型进行处理,根据对象映射规则,返回相应的对象

Mybatis是否支持延时加载? 原理?

延迟加载: 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据,可以减少系统响应时间和数据库压力

Mybatis支持一对一关联对象(association)和一对多关联集合(collection)对象的延迟加载, 但默认是没有开启的,如果开启则在Mybatis配置文件中将lazyLoadingEnabled设置为 ture(全局), 或者在映射文件中使用fetchType=lazy配置(局部加载)

原理: 使用CGLIB创建目标对象的代理对象,该代理对象会拦截所有访问操作,并判断当前访问的属性是否已被加载, 如果该属性已被加载,则直接返回该属性的值,不再进行加载操作;如果该属性未被加载,则通过sql语句查询出该属性并进行加载;

Mybatis的一级和二级缓存

一级缓存:基于PerpetualCache的HashMap本地缓存,存储作用域为SqlSession,各个SqlSession之间的缓存相互隔离,当Session flush或close之后,该SqlSession中的所有缓存就会清空,Mybatis默认开启一级缓存

二级缓存:基于namespace和mapper的作用域起作用的,不是依赖于SQL Session,默认也是采用PerpetualCache,HashMap存储,需要单独开启,一个是核心配置,一个是mapper映射文件 二级缓存默认是关闭的 开启方式,两步: 1,全局配置文件

image.png

2,映射文件 使用标签让当前mapper生效二级缓存