八股Spring
Spring
说一下你对 Spring 的理解
Spring框架核心特性包括:
- IoC容器:Spring通过控制反转实现了对象的创建和对象间的依赖关系管理。开发者只需要定义好Bean及其依赖关系,Spring容器负责创建和组装这些对象。
- AOP:面向切面编程,能够将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,以减少系统的重复代码,降低模块间的耦合度。
- 事务管理:Spring提供了一致的事务管理接口,支持声明式和编程式事务。开发者可以轻松地进行事务管理,而无需关心具体的事务API。
- MVC框架:Spring MVC是一个基于Servlet API构建的Web框架,采用了模型-视图-控制器(MVC)架构。它支持灵活的URL到页面控制器的映射,以及多种视图技术。
spring的核心思想说说你的理解?
核心思想 | 解决的问题 | 实现手段 | 典型应用场景 |
---|---|---|---|
IOC | 对象创建与依赖管理的高耦合 | 容器管理Bean生命周期 | 动态替换数据库实现、服务组装 |
DI | 依赖关系的硬编码问题 | Setter/构造器/注解注入 | 注入数据源、服务层依赖DAO层 |
AOP | 横切逻辑分散在业务代码中 | 动态代理与切面配置 | 日志、事务、权限校验统一处理 |
Spring通过这IOC、DI、AOP三大核心思想,实现了轻量级、高内聚低耦合的企业级应用开发框架,成为Java生态中不可或缺的基石。
Spring IoC和AOP 介绍一下
- IoC:控制反转,它是一种创建和获取对象的技术思想,传统开发过程中,我们需要通过new关键字来创建对象。使用IoC思想开发方式的话,我们不通过new关键字创建对象,而是通过IoC容器来帮我们实例化对象。 通过IoC的方式,可以大大降低对象之间的耦合度。
-
所谓控制就是对象的创建、初始化、销毁。
- 创建对象:原来是 new 一个,现在是由 Spring 容器创建。
- 初始化对象:原来是对象自己通过构造器或者 setter 方法给依赖的对象赋值,现在是由 Spring 容器自动注入。(DI)
- 销毁对象:原来是直接给对象赋值 null 或做一些销毁操作,现在是 Spring 容器管理生命周期负责销毁对象。
- AOP:面向切面编程,能够将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,以减少系统的重复代码,降低模块间的耦合度。Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。
在 AOP 中有以下几个概念:
在 Spring 框架中,IOC 和 AOP 结合使用,可以更好地实现代码的模块化和分层管理。
- 通过 IOC 容器管理对象的依赖关系,然后通过 AOP 将横切关注点统一切入到需要的业务逻辑中。
- 使用 IOC 容器管理 Service 层和 DAO 层的依赖关系,然后通过 AOP 在 Service 层实现事务管理、日志记录等横切功能,使得业务逻辑更加清晰和可维护。
IOC和AOP是通过什么机制来实现的?
Spring IOC 实现机制
- 反射:Spring IOC容器利用Java的反射机制动态地加载类、创建对象实例及调用对象方法,反射允许在运行时检查类、方法、属性等信息,从而实现灵活的对象实例化和管理。
- 依赖注入:IOC的核心概念是依赖注入,即容器负责管理应用程序组件之间的依赖关系。Spring通过构造函数注入、属性注入或方法注入,将组件之间的依赖关系描述在配置文件中或使用注解。
- 设计模式 - 工厂模式:Spring IOC容器通常采用工厂模式来管理对象的创建和生命周期。容器作为工厂负责实例化Bean并管理它们的生命周期,将Bean的实例化过程交给容器来管理。
- 容器实现:Spring IOC容器是实现IOC的核心,通常使用BeanFactory或ApplicationContext来管理Bean。BeanFactory是IOC容器的基本形式,提供基本的IOC功能;ApplicationContext是BeanFactory的扩展,并提供更多企业级功能。
Spring AOP 实现机制
Spring AOP的实现依赖于动态代理技术。动态代理是在运行时动态生成代理对象,而不是在编译时。它允许开发者在运行时指定要代理的接口和行为,从而实现在不修改源码的情况下增强方法的功能。
Spring AOP支持两种动态代理:
- 基于JDK的动态代理:使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现。这种方式需要代理的类至少实现一个接口(InvocationHandler)用invoke()方法。
- 基于CGLIB的动态代理:当被代理的类没有实现接口时,Spring会使用CGLIB库生成一个被代理类的子类作为代理。CGLIB(Code Generation Library)是一个第三方代码生成库,通过继承方式实现代理。
如果让你设计一个SpringIoc,你觉得会从哪些方面考虑这个设计?
- Bean的生命周期管理:需要设计Bean的创建、初始化、销毁等生命周期管理机制,可以考虑使用工厂模式和单例模式来实现。
- 依赖注入:需要实现依赖注入的功能,包括属性注入、构造函数注入、方法注入等,可以考虑使用反射机制和XML配置文件来实现。
- Bean的作用域:需要支持多种Bean作用域,比如单例、原型、会话、请求等,可以考虑使用Map来存储不同作用域的Bean实例。
- AOP功能的支持:需要支持AOP功能,可以考虑使用动态代理机制和切面编程来实现。
- 异常处理:需要考虑异常处理机制,包括Bean创建异常、依赖注入异常等,可以考虑使用try-catch机制来处理异常。
- 配置文件加载:需要支持从不同的配置文件中加载Bean的相关信息,可以考虑使用XML、注解或者Java配置类来实现。
如果让你设计一个SpringIoc,你觉得会从哪些方面考虑这个设计?
- Bean的生命周期管理:需要设计Bean的创建、初始化、销毁等生命周期管理机制,可以考虑使用工厂模式和单例模式来实现。
- 依赖注入:需要实现依赖注入的功能,包括属性注入、构造函数注入、方法注入等,可以考虑使用反射机制和XML配置文件来实现。
- Bean的作用域:需要支持多种Bean作用域,比如单例、原型、会话、请求等,可以考虑使用Map来存储不同作用域的Bean实例。
- AOP功能的支持:需要支持AOP功能,可以考虑使用动态代理机制和切面编程来实现。
- 异常处理:需要考虑异常处理机制,包括Bean创建异常、依赖注入异常等,可以考虑使用try-catch机制来处理异常。
- 配置文件加载:需要支持从不同的配置文件中加载Bean的相关信息,可以考虑使用XML、注解或者Java配置类来实现。
动态代理和静态代理的区别
代理是一种常用的设计模式,目的是:为其他对象提供一个代理以控制对某个对象的访问(就是一个秘书),将两个类的关系解耦。代理类和委托类都要实现相同的接口,因为代理真正调用的是委托类的方法。
区别:
- 静态代理:由程序员创建或者是由特定工具创建,在代码编译时就确定了被代理的类是一个静态代理。静态代理通常只代理一个类;
- 动态代理:在代码运行期间,运用反射机制动态创建生成。动态代理代理的是一个接口下的多个实现类。
什么是反射?有哪些使用场景?
反射机制是指在运行状态下,对于任意一个类,都能够获取这个类的所有信息,动态操作对象(创建对象实例,调用其方法,访问和修改字段值),也就是说,Java 反射允许在运行时获取类的信息并动态操作对象,即使在编译时不知道具体的类也能实现。
反射具有以下特性:
- 运行时类信息访问:反射机制允许程序在运行时获取类的完整结构信息,包括类名、包名、父类、实现的接口、构造函数、方法和字段等。
- 动态对象创建:可以使用反射API动态地创建对象实例,即使在编译时不知道具体的类名。这是通过Class类的newInstance()方法或Constructor对象的newInstance()方法实现的。
- 动态方法调用:可以在运行时动态地调用对象的方法,包括私有方法。这通过Method类的invoke()方法实现,允许你传入对象实例和参数值来执行方法。
- 访问和修改字段值:反射还允许程序在运行时访问和修改对象的字段值,即使是私有的。这是通过Field类的get()和set()方法完成的。
Java反射机制在spring框架中,很多地方都用到了反射,让我们来看看Spring的IoC和AOP是如何使用反射技术的。
1、Spring框架的依赖注入(DI)和控制反转(IoC)
Spring 使用反射来实现控制反转的核心特性:依赖注入。
在Spring中,开发者可以通过XML配置文件或者基于注解的方式声明组件之间的依赖关系。当应用程序启动时,Spring容器会扫描这些配置或注解,然后利用反射来实例化Bean(即Java对象),并根据配置自动装配它们的依赖。
例如,当一个Service类需要依赖另一个DAO类时,开发者可以在Service类中使用@Autowired注解,而无需自己编写创建DAO实例的代码。Spring容器会在运行时解析这个注解,通过反射找到对应的DAO类,实例化它,并将其注入到Service类中。这样不仅降低了组件之间的耦合度,也极大地增强了代码的可维护性和可测试性。
2、动态代理的实现
在需要对现有类的方法调用进行拦截、记录日志、权限控制或是事务管理等场景中,反射结合动态代理技术被广泛应用。
一个典型的例子是Spring AOP(面向切面编程)的实现。Spring AOP允许开发者定义切面(Aspect),这些切面可以横切关注点(如日志记录、事务管理),并将其插入到业务逻辑中,而不需要修改业务逻辑代码。
例如,为了给所有的服务层方法添加日志记录功能,可以定义一个切面,在这个切面中,Spring会使用JDK动态代理或CGLIB(如果目标类没有实现接口)来创建目标类的代理对象。这个代理对象在调用任何方法前或后,都会执行切面中定义的代码逻辑(如记录日志),而这一切都是在运行时通过反射来动态构建和执行的,无需硬编码到每个方法调用中。
这两个例子展示了反射机制如何在实际工程中促进松耦合、高内聚的设计,以及如何提供动态、灵活的编程能力,特别是在框架层面和解决跨切面问题时。
spring是如何解决循环依赖的?
循环依赖指的是两个类中的属性相互依赖对方:例如 A 类中有 B 属性,B 类中有 A属性,从而形成了一个依赖闭环,如下图。
循环依赖问题在Spring中主要有三种情况:
- 第一种:通过构造方法进行依赖注入时产生的循环依赖问题。
- 第二种:通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
- 第三种:通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。
只有【第三种方式】的循环依赖问题被 Spring 解决了,其他两种方式在遇到循环依赖问题时,Spring都会产生异常。
Spring 解决单例模式下的setter循环依赖问题的主要方式是通过三级缓存(都是Mapper类型)解决循环依赖。三级缓存指的是 Spring 在创建 Bean 的过程中,通过三级缓存来缓存正在创建的 Bean,以及已经创建完成的 Bean 实例。
spring框架中都用到了哪些设计模式
- 工厂设计模式 : Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
- 代理设计模式 : Spring AOP 功能的实现。
- 单例设计模式 : Spring 中的 Bean 默认都是单例的。
- 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
- 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
- 观察者模式: 当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知并被自动更新
- 适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。
工代单模包观适(带工摸蛋包观屎)
spring 常用注解有什么?
@Autowired 将bean注入到属性中
@Component将类实例化成Bean
@Bean标记一个方法可以返回Bean
@Configuration标记一个类为配置类,可以包含@Bean
@Controller控制层
@Service服务层
@Mapper数据访问层
Spring的事务什么情况下会失效?
Spring Boot通过Spring框架的事务管理模块来支持事务操作。事务管理在Spring Boot中通常是通过 @Transactional 注解来实现的。事务可能会失效的一些常见情况包括:
- 未捕获异常: 如果一个事务方法中发生了未捕获的异常,并且异常未被处理或传播到事务边界之外,那么事务会失效,所有的数据库操作会回滚。
- 非受检异常: 默认情况下,Spring对运行时异常非受检异常(RuntimeException或其子类)进行回滚处理,这意味着当事务方法中抛出这些异常时,事务会回滚。
- 事务传播属性设置不当: 如果在多个事务之间存在事务嵌套,且事务传播属性配置不正确,可能导致事务失效。特别是在方法内部调用有 @Transactional 注解的方法时要特别注意。
- 跨方法调用事务问题: 如果一个事务方法内部调用另一个方法,而这个被调用的方法没有 @Transactional 注解,这种情况下外层事务可能会失效。
- 事务在非公开方法中失效: 如果 @Transactional 注解标注在私有方法上或者非 public 方法上,事务也会失效。
未捕获的异常 运行时异常 事务嵌套且配置错误 调用了非事务方法 事务为非公开方法
Spring的事务,使用this调用是否生效?
不能生效。因为Spring事务是通过代理对象来控制的,只有通过代理对象的方法调用才会应用事务管理的相关规则。当使用this
直接调用时,是绕过了Spring的代理机制,因此不会应用事务设置。
Bean的生命周期
初始化时
Aware回调
BeanNameAware 根据Bean的id
BeanFactoryAware 用SetBeanFactory方法
ApplicationContextAware 用SetApplicationFactory方法 引入上下文
调用Initial-Method方法
如果实现AOP则会创建动态代理
创建之前可以使用BeanFactoryPostProcessor修改Bean的属性和作用域
实例化时可以反射机制 还可以BeanPostProcessor进行额外处理
Bean的单例和非单例,生命周期是否一样?
不一样的,Spring Bean 的生命周期完全由 IoC 容器控制。Spring 只帮我们管理单例模式 Bean 的完整生命周期,对于 prototype
的 Bean,Spring 在创建好交给使用者之后,则不会再管理后续的生命周期。
具体区别如下:
阶段 | 单例(Singleton) | 非单例(如Prototype) |
---|---|---|
创建时机 | 容器启动时创建(或首次请求时,取决于配置)。 | 每次请求时创建新实例。 |
初始化流程 | 完整执行生命周期流程(属性注入、Aware接口、初始化方法等)。 | 每次创建新实例时都会完整执行生命周期流程(仅到初始化完成)。 |
销毁时机 | 容器关闭时销毁,触发DisposableBean 或destroy-method 。 | 容器不管理销毁,需由调用者自行释放资源(Spring不跟踪实例)。 |
内存占用 | 单实例常驻内存,高效但需注意线程安全。 | 每次请求生成新实例,内存开销较大,需手动管理资源释放。 |
适用场景 | 无状态服务(如Service、DAO层)。 | 有状态对象(如用户会话、临时计算对象)。 |
Spring bean的作用域有哪些?
单例/多例/Requst/Session/webSocket/Application
Spring框架中的Bean作用域(Scope)定义了Bean的生命周期和可见性。不同的作用域影响着Spring容器如何管理这些Bean的实例,包括它们如何被创建、如何被销毁以及它们是否可以被多个用户共享。
Spring支持几种不同的作用域,以满足不同的应用场景需求。以下是一些主要的Bean作用域:
- Singleton(单例):在整个应用程序中只存在一个 Bean 实例。默认作用域,Spring 容器中只会创建一个 Bean 实例,并在容器的整个生命周期中共享该实例。
- Prototype(原型):每次请求时都会创建一个新的 Bean 实例。次从容器中获取该 Bean 时都会创建一个新实例,适用于状态非常瞬时的 Bean。
- Request(请求):每个 HTTP 请求都会创建一个新的 Bean 实例。仅在 Spring Web 应用程序中有效,每个 HTTP 请求都会创建一个新的 Bean 实例,适用于 Web 应用中需求局部性的 Bean。
- Session(会话):Session 范围内只会创建一个 Bean 实例。该 Bean 实例在用户会话范围内共享,仅在 Spring Web 应用程序中有效,适用于与用户会话相关的 Bean。
- Application:当前 ServletContext 中只存在一个 Bean 实例。仅在 Spring Web 应用程序中有效,该 Bean 实例在整个 ServletContext 范围内共享,适用于应用程序范围内共享的 Bean。
- WebSocket(Web套接字):在 WebSocket 范围内只存在一个 Bean 实例。仅在支持 WebSocket 的应用程序中有效,该 Bean 实例在 WebSocket 会话范围内共享,适用于 WebSocket 会话范围内共享的 Bean。
- Custom scopes(自定义作用域):Spring 允许开发者定义自定义的作用域,通过实现 Scope 接口来创建新的 Bean 作用域。
在Spring配置文件中,可以通过标签的scope属性来指定Bean的作用域。例如:
<bean id="myBean" class="com.example.MyBeanClass" scope="singleton"/>
在Spring Boot或基于Java的配置中,可以通过@Scope注解来指定Bean的作用域。例如:
@Bean
@Scope("prototype")
public MyBeanClass myBean() {
return new MyBeanClass();
}
在Spring中,在bean加载/销毁前后,如果想实现某些逻辑,可以怎么做?
- init method()---destory method()
- @Bean的init method()---destory method()
- InitializingBean() DIsposableBean()接口
- @PostConstruct @PreDestory注解
Bean注入和xml注入最终得到了相同的效果,它们在底层是怎样做的?
在Spring框架中,基于注解的Bean注入(如@Autowired
、@Resource
)和基于XML的依赖注入虽然在配置方式上不同,但在底层最终都通过Spring容器的统一机制实现依赖注入。
阶段 | 注解注入 | XML注入 |
---|---|---|
配置解析 | 通过注解处理器扫描类路径,解析@Component 、@Autowired 等注解。 | 解析XML文件中的<bean> 、<property> 、<constructor-arg> 标签。 |
XML 注入
使用 XML 文件进行 Bean 注入时,Spring 在启动时会读取 XML 配置文件,以下是其底层步骤:
- Bean 定义解析:Spring 容器通过
XmlBeanDefinitionReader
类解析 XML 配置文件,读取其中的<bean>
标签以获取 Bean 的定义信息。 - 注册 Bean 定义:解析后的 Bean 信息被注册到
BeanDefinitionRegistry
(如DefaultListableBeanFactory
)中,包括 Bean 的类、作用域、依赖关系、初始化和销毁方法等。 - 实例化和依赖注入:当应用程序请求某个 Bean 时,Spring 容器会根据已经注册的 Bean 定义:
- 首先,使用反射机制创建该 Bean 的实例。
- 然后,根据 Bean 定义中的配置,通过 setter 方法、构造函数或方法注入所需的依赖 Bean。
注解注入
使用注解进行 Bean 注入时,Spring 的处理过程如下:
- 类路径扫描:当 Spring 容器启动时,它首先会进行类路径扫描,查找带有特定注解(如
@Component
、@Service
、@Repository
和@Controller
)的类。 - 注册 Bean 定义:找到的类会被注册到
BeanDefinitionRegistry
中,Spring 容器将为其生成 Bean 定义信息。这通常通过AnnotatedBeanDefinitionReader
类来实现。 - 依赖注入:与 XML 注入类似,Spring 在实例化 Bean 时,也会检查字段上是否有
@Autowired
、@Inject
或@Resource
注解。如果有,Spring 会根据注解的信息进行依赖注入。
尽管使用的方式不同,但 XML 注入和注解注入在底层的实现机制是相似的,主要体现在以下几个方面:
- BeanDefinition:无论是 XML 还是注解,最终都会生成
BeanDefinition
对象,并存储在同一个BeanDefinitionRegistry
中。 - 后处理器:
- Spring 提供了多个 Bean 后处理器(如
AutowiredAnnotationBeanPostProcessor
),用于处理注解(如@Autowired
)的依赖注入。 - 对于 XML,Spring 也有相应的后处理器来处理 XML 配置的依赖注入。
- Spring 提供了多个 Bean 后处理器(如
- 依赖查找:在依赖注入时,Spring 容器会通过
ApplicationContext
中的 BeanFactory 方法来查找和注入依赖,无论是通过 XML 还是注解,都会调用类似的查找方法。
Spring给我们提供了很多扩展点,这些有了解吗?
Spring框架提供了许多扩展点,使得开发者可以根据需求定制和扩展Spring的功能。以下是一些常用的扩展点:
- BeanFactoryPostProcessor:允许在Spring容器实例化bean之前修改bean的定义。常用于修改bean属性或改变bean的作用域。
- BeanPostProcessor:可以在bean实例化、配置以及初始化之后对其进行额外处理。常用于代理bean、修改bean属性等。
- PropertySource:用于定义不同的属性源,如文件、数据库等,以便在Spring应用中使用。
- ImportSelector和ImportBeanDefinitionRegistrar:用于根据条件动态注册bean定义,实现配置类的模块化。
- Spring MVC中的HandlerInterceptor:用于拦截处理请求,可以在请求处理前、处理中和处理后执行特定逻辑。
- Spring MVC中的ControllerAdvice:用于全局异常处理器。
- Spring Boot的自动配置:通过创建自定义的自动配置类,可以实现对框架和第三方库的自动配置。
- 自定义注解:创建自定义注解,用于实现特定功能或约定,如权限控制、日志记录等。
SpringMVC
MVC分层介绍一下
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
- 视图(view): 为用户提供使用界面,与用户直接进行交互。
- 控制器(controller): 用于将用户请求转发给相应的 Model 进行处理,并根据 Model 的计算结果向用户提供相应响应。它使视图与模型分离。
- 模型(model): 代表一个存取数据的对象或 JAVA POJO(Plain Old Java Object,简单java对象)。它也可以带有逻辑,主要用于承载数据,并对用户提交请求进行计算的模块。模型分为两类,一类称为数据承载 Bean,一类称为业务处理Bean。所谓数据承载 Bean 是指实体类(如:User类),专门为用户承载业务数据的;而业务处理 Bean 则是指Service 或 Mapper 对象, 专门用于处理用户提交请求的。
流程步骤:
- 用户通过View 页面向服务端提出请求,可以是表单请求、超链接请求、AJAX 请求等;
- 服务端 Controller 控制器接收到请求后对请求进行解析,找到相应的Model,对用户请求进行处理Model 处理;
- 将处理结果再交给 Controller(控制器其实只是起到了承上启下的作用);
- 根据处理结果找到要作为向客户端发回的响应View 页面,页面经渲染后发送给客户端。
相比于Controller Service Mapper模型把Service和Mapper合并为Module 在前面加了View
了解SpringMVC的处理流程吗?
用户请求url到前端控制器,然后查找Mapping 映射器 将请求映射到适配器,适配器调用Controller处理器,处理器返回ModelAndVIew到前端,再经过视图解析,渲染后返回给用户
前端->映射器->适配器->处理器->视图解析
SpringBoot
怎么理解SpringBoot中的约定大于配置?
约定大于配置是Spring Boot的核心设计理念,它通过预设合理的默认行为和项目规范,大幅减少开发者需要手动配置的步骤,从而提升开发效率和项目标准化程度。
理解 Spring Boot 中的“约定大于配置”原则,可以从以下几个方面来解释:
- 自动化配置:Spring Boot 提供了大量的自动化配置,通过分析项目的依赖和环境,自动配置应用程序的行为。开发者无需显式地配置每个细节,大部分常用的配置都已经预设好了。例如,引入
spring-boot-starter-web
后,Spring Boot会自动配置内嵌Tomcat和Spring MVC,无需手动编写XML。 - 默认配置:Spring Boot 为诸多方面提供大量默认配置,如连接数据库、设置 Web 服务器、处理日志等。只需要修改就可以连接自己的数据库地址,开发人员无需手动配置这些常见内容,框架已做好决策。例如,默认的日志配置可让应用程序快速输出日志信息,无需开发者额外繁琐配置日志级别、输出格式与位置等。
- 约定的项目结构:Spring Boot 约定特定项目结构,通常主应用程序类(含 main 方法)置于根包,控制器类、服务类、数据访问类等分别放在相应子包,如
com.example.demo.controller
放控制器类,com.example.demo.service
放服务类等。此约定使团队成员更易理解项目结构与组织,新成员加入项目时能快速定位各功能代码位置,提升协作效率。
SpringBoot自动装配原理是什么?
首先SpringBoot的核心是IoC,也就是控制反转,控制反转就是将对象的控制权交给IOC容器来管理,我们在程序中直接调用,那么就需要在程序启动时候将这些对象创建到容器当中,就用自动装配。
自动装配是通过SPI机制实现的(Service Provider Inter) ,SPI就是在程序运行时候,这个机制允许第三方开发者或模块提供自己的实现来扩展或替换框架中的某些部分,而不需要修改框架的源代码
自动装配机制允许开发者在项目中引入相关依赖,SpringBoot根据这些依赖自动装配应用程序
的功能和上下文。
在启动类上加上@SpringBootApplication注解,这是一个复合注解,包括了@EnableAutoConfiguration注解,这是真正的自动装配的核心,
首先要引入Starter,启动依赖组件时候 组件名必须包含@Configuration配置类,在Configuration中我们需要用@Bean注解声明需要装配到IoC容器中的Bean对象,
而这个配置类需要放在jar包里面,通过SpringBoot中约定大于配置这个理念,把这个类放在META-INF/spring.factories文件中,这样Spring就知道这个配置类的位置
在这个文件中通过·AutoConfigurationImportSelector进行扫描,判断是否满足导入条件,最后动态加载到应用程序中。
SpringBoot的自动配置是为了让开发人员可以更加聚焦在业务代码的编写上,而不需要关心与业务无关的配置。
接下来将逐个解释这些注解的作用:
@Target({ElementType.TYPE})
: 该注解指定了这个注解可以用来标记在类上。在这个特定的例子中,这表示该注解用于标记配置类。@Retention(RetentionPolicy.RUNTIME)
: 这个注解指定了注解的生命周期,即在运行时保留。这是因为 Spring Boot 在运行时扫描类路径上的注解来实现自动配置,所以这里使用了 RUNTIME 保留策略。@Documented
: 该注解表示这个注解应该被包含在 Java 文档中。它是用于生成文档的标记,使开发者能够看到这个注解的相关信息。@Inherited
: 这个注解指示一个被标注的类型是被继承的。在这个例子中,它表明这个注解可以被继承,如果一个类继承了带有这个注解的类,它也会继承这个注解。@SpringBootConfiguration
: 这个注解表明这是一个 Spring Boot 配置类。如果点进这个注解内部会发现与标准的 @Configuration 没啥区别,只是为了表明这是一个专门用于 SpringBoot 的配置。@EnableAutoConfiguration
: 这个注解是 Spring Boot 自动装配的核心。它告诉 Spring oot 启用自动配置机制,根据项目的依赖和配置自动配置应用程序的上下文。通过这个注解,SpringBoot 将尝试根据类路径上的依赖自动配置应用程序。@ComponentScan
: 这个注解用于配置组件扫描的规则。在这里,它告诉 SpringBoot 在指定的包及其子包中查找组件,这些组件包括被注解的类、@Component 注解的类等。其中的 excludeFilters 参数用于指定排除哪些组件,这里使用了两个自定义的过滤器,分别是 TypeExcludeFilter 和 AutoConfigurationExcludeFilter。
说几个启动器(starter)?
web、security、mybatis、jdbc->连接MYSQL、redis、test
- spring-boot-starter-web:这是最常用的起步依赖之一,它包含了Spring MVC和Tomcat嵌入式服务器,用于快速构建Web应用程序。
- spring-boot-starter-security:提供了Spring Security的基本配置,帮助开发者快速实现应用的安全性,包括认证和授权功能。
- mybatis-spring-boot-starter:这个Starter是由MyBatis团队提供的,用于简化在Spring Boot应用中集成MyBatis的过程。它自动配置了MyBatis的相关组件,包括SqlSessionFactory、MapperScannerConfigurer等,使得开发者能够快速地开始使用MyBatis进行数据库操作。
- spring-boot-starter-data-jpa 或 spring-boot-starter-jdbc:如果使用的是Java Persistence API (JPA)进行数据库操作,那么应该使用spring-boot-starter-data-jpa。这个Starter包含了Hibernate等JPA实现以及数据库连接池等必要的库,可以让你轻松地与MySQL数据库进行交互。你需要在application.properties或application.yml中配置MySQL的连接信息。如果倾向于直接使用JDBC而不通过JPA,那么可以使用spring-boot-starter-jdbc,它提供了基本的JDBC支持。
- spring-boot-starter-data-redis:用于集成Redis缓存和数据存储服务。这个Starter包含了与Redis交互所需的客户端(默认是Jedis客户端,也可以配置为Lettuce客户端),以及Spring Data Redis的支持,使得在Spring Boot应用中使用Redis变得非常便捷。同样地,需要在配置文件中设置Redis服务器的连接详情。
- spring-boot-starter-test:包含了单元测试和集成测试所需的库,如JUnit, Spring Test, AssertJ等,便于进行测试驱动开发(TDD)。
SpringBoot里面有哪些重要的注解?还有一个配置相关的注解是哪个?
Spring Boot 中一些常用的注解包括:
- @SpringBootApplication:用于标注主应用程序类,标识一个Spring Boot应用程序的入口点,同时启用自动配置和组件扫描。
- @Controller:标识控制器类,处理HTTP请求。
- @RestController:结合@Controller和@ResponseBody,返回RESTful风格的数据。
- @Service:标识服务类,通常用于标记业务逻辑层。
- @Mapper:标识数据访问组件,通常用于标记数据访问层。
- @Component:通用的Spring组件注解,表示一个受Spring管理的组件。
- @Autowired:用于自动装配Spring Bean。
- @Value:用于注入配置属性值。
- @RequestMapping:用于映射HTTP请求路径到Controller的处理方法。
- @GetMapping、@PostMapping、@PutMapping、@DeleteMapping:简化@RequestMapping的GET、POST、PUT和DELETE请求。
- @Configuration:用于指定一个类为配置类,通常与@Bean配合使用,@Bean用于声明一个Bean实例,由Spring容器进行管理。
Springboot怎么开启事务?
方法上添加 @Transactional
注解即可。
Mybatis
与传统的JDBC相比,MyBatis的优点?
- 基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任 何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于统一管理;提供 XML 标签,支持编写动态 SQL 语句,并可重用。
- 与 JDBC 相比,减少了 50%以上的代码量,消除了 JDBC 大量冗余的代码,不 需要手动开关连接;
- 很好的与各种数据库兼容,因为 MyBatis 使用 JDBC 来连接数据库,所以只要 JDBC 支持的数据库 MyBatis 都支持。
还记得JDBC连接数据库的步骤吗?
加载驱动程序->建立数据库连接->创建对象用于查询->查询与更新->处理结果->关闭连接
使用Java JDBC连接数据库的一般步骤如下:
- 加载数据库驱动程序:在使用JDBC连接数据库之前,需要加载相应的数据库驱动程序。可以通过 Class.forName("com.mysql.jdbc.Driver") 来加载MySQL数据库的驱动程序。不同数据库的驱动类名会有所不同。
- 建立数据库连接:使用 DriverManager 类的 getConnection(url, username, password) 方法来连接数据库,其中url是数据库的连接字符串(包括数据库类型、主机、端口等)、username是数据库用户名,password是密码。
- 创建 Statement 对象:通过 Connection 对象的 createStatement() 方法创建一个 Statement 对象,用于执行 SQL 查询或更新操作。
- 执行 SQL 查询或更新操作:使用 Statement 对象的 executeQuery(sql) 方法来执行 SELECT 查询操作,或者使用 executeUpdate(sql) 方法来执行 INSERT、UPDATE 或 DELETE 操作。
- 处理查询结果:如果是 SELECT 查询操作,通过 ResultSet 对象来处理查询结果。可以使用 ResultSet 的 next() 方法遍历查询结果集,然后通过 getXXX() 方法获取各个字段的值。
- 关闭连接:在完成数据库操作后,需要逐级关闭数据库连接相关对象,即先关闭 ResultSet,再关闭 Statement,最后关闭 Connection。
如果项目中要用到原生的mybatis去查询(XML),该怎样写?
配置->创建类->创建XML文件->编写Mapper接口->编写查询语句->调用查询方法
- 配置MyBatis: 在配置文件中配置数据源、MyBatis的Mapper文件位置等信息。
- 创建实体类: 创建与数据库表对应的实体类,字段名和类型需与数据库表保持一致。
public class User {
private Long id;
private String username;
private String email;
// Getters and setters
}
- 创建对应XML文件: 在resources目录下创建XML文件,定义SQL语句和映射关系。
<!-- userMapper.xml -->
<mapper namespace="com.example.dao.UserMapper">
<select id="selectUserById" resultType="com.example.model.User">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>
- 编写Mapper接口: 创建Mapper接口,定义查询方法。
public interface UserMapper {
User selectUserById(Long id);
}
- 编写具体的XML文件: 在XML文件中编写对应的SQL语句。
- 调用查询方法: 调用Mapper接口中的方法进行查询。
// 在Service层中调用
User user = userMapper.selectUserById(1);
通过以上步骤,你可以利用原生的MyBatis框架来进行数据库查询操作。请确保配置正确、SQL语句准确并与数据库字段匹配,以确保查询的准确性和高效性。
Mybatis里的 # 和 $ 的区别?
- Mybatis 在处理 #{} 时,会创建预编译的 SQL 语句,将 SQL 中的 #{} 替换为 ? 号,在执行 SQL 时会为预编译 SQL 中的占位符(?)赋值,调用 PreparedStatement 的 set 方法来赋值,预编译的 SQL 语句执行效率高,并且可以防止SQL 注入,提供更高的安全性,适合传递参数值。参数化查询,可以防止SQL注入
- Mybatis 在处理 ${} 时,只是创建普通的 SQL 语句,然后在执行 SQL 语句时 MyBatis 将参数直接拼入到 SQL 里,不能防止 SQL 注入,因为参数直接拼接到 SQL 语句中,如果参数未经过验证、过滤,可能会导致安全问题。
负载均衡有哪些算法?
- 简单轮询:将请求按顺序分发给后端服务器上,不关心服务器当前的状态,比如后端服务器的性能、当前的负载。
- 加权轮询:根据服务器自身的性能给服务器设置不同的权重,将请求按顺序和权重分发给后端服务器,可以让性能高的机器处理更多的请求
- 一致性哈希:根据请求的客户端 ip(有助于保持会话状态)、或请求的url通过哈希算法得到一个数值,利用该数值取模映射出对应的后端服务器,这样能保证同一个客户端或相同url的请求每次都使用同一台服务器
- 最小活跃数:统计每台服务器上当前正在处理的请求数,也就是请求活跃数,将请求分发给活跃数最少的后台服务器
-
加权最少连接:结合了最少连接和加权轮询,根据服务器的权重和当前连接数分配请求。
- 简单随机:将请求随机分发给后端服务器上,请求越多,各个服务器接收到的请求越平均
- 加权随机: