框架篇
- 一. Spring
- 二. MyBatis
一. Spring
1. Spring
1.1 Bean生命周期
首先会通过一个非常重要的类,叫做BeanDefinition获取bean的定义信息,这里面就封装了bean的所有信息,比如,类的全路径,是否是延迟加载,是否是单例等等这些信息,在创建bean的时候,
第一步是调用构造函数实例化bean,
第二步是bean的依赖注入,比如一些set方法注入,像平时开发用的@Autowire都是这一步完成
第三步是处理Aware接口,如果某一个bean实现了Aware接口就会重写方法执行,
第四步是bean的后置处理器BeanPostProcessor,这个是前置处理器,
第五步是初始化方法,比如实现了接口InitializingBean或者自定义了方法init-method标签@PostContruct,
第六步是执行了bean的后置处理器BeanPostProcessor,主要是对bean进行增强,有可能在这里产生代理对象,
最后一步是销毁bean。
1.2 Bean循环依赖(引用)说说spring中的循环引用
循环依赖:循环依赖其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于A。
循环依赖在spring中是允许存在,spring框架依据三级缓存已经解决了大部分的循环依赖
①一级缓存:单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象
②二级缓存:缓存早期的bean对象(生命周期还没走完,也就是半成品)
③三级缓存:缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的(普通对象或者代理对象)
具体流程:
第一,先实例A对象,同时会创建ObjectFactory对象存入三级缓存singletonFactories 。
第二,A在初始化的时候需要B对象,这个走B的创建的逻辑。
第三,B实例化完成,也会创建ObjectFactory对象存入三级缓存singletonFactories 。
第四,B需要注入A,通过三级缓存中获取ObjectFactory来生成一个A的对象同时存入二级缓存,这个是有两种情况,一个是可能是A的普通对象,另外一个是A的代理对象,都可以ObjectFactory来生产对应的对象,这也是三级缓存的关键。
第五,B通过从通过二级缓存earlySingletonObjects 获得到A的对象后可以正常注入,B创建成功,存入一级缓存singletonObjects 。
第六,回到A对象初始化,因为B对象已经创建完成,则可以直接注入B,A创建成功存入一次缓存singletonObjects。
第七,二级缓存中的临时对象A清除。
构造方法出现了循环依赖怎么解决?
回答:由于bean的生命周期中构造函数是第一个执行的,spring框架并不能解决构造函数的的依赖注入,可以使用 @Lazy懒加载 ,什么时候需要对象再进行bean对象的创建。
1.3 Bean线程安全问题
问题:Spring中的Bean是线程安全的吗?
Spring容器本身并不负责线程安全,只有在特定情况下才存在线程安全问题。根据Bean的作用域和状态来判断是否存在线程安全问题。
作用域:
Spring容器中有多种作用域,包括单例(single)、多例(prototype)、请求(request)、会话(session)、应用程序(application)和全球会话(globalsession)。
状态:(两种)
无状态指的是,在操作bean的成员变量的时候只是查询不涉及修改。
有状态则是,操作查询的同时又去修改它。
回答:一般情况下,在多例和请求作用域中,一般不存在线程安全问题,因为每次使用都会创建一个全新的实例。但如果是单例和作用域时,就要看状态了,比如bean中成员变量是无状态的则是线程安全的,如果是有状态的则是不安全的。
解决线程安全问题:可以通过更改作用域、避免在bean中定义需要修改的成员变量或使用ThreadLocal来解决线程安全问题。
1.4 AOP(什么是AOP?)
AOP:Aspect Oriented Programming面向切面编程
所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
应用场景(你们项目中有没有使用到AOP?)
- 日志记录:在系统中记录日志是非常重要的,可以使用AOP来实现日志记录的功能,可以在方法执行前、执行后或异常抛出时记录日志。
- 事务处理:在数据库操作中使用事务可以保证数据的一致性,可以使用AOP来实现事务处理的功能,可以在方法开始前开启事务,在方法执行完毕后提交或回滚事务。
- 缓存控制:在系统中有些数据可以缓存起来以提高访问速度,可以使用AOP来实现缓存控制的功能,可以在方法执行前查询缓存中是否有数据,如果有则返回,否则执行方法并将方法返回值存入缓存中,等等。
1.5 事务原理(Spring事务如何实现的?)
编程式事务
编程式事务是指手动编写程序来管理事务,即通过编写代码的方式直接控制事务的提交和回滚。在 Java 中,通常使用事务管理器(如 Spring 中的
PlatformTransactionManager
)来实现编程式事务。
Connection conn = ...;
try {
// 开启事务:关闭事务的自动提交
conn.setAutoCommit(false);
// 核心操作
// 业务代码
// 提交事务
conn.commit();
}catch(Exception e){
// 回滚事务
conn.rollBack();
}finally{
// 释放数据库连接
conn.close();
}
声明式事务
声明式事务是指使用注解或 XML 配置的方式来控制事务的提交和回滚。
开发者只需要添加配置即可, 具体事务的实现由第三方框架实现,避免我们直接进行事务操作!
使用声明式事务可以将事务的控制和业务逻辑分离开来,提高代码的可读性和可维护性。
回答:Spring实现的事务(声明式事务)本质就是aop完成,对方法前后进行拦截,在执行方法之前开启事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
1.6 事务失效
-
异常捕获处理,自己处理了异常但是没有抛出来。
解决方法:将异常手动抛出即可 -
抛出检查异常,@Transactional的属性rollbackFor默认是运行时异常。
解决方法:改成最大的Exception即可。 -
我之前还遇到过一个,如果方法上不是public修饰的,也会导致事务失效。本质还是底层有个权限判断,如果不是Public就返回NULL,即不支持事务。
解决方法:改成Public即可。 -
还一个是在解决项目中优惠卷订单业务时,将扣减库存和添加订单抽取成一个方法并且添加事务后在本类中其他方法调用时,事务失效。
解决方法:由于事务底层是利用AOP动态代理,但此时使用的是this对象的方法,导致事务失效,可以选择1.为处理扣减库存和创建订单新建一个service。2.获取AOP动态代理对象。3.本类自己注入调用自己(不会出现循环依赖)。强烈推荐其他博主的文章: 详细事务失效(12种)请点击本链接
2. SpringMVC
Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块的名称( spring-webmvc
),但它通常被称为“Spring MVC”。
2.1 执行流程
先介绍核心组件:
- DispatcherServlet : SpringMVC提供,我们需要使用web.xml配置使其生效,它是整个流程处理的核心,所有请求都经过它的处理和分发![ CEO ]。
- HandlerMapping : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它内部缓存handler(value:类名和方法名)和handler访问路径数据(key),被DispatcherServlet调用,用于查找路径对应的handler![秘书]
- HandlerAdapter : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它可以处理请求参数和处理响应数据,每次DispatcherServlet都是通过handlerAdapter间接调用handler,他是handler和DispatcherServlet之间的适配器![经理]
- Handler : handler又称处理器,他是Controller类内部的方法简称,是由我们自己定义,用来接收参数,向后调用业务,最终返回响应结果![打工人]
- ViewResovler : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效!视图解析器主要作用简化模版视图页面查找的,但是需要注意,前后端分离项目,后端只返回JSON数据,不返回页面,那就不需要视图解析器!所以,视图解析器,相对其他的组件不是必须的![财务]
执行流程:
1、用户发送出请求到前端控制器DispatcherServlet,这是一个调度中心。
2、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
3、HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
4、DispatcherServlet调用HandlerAdapter(处理器适配器)。
5、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
6、Controller执行完成返回ModelAndView对象。
7、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
8、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
9、ViewReslover解析后返回具体View(视图)。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应用户。
当然现在的开发,基本都是前后端分离的开发的,并没有视图这些,一般都
是handler中使用Response直接结果返回
3. Springboot
3.1 自动配置原理
在Spring Boot项目中的引导类上有一个注解@SpringBootApplication
,这个
注解是对三个注解进行了封装,分别是:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
其中 @EnableAutoConfiguration
是实现自动化配置的核心注解。
该注解通过 @Import
注解导入对应的配置选择器。关键的是内部就是读取了该项目和该项目引用的Jar包的的classpath路径下META-INF/spring.factories文件中的所配置的类的全类名。在这些配置类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中。
一般条件判断会有像 @ConditionalOnClass
这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。
4. Spring ,Spring MVC, Springboot框架常见的注释有哪些?
Spring
第一类是:声明bean,有@Component、@Service、@Repository、@Controller
第二类是:依赖注入相关的,有@Autowired、@Qualifier、@Resourse
第三类是:设置作用域 @Scope
第四类是:spring配置相关的,比如@Configuration,@ComponentScan 和@Bean
第五类是:跟aop相关做增强的注解 @Aspect,@Before,@After,@Around,@Pointcut
第六类是:事务相关@transaction等等SpringMVC
@RequestMapping:用于映射请求路径;还有像@PostMapping、@GetMapping这些。
@RequestBody:注解实现接收http请求的json数据,将json转换为java对象;
@RequestParam:指定请求参数的名称;
@PathViriable:从请求路径下中获取请求参数(/user/{id}),传递给方法的形式参数;@ResponseBody:注解实现将controller方法返回对象转化为json对象响应给客户端。@RequestHeader:获取指定的请求头数据。Springboot
Spring Boot的核心注解是@SpringBootApplication , 他由几个注解组成 :
@SpringBootConfiguration: 组合了- @Configuration注解,实现配置文件的功能;
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项;
@ComponentScan:Spring组件扫描。
二. MyBatis
1. 执行流程
执行流程:
- 读取MyBtias配置文件:mybatis-config.xml加载运行环境和映射文件。
- 构造会话工厂SqlSessionFactory。
- 会话工厂构建SqlSession对象(包含了执行SQL语句的所有方法)。
- 操作数据库接口,Executor执行器,同时负责查询缓存(一级,二级)的维护。
- Executor接口的执行方法中有个MappedStatement类型数据,封装了映射信息。
- 输入参数映射。Java->SQL
- 输出参数映射。SQL->Java
2. 延迟加载(Mybatis是否支持延迟加载?)
是支持的。
延迟加载的意思是:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。
Mybatis支持一对一关联对象和一对多关联集合对象的延迟加载。
在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false,默认是关闭的
-- mybatis-config.xml文件中
<settings>
<setting name="lazyLoadingEnabled" value="true"/> -- 开启延迟加载
</settings>
延迟加载底层原理
第一是,使用CGLIB创建目标对象的代理对象,这里的目标对象就是开启了延迟加载的mapper。
第二个是,当调用目标方法时,进入拦截器invoke方法,发现目标方法是null值,再执行sql查询。
第三个是,获取数据以后,调用set方法设置属性值,再继续查询目标方法,就有值了。
3. 一二级缓存(Mybatis的一级、二级缓存用过吗?)
一级缓存
基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当Session进行flush或close之后,该Session中的所有Cache就将清空,默认打开一级缓存。
二级缓存
二级缓存需要单独开启
二级缓存是基于namespace和mapper的作用域起作用的,不是依赖于SQLsession,默认也是采用 PerpetualCache,HashMap 存储。
如果想要开启二级缓存需要在全局配置文件和映射文件中开启配置才行。
-- mybatis-config.xml文件中
<settings>
<setting name="cacheEnabled" value="true"/> --开启二级缓存
</settings>
-- userMapper.xml文件中
<mapper>
<cache/> -- 开启二级缓存
<mapper/>
标签:事务,缓存,JAVA,对象,Spring,二级缓存,SSM,bean,MyBatis From: https://blog.csdn.net/kingandsong/article/details/140470465Mybatis的二级缓存什么时候会清理缓存中的数据?
当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了新增、修改、删除操作后,默认该作用域下所有 select 中的缓存将被 clear。