【ⓈSpring & Spring MVC】Spring面试题
什么是Spring
Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。常见的配置方式有三种:基于XML的配置、基于注解的配置、基于Java的配置。
- 基于XML的配置:所有的 Spring 组件都用 xml 文件的形式来进行配置,主要的命名空间:context、beans、jdbc、tx、aop、mvc 。
- 基于注解的配置:
- Spring 在 2.5 版本以后开始支持用注解的方式来配置依赖注入。可以用注解的方式来替代 XML 方式的 bean 描述,可以将 bean 描述转移到组件类的内部,只需要在相关类上、方法上或者字段声 明上使用注解即可。
- 注解的初衷是简化 xml 配置的,因此不能单独存在,有些配置不方便用注解解决,比如 jdbc 的连接池,仍需 xml 配置。
- 注解装配在 Spring 中是默认关闭的,需要在xml中使用<context:annotation-config/> 标签配置打开。
- 比较重要的注解类型:@Repository、@Service、@Controller、@Autowired
- 基于 Java 的配置:
- Spring 3.0 以后,提供了 Java 配置的能力,Spring 4.x 和 SpringBoot 都推荐使用 Java 配置。
- 通过提供新的注解标签,提供完全替代 xml 的解决方案,xml = 注解 + javaConfig
- 两个重要的注解:@Configuration代表配置类,@Bean生命一个bean
主要由以下几个模块组成:
- Spring Core:核心类库,提供IOC服务;
- Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);
- Spring AOP:AOP服务;
- Spring DAO:对JDBC的抽象,简化了数据访问异常的处理;
- Spring ORM:对现有的ORM框架的支持;
- Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传;
- Spring MVC:提供面向Web应用的Model-View-Controller实现。
你们项目中为什么使用Spring框架?
- 轻量:Spring 是轻量的,基本的版本大约2MB。
- 控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
- 面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
- 容器:Spring 包含并管理应用中对象的生命周期和配置。
- MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。
- 事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。
- 异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。
什么是 Spring 的依赖注入?
依赖注入, 是 IOC 的一个方面。意思是说你不用创建对象, 而只需要描述它如何被创建 。你不用在代码里直接组装你的组件和服务, 但是要在配置文件里描述哪些组件需要哪些服务, 之后一个容器( IOC 容器)负责把他们它们装起来。
依赖注入的方式有几种,各是什么?
构造器注入
将被依赖对象通过构造函数的参数注入给依赖对象,并且在初始化对象的时候注入。
private UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao){
this.userDao = userDao;
}
优点:对象初始化完成后便可获得可使用的对象。
缺点:当需要注入的对象很多时,构造器参数列表将会很长;
不够灵活。若有多种注入方式,每种方式只需注入指定几个依赖,那么就需要提供多个重载的构造函数,麻烦。
setter方法注入
IoC Service Provider通过调用成员变量提供的setter函数将被依赖对象注入给依赖类。
private UserDao userDao;
@Autowired
public setUserDao(UserDao userDao){
this.userDao = userDao;
}
优点:灵活,可以选择性地注入需要的对象。
缺点:依赖对象初始化完成后由于尚未注入被依赖对象,因此还不能使用。
基于字段的依赖注入
@Autowired
private UserDao userDao;
基于字段的依赖注入方式会在Idea当中有提示,但是这种使用方式使用的也最广泛,因为简洁方便,你甚至可以在一些Spring指南中看到这种注入方法。
缺点:
- 不能应用到有final修饰的变量,因为final类型的变量在调用class的构造函数的这个过程当中就得初始化完成。
- 隐藏依赖性,我们一般都把属性声明为private形式的,不看代码基本不知道依赖了哪些
Spring官方建议不要使用属性依赖注入,推荐的方法是使用基于构造函数和基于setter的依赖注入。对于必需的依赖项,建议使用基于构造函数的注入,以使它们成为不可变的,并防止它们为null。对于可选的依赖项,建议使用基于Setter的注入。
什么是 spring 装配
当 bean 在 Spring 容器中组合在一起时,它被称为装配或 bean 装配。Spring 容器需要知道需要什么 bean 以及容器应该如何使用依赖注入来将 bean 绑定在一起,同时装配 bean。
自动装配有哪些方式?
-
no - 这是默认设置,表示没有自动装配。应使用显式 bean 引用进行装配。 -
byName - 它根据 bean 的名称注入对象依赖项。它匹配并装配其属性与 XML 文件中由相同名称定义的 bean。 -
byType - 它根据类型注入对象依赖项。如果属性的类型与 XML 文件中的一个 bean 名称匹配,则匹配并装配属性。 -
constructor- 它通过调用类的构造函数来注入依赖项。它有大量的参数。 -
autodetect - 首先容器尝试通过构造函数使用 autowire 装配,如果不能,则尝试通过 byType 自动装配。
自动装配有什么局限?
- 重写:你仍然需要使用<property>设置指明依赖,这意味着总要重写自动装配。
- 原生数据类型:你不能自动装配简单的属性,如原生类型、字符串和类。
- 模糊特性:自动装配总是没有自定义装配精确,因此,如果可能尽量使用自定义装配。
你用过哪些重要的 Spring 注解?
-
@Controller - 用于 Spring MVC 项目中的控制器类。 -
@Service - 用于服务类。 -
@RequestMapping - 用于在控制器处理程序方法中配置 URI 映射。 -
@ResponseBody - 用于发送 Object 作为响应,通常用于发送 XML 或 JSON 数据作为响应。 -
@PathVariable - 用于将动态值从 URI 映射到处理程序方法参数。 -
@Autowired - 用于在 spring bean 中自动装配依赖项。 -
@Qualifier - 使用 @Autowired 注解,以避免在存在多个 bean 类型实例时出现混淆。 -
@Scope - 用于配置 spring bean 的范围。 -
@Configuration,@ComponentScan 和 @Bean - 用于基于 java 的配置。 -
@Aspect,@Before,@After,@Around,@Pointcut - 用于切面编程(AOP)。
如何在 spring 中启动注解装配?
默认情况下,Spring 容器中未打开注解装配。因此,要使用基于注解装配,我们必须通过配置<context:annotation-config /> 元素在 Spring 配置文件中启用它。
Spring框架中都用到了哪些设计模式?
简单工厂模式:Spring 中的 BeanFactory 就是简单工厂模式的体现。根据传入一个唯一的标识来获得 Bean 对象,但是在传入参数后创建还是传入参数前创建,要根据具体情况来定。
工厂模式:Spring 中的 FactoryBean 就是典型的工厂方法模式,实现了 FactoryBean 接口的 bean是一类叫做 factory 的 bean。其特点是,spring 在使用 getBean() 调用获得该 bean 时,会自动调用该 bean 的 getObject() 方法,所以返回的不是 factory 这个 bean,而是这个 bean.getOjbect()方法的返回值。
单例模式:在 spring 中用到的单例模式有:scope="singleton" ,注册式单例模式,bean 存放于Map 中。bean name 当做 key,bean 当做 value。
原型模式:在 spring 中用到的原型模式有:scope="prototype" ,每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。
迭代器模式:在 Spring 中有个 CompositeIterator 实现了 Iterator,Iterable 接口和 Iterator 接口,这两个都是迭代相关的接口。可以这么认为,实现了 Iterable 接口,则表示某个对象是可被迭代的。Iterator 接口相当于是一个迭代器,实现了 Iterator 接口,等于具体定义了这个可被迭代的对象时如何进行迭代的。
代理模式:Spring 中经典的 AOP,就是使用动态代理实现的,分 JDK 和 CGlib 动态代理。
适配器模式:Spring 中的 AOP 中 AdvisorAdapter 类,它有三个实现:MethodBeforAdviceAdapter、AfterReturnningAdviceAdapter、ThrowsAdviceAdapter。Spring会根据不同的 AOP 配置来使用对应的 Advice,与策略模式不同的是,一个方法可以同时拥有多个Advice。Spring 存在很多以 Adapter 结尾的,大多数都是适配器模式。
观察者模式:Spring 中的 Event 和 Listener。spring 事件:ApplicationEvent,该抽象类继承了EventObject 类,JDK 建议所有的事件都应该继承自 EventObject。spring 事件监听器:ApplicationListener,该接口继承了 EventListener 接口,JDK 建议所有的事件监听器都应该继承EventListener。
模板模式:Spring 中的 org.springframework.jdbc.core.JdbcTemplate 就是非常经典的模板模式的应用,里面的 execute 方法,把整个算法步骤都定义好了。
责任链模式:DispatcherServlet 中的 doDispatch() 方法中获取与请求匹配的处理器HandlerExecutionChain,this.getHandler() 方法的处理使用到了责任链模式。
注意:这里只是列举了部分设计模式,其实里面用到了还有享元模式、建造者模式等。可选择性的回答。
Spring 中有多少种 IOC 容器?
- BeanFactory - BeanFactory 就像一个包含 bean 集合的工厂类。它会在客户端要求时实例化 bean。
- ApplicationContext - ApplicationContext 接口扩展了 BeanFactory 接口。它在 BeanFactory 基础上提供了一些额外的功能。
区分 BeanFactory 和 ApplicationContext
Spring 是怎么解决循环依赖的?
三级缓存:
- 一级缓存:singletonObjects ,存放已经经历完整生命周期的 Bean 对象
- 二级缓存:earlySingletonObjects ,存放早期暴露出来的 Bean 对象,Bean 的生命周期未结束(属性还未填充完成)
- 三级缓存:singletonFactories 存放可以生成 Bean 的工厂。
注意:非单例的 bean 是没有缓存的,不会将其放到三级缓存中。
整个流程大致如下:
- 首先 A 完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),在初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建出来;
- 然后 B 就走创建流程,在 B 初始化的时候,同样发现自己依赖 C,C 也没有被创建出来;
- 这个时候 C 又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A)。这个时候由于 A 已经添加至缓存中(一般都是添加至三级缓存 singletonFactories),通过ObjectFactory 提前曝光,所以可以通过 ObjectFactory#getObject() 方法来拿到 A 对象。C 拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中;
- 回到 B,B 也可以拿到 C 对象,完成初始化,A 可以顺利拿到 B 完成初始化。到这里整个链路就已经完成了初始化过程了。
关键字:三级缓存,提前曝光。
说说事务的隔离级别
未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据。
提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)可重复读(Repeated Read):在同一个事务内的查询都是事务开始时刻一致的,Mysql的InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻读(多个事务同时修改同一条记录,事务之间不知道彼此存在,当事务提交之后,后面的事务修改的数据将会覆盖前事务,前一个事务就像发生幻觉一样)。
可重复读(REPEATABLE_READ):它能确保同一事务多次查询的结果一致。但也会有新的问题,比如此级别的事务正在执行时,另一个事务成功的插入了某条数据,但因为它每次查询的结果都是一样的,所以会导致查询不到这条数据,自己重复插入时又失败(因为唯一约束的原因)。明明在事务中查询不到这条信息,但自己就是插入不进去,这就叫幻读 (Phantom Read)。
可串行化(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。
不可重复读和幻读的区别主要是:解决不可重复读需要锁定了当前满足条件的记录,而解决幻读需要锁定当前满足条件的记录及相近的记录。比如查询某个商品的信息,可重复读事务隔离级别可以保证当前商品信息被锁定,解决不可重复读;但是如果统计商品个数,中途有记录插入,可重复读事务隔离级别就不能保证两个事务统计的个数相同。
说说事务的传播级别
Spring事务定义了7种传播机制:
- PROPAGATION_REQUIRED:默认的Spring事物传播级别,若当前存在事务,则加入该事务,若不存在事务,则新建一个事务。
- PAOPAGATION_REQUIRE_NEW:若当前没有事务,则新建一个事务。若当前存在事务,则新建一个事务,新老事务相互独立。外部事务抛出异常回滚不会影响内部事务的正常提交。
- PROPAGATION_NESTED:如果当前存在事务,则嵌套在当前事务中执行。如果当前没有事务,则新建一个事务,类似于REQUIRE_NEW。
- PROPAGATION_SUPPORTS:支持当前事务,若当前不存在事务,以非事务的方式执行。
- PROPAGATION_NOT_SUPPORTED:以非事务的方式执行,若当前存在事务,则把当前事务挂起。
- PROPAGATION_MANDATORY:强制事务执行,若当前不存在事务,则抛出异常.
- PROPAGATION_NEVER:以非事务的方式执行,如果当前存在事务,则抛出异常。
Spring事务传播级别一般不需要定义,默认就是PROPAGATION_REQUIRED,除非在嵌套事务的情况下需要重点了解。
Spring 事务实现方式
编程式事务管理:这意味着你可以通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。(若你选择编程式事务管理,Spring推荐使用 TransactionTemplate)
声明式事务管理:这种方式意味着你可以将事务管理和业务代码分离。你只需要通过注解或者XML配置管理事务。
Spring框架的事务管理有哪些优点
- 为不同的事务API(如JTA, JDBC, Hibernate, JPA)提供了统一的编程模型。
- 为编程式事务管理提供了一个简单的API。
- 支持声明式事务管理。
- 可以和Spring的多种数据访问技术很好的融合。
事务三要素是什么?
- 数据源:表示具体的事务性资源,是事务的真正处理者,如MySQL等。
- 事务管理器:像一个大管家,从整体上管理事务的处理过程,如打开、提交、回滚等。
- 事务应用和属性配置:像一个标识符,表明哪些方法要参与事务,如何参与事务,以及一些相关属性如隔离级别、超时时间等。
Spring AOP的实现原理和场景
AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。
适用场景:事务管理、安全检查、权限控制、数据校验、缓存、对象池管理等。
AOP(这里的AOP指的是面向切面编程思想,而不是Spring AOP)主要的的实现技术主要有Spring AOP和AspectJ。
AspectJ的底层技术
AspectJ的底层技术是静态代理,即用一种AspectJ支持的特定语言编写切面,通过一个命令来编译,生成一个新的代理类,该代理类增强了业务类,这是在编译时增强,相对于下面说的运行时增强,编译时增强的性能更好。
Spring AOP
Spring AOP采用的是动态代理,在运行期间对业务方法进行增强,所以不会生成新类,对于动态代理技术,Spring AOP提供了对JDK动态代理的支持以及CGLib的支持。
JDK动态代理只能为接口创建动态代理实例,而不能对类创建动态代理。需要获得被目标类的接口信息(应用Java的反射技术),生成一个实现了代理接口的动态代理类(字节码),再通过反射机制获得动态代理类的构造函数,利用构造函数生成动态代理类的实例对象,在调用具体方法前调用invokeHandler方法来处理。
CGLib动态代理需要依赖asm包,把被代理对象类的class文件加载进来,修改其字节码生成子类(无论被代理类是否实现接口都可以)。
但是Spring AOP基于注解配置的情况下,需要依赖于AspectJ包的标准注解。
Spring AOP的五种通知类型
- 前置通知 Before advice(@Before):在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常
- 后置通知 After returning advice(@After):在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行
- 异常通知 After throwing advice(@AfterThrowing):在连接点抛出异常后执行
- 最终通知 After (finally) advice(@AfterReturning):在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容
- 环绕通知 Around advice(@Around):环绕通知围绕在连接点前后,能在方法调用前后自定义一些操作,还需要负责决定是继续处理 join point (调用 ProceedingJoinPoint 的 proceed 方法)还是中断执行
Spring AOP通知顺序
注意:Spring版本为5.2.7.RELEASE---SpringBoot版本为2.2.8.RELEASE时,AOP执行顺序发生了些许改变。
Spring版本为5.2.7.RELEASE之前:
正常情况:
@Around 环绕通知前
@Before前置通知
result:5
@Around 环绕通知后
@After后置通知
@AfterReturning返回后通知
异常情况:
@Around 环绕通知前
@Before前置通知
@After后置通知
@AfterThrowing异常通知
Spring版本为5.2.7.RELEASE之后:
正常情况:
@Around 环绕通知前
@Before前置通知
result:5
@AfterReturning返回后通知
@After后置通知
@Around 环绕通知后
异常情况:
@Around 环绕通知前
@Before前置通知
@AfterThrowing异常通知
@After后置通知
Spring中事务与aop的先后顺序
Spring声明式事务是基于AOP实现的,那么,如果我们在同一个方法自定义多个AOP,我们如何指定他们的执行顺序呢?
配置AOP执行顺序的主要方式是指定order,order越小越是最先执行。
1)实现org.springframework.core.Ordered接口
2)通过@Order注解
spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行around,before。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行after、afterReturn。也就是说对多个AOP来说,先before的,一定后after。
如果我们要在同一个方法事务提交后执行自己的AOP,那么把事务的AOP order设置为2,自己的AOP order设置为1,然后在afterReturn里边处理自己的业务逻辑。
Spring bean的作用域和生命周期
作用域:
生命周期:
什么是Spring MVC?简单介绍下你对Spring MVC的理解?
Spring MVC是一个基于Java,实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把模型-视图-控制器分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
Spring MVC的主要组件?
(1)前端控制器 DispatcherServlet(不需要程序员开发)
作用:接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。
(2)处理器映射器HandlerMapping(不需要程序员开发)
作用:根据请求的URL来查找Handler
(3)处理器适配器HandlerAdapter(不需要程序员开发))
注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。
(4)处理器Handler(需要程序员开发,其实就是Controller)
(5)视图解析器 ViewResolver(不需要程序员开发)
作用:进行视图的解析,根据视图逻辑名解析成真正的视图(view)
(6)视图View(需要程序员开发)
View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)
SpringMVC工作原理(执行流程)
具体步骤:
第一步:发起请求到前端控制器(DispatcherServlet)
第二步:前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找)
第三步:处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略。
第四步:前端控制器调用处理器适配器HandlerAdapter去执行Handler
第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler
第六步:Handler执行完成给适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)
第八步:前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可
第九步:视图解析器向前端控制器返回View
第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)
第十一步:前端控制器向用户响应结果
MVC是什么?MVC设计模式的好处有哪些
MVC是一种设计模式(设计模式就是日常开发中编写代码的一种好的方法和经验的总结)。模型(model)-视图(view)-控制器(controller),三层架构的设计模式。用于实现前端页面的展现与后端业务数据处理的分离。
MVC设计模式的好处:
- 分层设计,实现了业务系统各个组件之间的解耦,有利于业务系统的可扩展性,可维护性。
- 有利于系统的并行开发,提升开发效率。
Spring MVC与Struts2区别
类型 | Spring MVC | Struts2 |
---|---|---|
前端控制器 | Spring MVC的前端控制器是servlet:DispatcherServlet | Struts2的前端控制器是filter:StrutsPreparedAndExcutorFilter |
拦截机制和请求参数的接受方式 | Spring MVC是方法级别的拦截,一个方法对应一个request上下文。Spring MVC是使用方法的形参接收请求的参数,基于方法的开发,线程安全,单例模式 | Struts2框架是类级别的拦截,每次请求就会创建一个Action,一个Action对应一个request上下文。Struts2是通过类的成员变量接收请求的参数,是基于类的开发,线程不安全,多例模式 |
配置和性能 | 配置少,开发效率和性能高于Struts2 | 配置多,开发效率和性能低于Spring MVC |
与Spring框架的整合 | Spring MVC是Spring框架的一部分,无缝整合 | Struts2与Spring整合相对麻烦 |
@Autowired 注解有什么用?
@Autowired 可以更准确地控制应该在何处以及如何进行自动装配。此注解用于在 setter 方法,构造函数,具有任意名称或多个参数的属性或方法上自动装配 bean。默认情况下,它是类型驱动的注入。
@Qualifier 注解有什么用?
当您创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。
@Autowired和@Resource的区别
@Resource的作用相当于@Autowired,均可标注在字段或属性的setter方法上。
不同点:
- @Autowired就是由Spring提供;@Resource是由javax.annotation.Resource提供。
- @Autowired只按照byType 注入;@Resource默认按byName自动注入,也提供按照byType 注入;
- @Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。@Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
推荐使用@Resource注解在字段上,这样就不用写setter方法了。并且这个注解是属于J2EE的,减少了与Spring的耦合,这样代码看起就比较优雅 。
使用 Spring 访问 Hibernate 的方法有哪些?
- 使用 Hibernate 模板和回调进行控制反转
- 扩展 HibernateDAOSupport 并应用 AOP 拦截器节点
参考: |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
2022-04-01 Seata AT模式案例讲解(Spring)