当前位置: 首页 > news >正文

Review --- 框架

Spring框架中的bean是单例的吗?

  • Spring默认bean是单例的
  • 可以通过注解Scope显示定义bean为单例或多例
  • singleton:bean在每个Spring IOC容器中只有一个实例
  • prototype:一个bean的定义可以有多个实例

在这里插入图片描述

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

在这里插入图片描述
成员变量需要考虑线程安全,局部变量一般不需要考虑线程安全。实际开发中应尽量避免使用可变成员变量

如图,每个请求过来都会使这个count+1,不是线程安全的

Spring bean没有可变状态(无状态就是指当前变量能不能被修改),所以在某种程度上说Spring的单例bean是线程安全的.

But 一般在spring的bean中都是注入无状态的对象,没有线程安全问题,如果在bean中定义了可修改的成员变量,是要考虑安全问题的,可以使用多例或者加锁来解决

综上,

回答参考:

嗯,spring中的单例bean不是线程安全的
当多个用户请求一个服务时,容器会给每一个请求分配一个线程,这时多个线程会并发执行请求对应的业务逻辑(成员方法),如果该处理逻辑中有对该单例状态的修改(体现为该单例的成员属性),则必须考虑线程同步问题。

Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。

我们通常在项目中使用的Spring bean都是不可变状态(比如service类),所以在某种程度上说Spring的单例bean时线程安全的。

什么是AOP?你们项目中有没有用到AOP?

AOP称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。

Spring事务的底层就是AOP,AOP的底层是动态代理,动态代理的底层是反射44

常见的AOP使用场景:

  • 记录操作日志
  • 缓存处理
  • Spring中内置的事务处理

记录操作日志思路

在这里插入图片描述
在这里插入图片描述

自定义日志注解结合切点表达式,前置通知,后置通知,环绕通知,异常通知等来进行请求信息的捕获和记录(保存)

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

Spring支持编程式事务管理和声明式事务管理两种方式。

  • 变成式事务控制:需使用Transaction Template来进行实现,对业务代码有侵入性,项目中很少使用
  • 声明式事务管理:声明式事务管理建立在AOP之上。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编制到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

回答参考:

在这里插入图片描述

Spring中事务失效的场景有哪些?

如果业务层关系比较复杂,事务失效必须要注意!!!

在Spring中,事务管理是通过AOP(面向切面编程)实现的。尽管Spring的声明式事务管理非常强大且易于使用,但在某些特定情况下,事务可能会失效。

1. 异常捕获处理不当

  • 问题:如果在方法内部捕获了异常,并且没有正确地重新抛出异常(特别是运行时异常),那么Spring事务管理器将不会回滚事务。
  • 解决方案:确保在需要回滚事务的地方正确地抛出异常。如果你必须捕获异常,请考虑手动标记事务为回滚状态,例如通过调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

2. 抛出检查异常

  • 默认行为:Spring默认只会在遇到未捕获的运行时异常(RuntimeException)或错误(Error)时自动回滚事务。对于检查异常(Checked Exception),默认是不会触发回滚的。
  • 解决方案:可以通过指定rollbackFor属性来改变这种行为,让特定的检查异常也能触发回滚。
    @Transactional(rollbackFor = Exception.class)
    public void someMethod() throws Exception {// 方法体
    }
    

3. 非public方法

  • 问题:只有被@Transactional注解标注的public方法才能启动事务。如果在非public方法上添加此注解,则该注解将不起作用。
  • 解决方案:确保事务性的业务逻辑位于public方法中,并且这些方法上有@Transactional注解。

其他需要注意的情况

4. 自调用问题
  • 问题:在一个类内部直接调用另一个带有@Transactional注解的方法不会触发事务代理,因为这是直接的方法调用而不是通过Spring AOP代理进行的。
  • 解决方案:可以使用AOP代理的方式来调用目标方法,或者重构代码以避免自调用问题。例如,通过注入自身实例的方式来进行调用:
    @Autowired
    private ApplicationContext applicationContext;public void callingMethod() {MyService self = applicationContext.getBean(MyService.class);self.transactionalMethod();
    }
    
5. 嵌套事务
  • 问题:Spring不支持真正的嵌套事务。虽然可以配置传播行为为Propagation.NESTED,但这实际上是保存点机制,而不是真正意义上的嵌套事务。
  • 解决方案:理解并正确配置事务的传播行为,根据实际需求选择合适的传播方式。
6. 数据源配置错误
  • 问题:如果数据源配置有误,比如事务管理器未正确绑定到正确的数据源,也可能导致事务无法正常工作。
  • 解决方案:确保数据源和事务管理器的配置正确无误。
7. 使用不当的事务隔离级别
  • 问题:选择不适当的事务隔离级别可能导致并发问题,如脏读、不可重复读等,影响系统性能和数据一致性。
  • 解决方案:根据应用的实际需求选择合适的事务隔离级别。

总结

在复杂的业务层关系中,确保事务的有效性尤为重要。除了注意上述提到的常见失效场景外,还应该仔细设计事务边界,合理设置事务传播行为和隔离级别,以及正确处理异常情况,这样才能保证事务按照预期工作,维护数据的一致性和完整性。

Spring的bean的生命周期?

  1. 通过BeanDefinition获取bean的定义信息
  2. 调用构造函数实例化bean
  3. bean的依赖注入
  4. 处理Aware接口(BeanNameAware、BeanFactoryAware、ApplicationContextAware)
  5. Bean的后置处理器BeanPostProcessor-前置(它允许你在Spring容器完成bean实例化之后但在Spring容器将其交给应用程序使用之前对bean进行修改或增强)
  6. 初始化方法(InitializingBean、init-method(自定义初始化方法))
  7. Bean的后置处理器BeanPostProcessor-后置
  8. 销毁bean

Bean的创建和初始化赋值是分开的

了解Spring容器是如何管理和创建bean实例的

方便进行调试和解决问题

BeanDefinition(bean的定义信息)

Spring容器在进行实例化时,会将xml配置的的信息封装成一个BeanDefinition对象,Spring根据BeanDefinition来创建Bean对象,里面有很多的属性用来描述Bean
在这里插入图片描述

在这里插入图片描述

回答参考

Spring中的循环引用问题

在这里插入图片描述

A B循环依赖分析流程

在这里插入图片描述
如何解决循环依赖问题?Spring其实已经解决了大部分的循环依赖问题。

三级缓存解决循环依赖

Spring解决循环依赖是通过三级缓存,对应的三级缓存如下所示:
在这里插入图片描述

在这里插入图片描述

一级缓存作用:限制bean在beanFactory中只存一份,即实现singleton scope,解决不了循环依赖(只有走完bean生命周期的单例bean才能被缓存)

如果要想打破循环依赖,就需要一个中间人的参与,这个中间人就是二级缓存。

在这里插入图片描述
三级缓存存储的是对象工厂,可以生产普通对象和代理对象,主要解决代理对象问题。

构造方法循环引用如何解决?

给其中一个bean加@Lazy注解进行懒加载,需要使用时再进行实例化
在这里插入图片描述

回答参考:

  • 循环依赖:循环依赖也叫循环引用,就是由两个或两个以上的bean互相持有对方,最终形成闭环,比如A依赖于B,B依赖于A
  • 循环依赖在spring中式允许存在的,spring通过三级缓存已经解决了大部分的循环依赖

一级缓存:单例池,用来存储走完bean生命周期的单例bean
二级缓存:存储半成品的bean对象
三级缓存:缓存的是ObjectFactory,表示对象工厂,用来创建普通/代理对象的

SpringMVC的执行流程是什么?

SpringMVC的执行流程是这个框架最核心的内容

  • 视图阶段(老旧JSP等)
  • 前后端分离阶段(接口开发,异步)

在这里插入图片描述

视图阶段版本:jsp

  1. 用户发送出请求到前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
  3. HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器(如果有),再一起返回给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进行渲染视图(即将模型数据填充至视图中)
    DispatcherServlet响应用户

前后端分离版本

  1. 用户发送出请求到前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
  3. HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet
  4. DispatcherServlet调用HandlerAdapter(处理器适配器)
  5. HandlerAdapter经过适配调用具体的处理器(Handler/Controller)
  6. 方法上添加了@RequestBody
  7. 通过HttpMessageConverter来返回结果转换为JSON并响应

SpringBoot自动配置原理

SpringBoot自动装配原理是最高频的一道面试题,也是框架最核心的思想

SpringBoot的自动配置主要依赖@SpringBootApplication注解,这个注解里面包含了另外三个注解

  • @SpringBootConfigruation:该注解与 @Configuration注解作用相同,用来声明当前类是一个配置类
  • @ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包
  • @EnableAutoConfiguration:SpringBoot实现自动化配置的核心注解

在这里插入图片描述

回答参考

在SpringBoot项目中的引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装,分别是:

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @ComponentScan

其中***@EnableAutoConfiguration是实现自动化配置的核心注解,该注解通过@Import注解导入对应的配置选择器。内部就是读取了该项目引用的Jar包的classpath路径下 META-INF/spring.factories 文件中的所配置的类的全类名。在这些配置类中所定义的Bean会根据条件注解所指定的条件来决定***是否需要将其导入到Spring容器中。

条件判断会有像***@ConditionalOnClass***这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有Bean放入spring容器中使用。

Spring框架常见注解(Spring、SpringBoot、SpringMVC)

  • Spring常见注解有哪些?
  • SpringMVC常见的注解有哪些?
  • SpringBoot常见注解有哪些?

Spring 核心注解

注解说明
@Component@Controller@Service@Repository类级别注解,用于实例化Bean(分别表示通用组件、控制层、服务层、持久层)
@Autowired根据类型自动依赖注入
@Qualifier结合@Autowired,按名称指定注入的Bean
@Scope定义Bean作用域(如singletonprototype
@Configuration标记类为配置类,替代XML配置
@ComponentScan指定Spring扫描的包路径
@Bean方法级别注解,将返回值注册为Bean
@Import导入其他配置类或普通类到IOC容器
@Aspect@Before@After@Around@PointcutAOP切面编程相关注解
@Lazy延迟初始化Bean
@Primary优先注入同类型Bean中的指定实例
@Profile环境隔离(如dev/prod
@PostConstruct@PreDestroy生命周期方法(JSR-250)
@Value注入属性值(如${property}
@PropertySource加载外部配置文件

Spring MVC 注解

注解说明
@RequestMapping映射请求路径(类/方法级别),可指定HTTP方法
@RequestBody接收HTTP请求的JSON数据并转换为Java对象
@RequestParam获取请求参数(可指定默认值和是否必填)
@PathVariable从URL路径中提取变量(如/user/{id}
@ResponseBody将方法返回值直接作为HTTP响应体(如JSON)
@RequestHeader获取请求头信息
@RestController@Controller + @ResponseBody组合注解
@GetMapping限定GET请求的简写形式
@PostMapping限定POST请求的简写形式
@PutMapping@DeleteMapping限定PUT/DELETE请求的简写形式
@ModelAttribute绑定请求参数到Model对象或向Model添加属性
@SessionAttributes将Model中的属性存储到Session
@CookieValue获取Cookie值
@CrossOrigin配置跨域请求支持

Spring Boot 注解

注解说明
@SpringBootConfiguration标记类为Spring Boot配置类(实际开发中更常用@SpringBootApplication
@EnableAutoConfiguration启用自动配置机制
@ComponentScan自动扫描组件包路径
@SpringBootApplication核心注解,包含@Configuration+@EnableAutoConfiguration+@ComponentScan
@Conditional系列条件化配置(如@ConditionalOnClass@ConditionalOnMissingBean
@ConfigurationProperties批量绑定配置文件属性到Bean
@EnableScheduling启用定时任务
@EnableAsync启用异步方法调用
@EnableCaching启用缓存支持
@EnableWebMvc/@EnableWebFlux显式启用MVC或响应式Web配置

Mybatis执行流程

  1. 读取Mybatis配置文件:mybatis-config。xml加载运行环境和映射文件
  2. 构建绘画工厂SqlSessionFactory
  3. 会话工厂创建SqlSession对象(包含了执行SQL语句的所有方法)
  4. 操作数据库的接口,Excutor执行器,同时负责查询缓存的维护
  5. Executor接口的执行方法中有一个MappedStatement类型的参数,封装了映射信息
  6. 输入参数映射
  7. 输出结果映射

Mybatis是否支持延迟加载

Mybatis支持延迟加载,但默认没有开启

什么是延迟加载呢?

就是按需加载,需要时再加载。不需要这个信息就不查询,提高性能
在这里插入图片描述
比如下面这个xml文件内容,fetchType=“lazy”,就对查询订单开启了延迟加载
在这里插入图片描述
也可以全局配置mybatis进行延迟加载
在这里插入图片描述
在mybatis的配置文件中,设置lazyLoadingEnabled的值为true

延迟加载的原理

  1. 使用CGLIB创建目标对象的代理对象
  2. 当调用目标方法user.getOrderList()时,进入拦截器invoke方法,发现user。。getOrderList()是null值,执行sql查询order列表
  3. 把order查询上来,然后调用user.setOrderList(List orderList),接着完成user.getOrderList()的调用

在这里插入图片描述

参考回答

在这里插入图片描述

Mybatis的一级、二级缓存

一级缓存

一级缓存是基于PerpetualCached的HashMap 本地缓存,其存储作用域为Session,当Session进行flush或close之后,该Session中的所有Cache就清空,Mybatis默认打开一级缓存
在这里插入图片描述

二级缓存

二级缓存是基于namespace和mapper的作用域起作用的,不是依赖于SqlSession的,默认也是采用PerpetualCache,HashMap 存储

在这里插入图片描述
注意事项:

  1. 对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了新增、修改、删除操作后,默认该作用域下所有 select 的缓存将被clear
  2. 二级缓存需要缓存的数据必须要实现Serializable接口
  3. 只有回话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中

参考回答

在这里插入图片描述

相关文章:

  • 实验-实现向量点积-RISC-V(计算机组成原理)
  • C语言编程中的时间处理
  • Cross-Site Scripting(XSS)
  • Go语言之路————并发
  • 一键清理功能,深度扫描本地存储数据
  • 深度学习驱动下的目标检测技术:原理、算法与应用创新(三)
  • memcached主主复制+keepalive
  • Python多线程实战:提升并发效率的秘诀
  • Linux常用命令42——tar压缩和解压缩文件
  • Python 之类型注解
  • Java项目使用Tomcat启动后JS文件中的中文乱码问题
  • 彻底删除Docker容器中的环境变量
  • 【Win32 API】 lstrcmpA()
  • 第J1周:ResNet-50算法实战与解析
  • entity线段材质设置
  • let、var、const的区别
  • 基于javaweb的SSM驾校管理系统设计与实现(源码+文档+部署讲解)
  • 软考第六章知识点总结
  • 如何安装cuda版本的pytorch
  • PTN中的L2VPN与L3VPN技术详解
  • 多少Moreless:向世界展示现代中式家具的生活美学
  • 查幽门螺杆菌的这款同位素长期被海外垄断,秦山核电站实现突破
  • 探秘多维魅力,长江经济带、珠三角媒体总编辑岳阳行启动
  • 今年有望投产里程已近3000公里,高铁冲刺谁在“狂飙”?
  • “85后”贵阳市政府驻重庆办事处主任吴育材拟任新职
  • 苏轼“胡为适南海”?