首页 > 其他分享 >速通spring与mybatis

速通spring与mybatis

时间:2024-09-06 14:47:55浏览次数:10  
标签:初始化 缓存 速通 对象 spring 代理 bean mybatis

 

Spring

1.什么叫线程安全:

  多个线程访问一个对象时,不需要额外的调度与交替执行也不需要额外的同步,调用这个对象的行为都可以获得正确的调度结果

如何保持线程安全:

  1. 使用final修饰变量,让其只可读不可修改
  2. 使用局部变量,公共数据私有化:这样堆内读取的数据就会改成在栈内读取
  3. 使用ThreadLocal,这样每个线程都会在进程内copy一份数据,各自使用自己的数据
  4. 使用互斥锁,让后续操作等待,这个适合线程比较多的情况
  5. 使用乐观锁CAS:失败重试机制:即将修改的数据的原数据不一致,重新获取数据再修改。在线程数较少时,使用锁比较消耗资源,使用此方法

 

2.spring框架中的单例bean是线程安全的吗?

spring中的bean默认是单例的,bean内有一个@scope("singleton")默认注解,说明他是单例的,另一种就是prototype原型设计模式。

单例bean是线程不安全的:如果springbean中有可变的状态,例如成员变量,他是公共的,这时就会有线程安全问题。

 

3.什么是AOP

面相切面编程,用于将与业务无关,但是会对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用模块,这个模块就是aspect(切面)

在事务@Transactional中,就是对方法前后进行了拦截,在执行方法前开启事务,在执行完目标方法之后根据执行情况提交或回滚事务

 

4.JDK动态代理和CGLIB动态代理

JDK代理只提供接口的代理,不支持类的代理

  • 运行时会为目标生成一个$proxy*.class的动态代理类
  • 代理类会实现接口的所有方法增强代码
  • 调用的时候通过代理类先去调用处理类进行增强,再通过反射去调用目标方法实现AOP

如果代理类没有实现接口,那么就是CGLIB来动态代理目标类

  • CGLIB底层是通过ASM在运行时动态的生成目标类的一个子类(会生成多个为了增强调用效率的类)
  • 重写父类所有增强代码
  • 调用是先通过代理类增强,再直接调用父类的方法,从而实现AOP(因此,如果被final修饰,无法通过CGLIB进行动态代理;CGLIB还会生成一个FastClass类路由类,可以让本类方法调用进行增强,而不会失效)

一般来说JDK生成类速度快(因为生成类比较少)而调用慢(采用反射所以慢);CGLIB生成类速度慢,后续调用快。但是JDK在版本升级性能都在提升。在JDK8的时候性能已经比CGLIB快20%左右了。

 

5.事务失效的场景

异常捕获处理,try catch你自己都把异常捕获了系统认为代码没问题就不会回滚了。当然也可以在catch中重新throw new runtimeexpection

抛出检查异常,默认只会回滚非检查异常,如果需要回滚,需要在事务注解内加上rollbackFor

非public方法(默认情况为default,而非public)

类内自调用,事务基于AOP,AOP基于动态代理,this指针的对象是普通对象而非动态代理对象(参考4,我是先写的5再写的4)

数据库引擎不支持、分布式情况

事务传播行为不一致:顺带讲一下常用的两种事务:1.required,适用于绝大多修改方法。A有事务时,B会融入A事务;A没有事务时,B会开启一个新事务。2.support,适用于绝大多查询方法,A有事务,B会加入A事务;A没事务,B会以非事务方法执行

 

6.springbean的生命周期

spring容器在实例化时会将xml中的<bean>封装成一个beanDefinition,其中有很多属性用来描述bean:

  • beanClassName(可以通过getBeanClassName()获取类名,反射创建bean)
  • scope(作用域:比如singleton,prototype,getScope)
  • lazy-init(懒加载)

生命周期:

  • beanDefinition调用bean的构造函数,(这里只负责创建bean,不负责赋值,赋值和创建是分开算的)
  • 依赖注入(通过@Autowired,@Value注入)
  • aware接口(BeanNameAware[运行bean获取自己的name]、BeanFactoryAware[用于获取bean,检查bean是否存在]、ApplicationContextAware[])方便对bean进一步扩展
  • BeanPostProcessor#before bean的后置处理器-前置
  • 初始化方法
  • BeanPostProcessor#after bean的后置处理器-后置(AOP就是通过这个来增强类的,AOP的底层为动态代理,动态代理分为JDK动态代理和CGlib动态代理)

 

7.如何解决循环依赖

为什么spring bean会有循环依赖:bean的生命周期中提到,bean的构造和赋值是分开的;在实例化时beanA会现在堆内申请空间,然后再进行初始化,初始化如果依赖于一个不存在的beanB,则需要优先去创建beanB,beanB也需要先实例化,然后初始化的时候发现他也需要beanA,从而形成循环

spring提供了三层缓存解决了大部分循环依赖问题:

一级缓存(singletonObjects)用于存储经历了完整生命周期的单例bean对象

二级缓存(earlySingletonObjects)用于存储早期的bean对象(生命周期还没有走完)

三级缓存(singletonFactories)用于存储ObjectFactory,对象工厂,用来创建对象的(后面可能会创建普通对象、代理对象)

二级缓存如何解决循环依赖:实例化A后,A的原始对象会被存入二级缓存中,初始化需要注入B,B不存在再实例化B。B先初始化,再初始化的时候将B原始对象存入二级缓存,这里通过二级缓存取出A的原始对象。B创建成功后再将B对象存入一级缓存中,B注入A,A也放入一级缓存中。这时A,B都将对象存入了一级缓存中,但是没有代理对象

三级缓存如何解决循环依赖:实例化A后,A会生成一个A-ObjectFactory,这时再去进行初始化,初始化需要注入B。实例化B,B再生成一个B-ObjectFactory,初始化需要注入A,B会让A-ObjectFactory创建一个A的代理对象。A的代理对象会被放入二级缓存,然后将A的代理对象注入给B,这时B创建成功了,B会被放入一级缓存中。将B注入给A代理对象,A代理对象也创建成功,这时A的代理对象也会放入一级缓存。至此创建成功

 构造方法出现了循环依赖:因为bean的生命周期中会有构造和初始化两个阶段,三级缓存解决的是初始化阶段循环依赖的问题;通过在构造方法内为循环依赖的bean加上@Lazy,让其懒加载。这样就可以将问题从构造方法中抛到初始化阶段从而解决问题

 

8.讲讲springMVC的执行流程

老版本(JSP这类)

1.浏览器发送请求给DispatcherServlet(由tomcat初始化的前端控制器)

2.通过handlermapping查询handler,handlermapping里存了一张map { key:前端发起的路径 , value:"类名#方法名" } 例如:{key:/user/getById/1,value:"UserController#getUserById(Long id)"}。再将handler、拦截器一并返回给dispatcherServlet

3.DispatcherServlet调用handlerAdaptor,handlerAdaptor调用适用的handler/controller,将得到的ModelAndView对象返回给dispatcherServlet。

4.dispatcherServlet将modelAndView传给ViewResolver,ViewResolver解析后返回具体的view

5.dispatcherServlet根据view渲染具体视图响应用户

新版本

1.浏览器发送请求给DispatcherServlet(由tomcat初始化的前端控制器)

2.通过handlermapping查询handlermapping,生成处理器对象及处理器拦截器,再一起返回给dispatcherServlet

3.dispatcherServlet调用handleradapter

4.方法上有responseBody,通过HttpMessageConverter来返回结果转换为JSON并响应

 

9.springboot的自动配置原理

springboot项目的引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装:@SpringbootConfiguration,@EnableAutoConfiguration,@CompentScan

其中@EnableAutoConfiguration是实现自动装配类的注解,通过@Import导入了默认的选择器。选择器就是读取了项目以及项目引用的jar包的classpath路径下META-INF/spring.factories所配置的类的类名,这些配置类内的bean会根据条件注解所指定的条件来决定是否需要将其导入到spring容器中

条件判断会类似@ConditionalOnClass的注解来决定是否加载该类,如果有则加载,其中的所有bean也会有@ConditionalOnMissingBean控制,如果一切正确则bean会自动置入spring容器中。

 

10.spring框架常见注解(spring,springboot,springMVC)

spring:bean相关,例如@Component @Controller @Service @Autowired @Scope @Configuration AOP相关 @PointCut @Around @Aspect

springboot:@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan @SpringbootApplication

springMVC:主要是controller里那些 @RequestMapping @RequestBody @RequsetHeader @RequestParam @PathViriable @RestController @ResponseBody

 

Mybatis

1.mybatis的执行流程

读取mybatis配置:spring版本有个mybatis-config.xml;springboot版本实现自动装配功能,直接在yaml文件写jdbc的链接相关内容就可以了

构造会话工厂SqlSessoinFactory

会话工厂构造会话对象SqlSession(用于执行映射SQL的语句、管理事务、代理mapper),sqlsession中的功能是基于executor实现的

executor是mybatis的核心接口,他的执行方法中有一个MapperedStatement类型的参数,其中封装了映射信息,会对输入参数和输出参数进行java到sql以及sql到java的映射。包括了id,resource,resultType,sql。

executor来执行实际的数据库操作。

 

2.mybatis是否支持延迟加载?

支持,但是默认情况下是未开启的

什么是延迟加载?举例来说:有User表和Order表,他们是1对多的关系;User实体类中有List<Order> orderList字段,Order实体类中有User user字段

查询用户时如果把用户所属的订单数据页查询出来,这就是正常加载

查询用户时暂时不查询订单数据,需要订单时再查询订单,这就是延迟加载

如何实现延迟加载:在映射的结果集里的子查询中有fetchType属性,设置为lazy即可实现延迟加载。上例中,只有当你调用了user.getOrderList()才会去执行对应的查询语句

延迟加载主要是通过CGLIB代理对象实现的,调用目标方法时会进入invoke拦截器,发现目标为null会执行sql查询再通过set设参然后才会进入目标方法

 

3.mybatis的一级缓存/二级缓存

mybatis的缓存都是由map集合存储的;sqlSession是与当前线程绑定的,一旦事务结束、请求处理完毕、发生异常,sqlSession都会关闭

一级缓存(默认开启的):作用域是同一个sqlSession,同一个sqlSession内执行两次参数一样的sql语句会在缓存中读取数据,持续到sqlSession没有close或者flush。一般在完成一次数据库操作/事务结束后sqlSession就会关闭了,因此其实一级缓存意义不大。

二级缓存(不建议使用):多个sqlSession共享的,其作用域是mapper的同一个namespace。不同sqlSession执行两次参数一样同一个namespace的sql会在缓存中读取数据。

二级缓存只建议用在数据字典这种基本不会改动,不会有/被外表链接的mapper中。

 

标签:初始化,缓存,速通,对象,spring,代理,bean,mybatis
From: https://www.cnblogs.com/kun1790051360/p/18378873

相关文章