资源参考自沉默王二、小林coding、javaguide
面试题
谈谈spring
Spring 是一个 Java 后端开发框架,其最核心的作用是帮我们管理 Java 对象,其最重要的功能有两个,分别是IOC和AOP。
IoC也就是控制反转。以前我们要使用一个对象时,都要自己先 new 出来。但有了 Spring 之后,我们只需要告诉 Spring 我们需要什么对象,它就会自动帮我们创建好并注入到 Spring 容器当中。
AOP也就是面向切面编程,在我们需要做一些通用功能的时候特别有用,比如说日志记录、权限校验、事务管理这些,我们不用在每个方法里都写重复的代码,直接用 AOP 就能统一处理。
除此之外spring还帮我们提供了一致的事务管理接口,只需要加个注解就能使用了
还有就是 Spring 对各种企业级功能的集成支持也特别好,比如数据库和消息队列等等,非常方便
spring有哪些常用注解?
依赖注入方面,
@Autowired
是用得最多的,可以标注在字段、setter 方法或者构造方法上。@Qualifier
在有多个同类型 Bean 的时候用来指定具体注入哪一个。@Resource
和@Autowired
功能差不多,不过它是按名称注入的。AOP 相关的注解,
@Aspect
定义切面,@Pointcut
定义切点,@Before
、@After
、@Around
这些定义通知类型。配置方面有Configuration、Bean、Value
事务由Transactional
Resourse和Autowired的区别?
首先从来源上说,
@Autowired
是 Spring 框架提供的注解,而@Resource
是 Java EE 标准提供的注解。换句话说,@Resource
是 JDK 自带的,而@Autowired
是 Spring 特有的。从注入方式上说,
@Autowired
默认按照类型,也就是 byType 进行注入,而@Resource
默认按照名称,也就是 byName 进行注入。
spring 用了哪些设计模式?
1、单例:单例模式是 Spring 的默认行为。默认容器中的 Bean 都是单例的这样可以节省内存,提高性能。当然我们也可以通过
@Scope
注解来改变 Bean 的作用域,比如设置为 prototype 就是每次获取都创建新实例。2、工厂:这个在 Spring 里用得非常多。BeanFactory 就是一个典型的工厂,它负责创建和管理所有的 Bean 对象。当我们通过
@Autowired
获取一个 Bean 的时候,底层就是通过工厂模式来创建和获取对象的。3、模板方法:模板方法模式在 Spring 里也很常见,比如 JdbcTemplate。它定义了数据库操作的基本流程:获取连接、执行 SQL、处理结果、关闭连接,但是具体的 SQL 语句和结果处理逻辑由我们来实现。
4、代理模式:在 AOP 中用得特别多。Spring AOP 的底层实现就是基于动态代理的,对于实现了接口的类用 JDK 动态代理,没有实现接口的类用 CGLIB 代理。比如我们用
@Transactional
注解的时候,Spring 会为我们的类创建一个代理对象,在方法执行前后添加事务处理逻辑。
IOC和AOP介绍一下
IoC:即控制反转的意思,它是一种创建和获取对象的技术思想,依赖注入(DI)是实现这种技术的一种方式。传统开发过程中,我们需要通过new关键字来创建对象。使用IoC思想开发方式的话,我们不通过new关键字创建对象,而是通过IoC容器来帮我们实例化对象,并进行Bean对象生命周期的管理,可以大大降低对象之间的耦合度。
AOP:是面向切面编程,能够将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,以减少系统的重复代码,降低模块间的耦合度。Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。
AOP的核心概念了解吗?
AspectJ:切面,只是一个概念,没有具体的接口或类与之对应,是 Join point,Advice 和 Pointcut 的一个统称。
Join point:连接点,指程序执行过程中的一个点,例如方法调用、异常处理等。
Advice:通知,即我们定义的一个切面中的横切逻辑,有"around","before"和"after"等。Advice 通常作为一个拦截器,也可以包含许多个拦截器作为一条链路围绕着 Join point 进行处理。
Pointcut:切点,用于匹配连接点,一个 AspectJ 中包含哪些 Join point 需要由 Pointcut 进行筛选。
Weaving:织入,在切点的引导下,将通知逻辑插入到目标方法上,使得我们的通知逻辑在方法调用时得以执行。
AOP proxy:AOP 代理,指在 AOP 实现框架中实现切面协议的对象。在 Spring AOP 中有两种代理,分别是 JDK 动态代理和 CGLIB 动态代理。
Target object:目标对象,就是被代理的对象。
Bean的生命周期说一下
首先所有待注入容器的类会生成BeanDefinition对象,然后会进行Bean实例化,实例化会调用合适的构造方法进行,然后是进行属性注入,然后是Bean初始化,然后我们就可以使用Bean,最后使用完Bean被销毁
(根据项目再优化表述)
为什么不推荐Autowired注入?
第一个是字段注入不利于单元测试。字段注入需要使用反射或 Spring 容器才能注入依赖,测试更复杂
第二个是字段注入会隐藏循环依赖问题,而构造方法注入会在项目启动时就去检查依赖关系,能更早发现问题。
第三个是构造方法注入可以使用 final 字段确保依赖在对象创建时就被初始化,避免了后续修改的风险。
Bean的作用域有哪些?
Singleton(单例):在整个应用程序中只存在一个 Bean 实例。默认作用域,Spring 容器中只会创建一个 Bean 实例,并在容器的整个生命周期中共享该实例。
Prototype(原型):每次请求时都会创建一个新的 Bean 实例。每次从容器中获取该 Bean 时都会创建一个新实例。
Request(请求):每个 HTTP 请求都会创建一个新的 Bean 实例。适用于 Web 应用中需求局部性的 Bean。
Session(会话):Session 范围内只会创建一个 Bean 实例。适用于与用户会话相关的 Bean。
Spring怎么解决循环依赖
Spring 通过三级缓存机制来解决循环依赖:
一级缓存:存放完全初始化好的单例 Bean。
二级缓存:存放提前暴露的 Bean,实例化完成,但未初始化完成。
三级缓存:存放 Bean 工厂,用于生成提前暴露的 Bean。
BeanFactory和FactoryBean有什么区别
AOP和AspectJ的区别?
说说jdk动态代理和cglib动态代理的区别
JDK 动态代理要求目标类必须实现至少一个接口,因为它是基于接口来创建代理的。而 CGLIB 代理不需要目标类实现接口,它是通过继承目标类来创建代理的。
从实现原理来说,JDK 动态代理是 Java 原生支持的,它通过反射机制在运行时动态创建一个实现了指定接口的代理类。当我们调用代理对象的方法时,会被转发到 InvocationHandler 的 invoke 方法中,我们可以在这个方法里插入切面逻辑,然后再通过反射调用目标对象的真实方法。
CGLIB 则是一个第三方的字节码生成库,它通过 ASM 字节码框架动态生成目标类的子类,然后重写父类的方法来插入切面逻辑。
说说Spring的事务
Spring 提供了两种事务管理方式,编程式事务和声明式事务。编程式事务就是我们要手动调用事务的开始、提交、回滚这些操作,虽然灵活但是代码比较繁琐。声明式事务只需要在需要事务的方法上加上
@Transactional
注解就好了,Spring 会帮我们自动处理事务的整个生命周期。
Transactional在哪些情况下会失效?
第一种,
@Transactional
注解用在非 public 修饰的方法上。Spring 的 AOP 代理机制决定了它无法代理 private 方法。因为 private 方法在子类中是不可见的,代理类无法覆盖它。第二种,方法内部调用。如果在一个类的方法 A 中,直接调用本类的另外一个加了 @Transactional 的方法 B,那么方法 B 的事务是不会生效的。因为这里实质调用的是this对象,不是代理对象
第三种,如果在事务方法内部用 try-catch 捕获了异常,但没有在 catch 块中将异常重新抛出,或者抛出一个新的能触发回滚的异常,那么 Spring 的事务拦截器就无法感知到异常的发生,也就没办法回滚。
第四种,Spring 事务默认只对 RuntimeException 和 Error 类型的异常进行回滚。如果在代码中抛出的是一个Checked Exception,又没有人为指定事务回滚的异常类型,那么事务同样不会回滚。
事务的传播机制说说
Spring 定义了七种事务传播行为
其中 REQUIRED 是默认的传播行为,表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
SUPPORTS则是支持当前事务,如果当前存在事务就加入,如果不存在则以非事务方式执行,适用于那些不需要强事务保证但可以参与现有事务的操作。
MANDATORY要求必须存在一个当前事务,否则抛出异常,用于强制方法必须在事务上下文中被调用。
REQUIRES_NEW总是会启动一个新的事务,如果当前存在事务则将其挂起,新事务与原有事务完全独立,互不影响,适用于需要独立提交或回滚的场景。
NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务则将其挂起,等该方法执行完毕后再恢复原事务,用于排除某些不需要事务管理的方法。
NEVER严格要求不能在事务中执行,如果当前存在事务则抛出异常,确保方法不会在事务上下文中被调用。
NESTED如果当前存在事务,则在嵌套事务内执行,嵌套事务可以独立提交或回滚,但外层事务回滚会导致嵌套事务也回滚,这种机制提供了更细粒度的事务控制。
SpringMVC的处理流程
Spring MVC 是一个基于 Servlet 的请求处理框架,核心流程可以概括为:请求接收 → 路由分发 → 控制器处理 → 视图解析。
用户发起的 HTTP 请求,首先会被 DispatcherServlet 捕获,这是 Spring MVC 的"前端控制器",负责拦截所有请求,起到统一入口的作用。
DispatcherServlet 接收到请求后,会根据 URL、请求方法等信息,交给 HandlerMapping 进行路由匹配,查找对应的处理器,也就是 Controller 中的具体方法。
找到对应 Controller 方法后,DispatcherServlet 会委托给处理器适配器 HandlerAdapter 进行调用。处理器适配器负责执行方法本身,并处理参数绑定、数据类型转换等。在注解驱动开发中,常用的是 RequestMappingHandlerAdapter。这一层会把请求参数自动注入到方法形参中,并调用 Controller 执行实际的业务逻辑。
Controller 方法最终会返回结果,比如视图名称、ModelAndView 或直接返回 JSON 数据。
当 Controller 方法返回视图名时,DispatcherServlet 会调用 ViewResolver 将其解析为实际的 View 对象,比如 Thymeleaf 页面。在前后端分离的接口项目中,这一步则通常是返回 JSON 数据。
最后,由 View 对象完成渲染,或者将 JSON 结果直接通过 DispatcherServlet 返回给客户端。
说说Springboot
约定大于配置"是 Spring Boot 最核心的理念。它预设了很多默认配置,比如默认使用内嵌的 Tomcat 服务器,默认的日志框架是 Logback 等等
自动装配也是 Spring Boot 的一大特色,它会根据项目中引入的依赖自动配置合适的 Bean,减少了配置的复杂性,使开发者更容易实现应用
Springboot自动装配了解吗?
在 Spring Boot 中,开启自动装配的注解是
@EnableAutoConfiguration
。这个注解会告诉 Spring 去扫描所有可用的自动配置类。Spring Boot 为了进一步简化,把这个注解包含到了
@SpringBootApplication
注解中。也就是说,当我们在主类上使用@SpringBootApplication
注解时,实际上就已经开启了自动装配。
springboot的启动流程说说
Spring Boot 的启动由 SpringApplication 类负责:
- 第一步,创建 SpringApplication 实例,负责应用的启动和初始化;
- 第二步,从 application.yml 中加载配置文件和环境变量;
- 第三步,创建上下文环境 ApplicationContext,并加载 Bean,完成依赖注入;
- 第四步,启动内嵌的 Web 容器。
- 第五步,发布启动完成事件 ApplicationReadyEvent,并调用 ApplicationRunner 的 run 方法完成启动后的逻辑。