200字
面试精选
2026-02-25
2026-03-10

1、JAVA基础

1.1、HashMap

hasdMap.put(k,v)原理:判断数组是否已创建,没有则创建数组;通过k根据hash算法计算出数组下标,判断当前下标是否有数据,没有数据构造node节点插入;有数据就发生了hash碰撞,此时判断k是否相等,相等就覆盖原来的值;不相等判断是否是树型节点,如果是树就构造一个树型节点放到红黑树里,如果不是就创建node节点放在链表里;判断链表长度是否大于 8并且数组长度大于64,大于的话链表转换为红黑树;插入完成之后判断当前节点数是否大于阈值,如果大于开始扩容为原数组的二倍。

1.2、String类能被继承吗 ?

不能被继承,因为String类有final修饰符,而final修饰的类是不能被继承的。

1.3、面向对象特性

封装、继承、多态、抽象

1.4、重定向与转发的区别

请求转发特点:一次请求,浏览器地址不变,访问的是自己本身的web资源,传输的数据不 会丢失。

重定向特点:两次请求,浏览器地址发生变化,可以访问自己Web之外的资源,传输的数据 会丢失。

1.5、Cookie与Session的区别

什么是会话 :用户打开一个浏览器访问页面,访问网站的很多页面,访问完成后将浏览器关闭的过程称为是一次会 话.

会话的作用 进行数据共享 针对某个浏览器的多次请求

Cookie: 在客户端保存用户信息;不安全;单个cookie存储上限不能超过4kb;只能存储ASCII字符串;

Session:在服务端保存用户信息;安全性高;存储容量没有上限;可以存储任何数据类型;

1.6、JVM 的内存结构是什么?新生代、老年代、永久代(元空间)的区别?

JVM 内存分为线程私有(程序计数器、虚拟机栈、本地方法栈)和线程共享(堆、方法区 / 元空间):

  • 堆:存储对象实例,分为新生代(Eden + Survivor0 + Survivor1,存放新创建的对象,GC 频繁)、老年代(存放存活时间长的对象,GC 频率低);

  • 元空间(JDK8 替代永久代):存储类元信息、常量池等,直接使用本地内存,避免永久代 OOM。

1.7、ArrayList 和 LinkedList 的区别,适用场景?

维度

ArrayList

LinkedList

底层结构

动态数组

双向链表

随机访问

O (1)(高效)

O (n)(低效)

增删操作

O (n)(需移动元素)

O (1)(仅需修改指针)

内存占用

连续内存,有扩容冗余

每个节点存储指针,开销大

适用场景

读多写少、随机访问

写多读少、频繁增删

1.8、synchronized 和 Lock 的区别?

维度

synchronized

Lock(ReentrantLock)

性质

关键字,JVM 层面

接口,API 层面

释放锁

自动释放(异常 / 方法结束)

手动释放(必须 finally)

可中断

不可中断

可中断(lockInterruptibly)

公平锁

非公平锁

可指定公平 / 非公平

尝试获取锁

tryLock ()(非阻塞)

性能

低并发下优化后接近 Lock

高并发下更高效

1.9、什么是线程池?核心参数有哪些?为什么要用线程池?
  • 线程池:管理一组可复用的线程,避免频繁创建 / 销毁线程的开销;

  • 核心参数(ThreadPoolExecutor):

    • corePoolSize:核心线程数(常驻线程,即使空闲也不销毁);

    • maximumPoolSize:最大线程数(核心 + 非核心线程总数上限);

    • keepAliveTime:非核心线程空闲超时时间;

    • workQueue:任务队列(存储等待执行的任务);

    • threadFactory:创建线程的工厂;

    • handler:拒绝策略(任务满时的处理方式,如 AbortPolicy、CallerRunsPolicy 等);

  • 优势:降低资源消耗、提高响应速度、统一管理线程、可监控(如任务数、执行时间)。

1.11、什么是类加载机制?双亲委派模型的作用?
  • 类加载过程:加载→验证→准备→解析→初始化;

  • 双亲委派模型:类加载时先委托父类加载器加载,父类加载器无法加载时才自己加载;

  • 作用:避免类重复加载、保证核心类(如 java.lang.String)不被篡改(沙箱安全)。

2、Spring

2.1、bean的作用域

1)singleton单例(默认就是);

2)prototype原型,每次请求都会创建一个新的 bean 实例;

3) request 每一次HTTP请求都会产生一个新的bean,该bean 仅在当前HTTP request内有效;

4)session每一次HTTP请求都会产生一个新的 bean,该bean 仅在当前 HTTP session 内有效;

5)globalSession 类似于标准的 HTTP session 作用域,不过仅仅在基于 portlet 的 web 应用中才 有意义;

2.2、bean的生命周期

注意:singleton单例情况下spring才会管理bean生命周期;

1)对scope为singleton且非懒加载的bean进行实例化;

2)按照Bean定义信息配置信息,注入所有的属性;

3)如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该Bean的id, 此时该Bean就获得了自己在配置文件中的id;

4)如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的 BeanFactory,这样该Bean就获得了自己所在的BeanFactory

5)如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入 该Bean的ApplicationContext,这样该Bean就获得了自己所在的ApplicationContext

6)如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation()方 法;

7)如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法;

8)如果Bean配置了init-method方法,则会执行init-method配置的方法;

9)如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessAfterInitialization()方 法;

10)经过流程9之后,就可以正式使用该Bean了,对于scope为singleton的Bean,Spring的ioc容器中会缓 存一份该bean的实例,而对于scope为prototype的Bean,每次被调用都会new一个新的对象,期生命周 期就交给调用方管理了,不再是Spring容器进行管理了 ;

11)容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法;

12)如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法,至此,整个Bean的 生命周期结束;

2.3、用到设计模式

1)简单工厂模式;又叫做静态工厂方法(StaticFactory Method)模式, 简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得bean对象;

2)工厂方法模式;采用工厂模式,即应用程 序将对象的创建及初始化职责交给工厂对象。如果将应用程序自己的工厂对象交给Spring管理,那 么Spring管理的就不是普通的bean,而是工厂Bean;

3)单例模式;保证一个类仅有一个实例,并提供一个访问它的全局访问点。 bean的单例模式;

4)适配器模式;AdvisorAdapter

5)包装器模式

6)代理模式;1、JDK动态代理。2、CGLib字节码生成技术代理。

7)观察者模式

8)策略模式

9)模板方法模式

2.4、IOC

控制反转,创建对象的控制权限由手动创建交给spring管理创建;底层实现:xml解析、工厂、反射(工厂通过加载xml配置文件获取对象的权限定名,通过反射技术创建对象实例;通过set属性方法进行依赖注入(如果没有设置beanName,默认是通过类型来注入);存放在map中,key=beanName,value=实例);

手写IOC简单案例:

public class BeanFactory {
​
    private static final Map<String,Object> map = new HashMap<>();
​
    static {
        try {
            //加载配置文件
            InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
            //dom4j解析xml配置文件
            Document document = new SAXReader().read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> beanList = rootElement.selectNodes("//bean");
            //将xml里面所配置的bean全部加载到map中
            for (Element element : beanList) {
                map.put(element.attributeValue("id"), Class.forName(element.attributeValue("class")).newInstance());
            }
            List<Element> propertyList= rootElement.selectNodes("//property");
            String parentId;
            Object parentObject;
            for (Element element : propertyList) {
                parentId = element.getParent().attributeValue("id");
                parentObject = map.get(parentId);
                for (Method method : parentObject.getClass().getMethods()) {
                    if (method.getName().equalsIgnoreCase("set" + element.attributeValue("name"))){
                        method.invoke(parentObject,map.get(element.attributeValue("ref")));
                    }
                }
                map.put(parentId,parentObject);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
​
    public static Object getBeanInstance(String id) {
        return map.get(id);
    }
}

2.5、AOP

面向切面编程;对某个方法进行增强;底层实现:jdk动态代理(默认)、cglib动态代理;

1、横切关注点 对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

2、切面(aspect) 类是对物体特征的抽象,切面就是对横切关注点的抽象 ​ 3、连接点(joinpoint) 被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方 法,实际上连接点还可以是字段或者构造器

4、切入点(pointcut) 对连接点进行拦截的定义

5、通知(advice) 所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五 类

6、目标对象 代理的目标对象

7、织入(weave) 将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction) 在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

通知方式:

1)前置通知(@Before):在方法前面执行;

2)正常返回通知(@AfterReturning):在方法正常返回后执行;

3)异常通知(@AfterThrowing ):在方法抛出异常时执行

4)后置通知(@After):方法正常执行或者抛出异常都会执行,相当于finally

5)环绕通知(@Around):在方法执行前后;

2.6、事务

传播机制

1) PROPAGATION_REQUIREDSpring默认的事务传播级别,使⽤该级别的特点是,如果上下⽂中 已经存在事务,那么就加⼊到事务中执⾏,如果当前上下⽂中不存在事务,则新建事务执⾏。所以这个 级别通常能满⾜处理⼤多数的业务场景。

2)PROPAGATION_SUPPORTS ,如果 上下⽂存在事务,则⽀持事务加⼊事务,如果没有事务,则使⽤⾮事务的⽅式执⾏。

3)PROPAGATION_MANDATORY , 该级别的事务要求上下⽂中必须要存在事务,否则就会抛出异 常!

4)PROPAGATION_REQUIRES_NEW ,每次都会新建⼀个事务,并且同时将上下⽂中的事务挂起,执⾏当前新建事务完成以后,上下 ⽂事务恢复再执⾏。

5)PROPAGATION_NOT_SUPPORTED ,上下⽂中存在事务,则挂起事务,执⾏当前逻辑,结束后恢复上下⽂的事务。

6)PROPAGATION_NEVER ,上下⽂中不能存在事务,⼀旦有事务,就抛出runtime异 常,强制停⽌执⾏;

7)PROPAGATION_NESTED ,如果上 下⽂中存在事务,则嵌套事务执⾏,如果不存在事务,则新建事务

隔离级别

1、Serializable :最严格的级别,事务串⾏执⾏,资源消耗最⼤;

2、REPEATABLE READ :保证了⼀个事务不会修改已经由另⼀个事务读取但未提交(回滚)的数据。 避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。(spring事务默认的隔离级别

3、READ COMMITTED :⼤多数主流数据库的默认事务等级,保证了⼀个事务不会读到另⼀个并⾏事务 已修改但未提交的数据,避免了“脏读取”。该级别适⽤于⼤多数系统。

4、Read Uncommitted :保证了读取过程中不会读取到⾮法数据。 上⾯的解释其实每个定义都有⼀些拗⼝,其中涉及到⼏个术语:脏读、不可重复读、幻读。 这⾥解释⼀下: 脏读 :所谓的脏读,其实就是读到了别的事务回滚前的脏数据。⽐如事务B执⾏过程中修改了数据X,在 未提交前,事务A读取了X,⽽事务B却回滚了,这样事务A就形成了脏读。 不可重复读 :不可重复读字⾯含义已经很明了了,⽐如事务A⾸先读取了⼀条数据,然后执⾏逻辑的时 候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读 了。 幻读 :⼩的时候数⼿指,第⼀次数⼗10个,第⼆次数是11个,怎么回事?产⽣幻觉了? 幻读也是这样⼦,事务A⾸先根据条件索引得到10条数据,然后事务B改变了数据库⼀条数据,导致也 符合事务A当时的搜索条件,这样事务A再次搜索发现有11条数据了,就产⽣了幻读

3、动态代理

3.1、JDK动态代理;被代理对象必须实现接口;
/**参数:1、类加载器,2、被代理对象实现的接口数组,3、InvocationHandler实例(必须要重写invoke方法,最终通过invoke方法来对被 *代理类的方法进行增强;)
 */
Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
            /**
            * proxy 代理对象
            * method 被代理对象的方法对象
            * args 被代理对象的方法的参数
            */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("aop增强,方法执行前");
                Object result = method.invoke(obj, args);
                System.out.println("aop增强,方法执行后");
                return result;
            }
        });
通过Proxy.newProxyInstance方法生成代理对象,然后通过代理对象调用被代理对象的任何方法都会执行invoke方法进行增强;
3.2、cglib动态代理;被代理对象不用实现接口;
3.3、动态代理和静态代理的区别;

静态代理:代理对象都是固定写好的.java文件;

动态代理:代理对象是动态生成的,不需要我们手动去编写;

4、Spring Mvc

4.1原理:

1:⽤户发送请求⾄前端控制器DispatcherServlet

2:DispatcherServlet收到请求调⽤HandlerMapping处理器映射器

3:处理器映射器根据请求Url找到具体的Handler(后端控制器),⽣成处理器对象及处理器拦截 器(如果 有则⽣成)⼀并返回DispatcherServlet

4:DispatcherServlet调⽤HandlerAdapter处理器适配器去调⽤Handler

5:处理器适配器执⾏Handler

6:Handler执⾏完成给处理器适配器返回ModelAndView

7:处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的⼀个 底层对 象,包括 Model 和 View

8:前端控制器请求视图解析器去进⾏视图解析,根据逻辑视图名来解析真正的视图。

9:视图解析器向前端控制器返回View

10:前端控制器进⾏视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域 第⼗⼀步:前端控制器向⽤户响应结果

人话:DispatcherServlet 拦截所有请求,根据url定位到对应的Controller,执行业务逻辑,返回视图模型解析模型数据渲染视图,返回客户端;

简单手写springmvc:

public class MyDispatcherServlet extends HttpServlet {
​
​
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }
​
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //通过req获取请求的url,对handlerList中handler的url进行匹配,匹配中了,通过反射调用该对象的对应方法执行业务逻辑进行返回;
    }
​
    @Override
    public void init(ServletConfig config) throws ServletException {
        //1、加载配置文件springmvc.properties,获取包路径
​
        //2、根基包路劲递归扫描所有.class结尾的文件 通过反射实例化放在list集合中
​
        //3、初始化bean对象,初始化IOC容器;遍历list里面的所有实例化对象判断是否标有@MyController和MyService注解添加到Map容器中;
        //key = 注解value值/获取该对象实现接口名的首字母小写,value = 就是该实例化对象
​
        //4、依赖注入;遍历map容器,通过反射获取每个对象的所有字段,匹配@MyController注解。根据注解value值通过name从map中做出对应的对象并注入
        //再或者根据字段类型,获取到key去map中寻找对象通过反射调用set方法注入
​
        //5、构造HandlerMapping处理器映射器,将URL和Method建立映射关系
        //继续遍历IOC容器里面所有对象,并且匹配@RequestMapping注解获取类上的parentUrl,再获取对象的所有方法匹配@RequestMapping注解获取
        //subUrl,拼接起来组成新的URL。创建Handler类,定义属性 url,object,method,paramMap:key = 方法的参数名,value = 参数位置,第几个参数
        //将url、实例化对象,方法名、参数信息封装到Handler添加到handlerList中。
    }
}
4.2、拦截器、过滤器之间的区别和执行顺序

拦截器:只会拦截访问的控制器⽅法(Handler)

过滤器:对Request请求起到过滤的作⽤,作⽤在Servlet之前,如果配置为/*可以对所 有的资源访问(servlet、js/css静态资源等)进⾏过滤处理

执行顺序:过滤器(filter) > 拦截器(inteceptor) > sevlet

5、Spring AI

5.1、核心组件
  • ModelClient:统一调用大模型接口,如 ChatModel、EmbeddingModel 等。

  • PromptTemplate:参数化模板,用于复用 Prompt 结构 。

  • VectorStore:向量数据库抽象层,用于存储和检索向量数据。

  • RAG:实现检索增强生成的组件集合。

  • ChatMemory:管理对话上下文记忆。

5.2、Spring AI、LangChain4j、LangChain 之间有什么区别?
  • LangChain:最知名的 Python 框架,提供丰富的模块(Chain、Agent)来处理复杂任务 。

  • LangChain4j:LangChain 的 Java 移植版本,为 Java 开发者提供了类似的功能 。

  • Spring AI:基于 Spring 生态(Spring Boot、Spring Cloud)构建,核心优势在于无缝集成。它更适合 Java 团队进行企业级业务系统的智能化改造(如智能客服、知识库),可以零成本融入现有微服务体系

    5.3、核心技术:RAG 与 Function Calling
  • 概念:RAG 旨在解决大模型知识陈旧和幻觉问题。流程是先根据用户问题从向量数据库中检索相关知识,然后将这些知识作为上下文发送给大模型生成答案 。

  • Spring AI 实现:通过 VectorStore 查询相关文档,然后用 PromptTemplate 将检索结果与原始问题拼装成增强 Prompt,最后调用大模型生成答案 。

5.4、系统提示词(System Prompt)通常包含哪些组成部分?

一个高质量的 System Prompt 应包含以下要素:

  • 角色定义:赋予 AI 身份(如资深面试官、数学老师)。

  • 核心任务:明确要完成的目标。

  • 规则约束:规定什么不能做(如禁止提及政治、必须包含步骤)。

  • 风格语气:规定表达风格(严肃、温和)。

  • 参考示例:特别是需要结构化输出(如 JSON)时,提供示例至关重要。

  • 优先级界定:多任务时明确优先级 。

6、SpringBoot

6.1、启动原理:

Spring Boot的启动过程,本质上就是创建并初始化Spring应用上下文(ApplicationContext)的过程。

  1. main方法:一切都从一个带有 @SpringBootApplication 注解的 main 方法开始,它调用了 SpringApplication.run()

  2. 创建SpringApplication实例run方法首先会创建一个 SpringApplication 实例。在这个创建过程中,它会进行一些关键的初始化工作,比如判断应用类型(是否为Web应用)、设置初始化器和监听器 。

  3. 准备环境:紧接着,它会准备应用的环境(Environment),加载 application.propertiesapplication.yml 等配置文件,为后续的Bean创建提供配置数据 。

  4. 创建ApplicationContext:根据应用类型(如Web应用),创建对应的 ApplicationContext(应用上下文)。默认情况下,非Web应用是 AnnotationConfigApplicationContext,Web应用是 AnnotationConfigServletWebServerApplicationContext

  5. 刷新上下文 - refreshContext:这是最核心的一步。它调用了Spring框架的“刷新”方法,完成了以下工作 :

    • 执行自动配置:解析并加载所有符合条件的自动配置类(这正是我们下面要讲的自动配置原理)。

    • 扫描组件:根据 @ComponentScan 的配置,扫描并注册用户自定义的Bean 。

    • 实例化Bean:创建所有单例Bean的实例,并进行依赖注入。

  6. 启动内嵌服务器 & 运行Runner:如果是Web项目,此时会启动内嵌的Tomcat、Jetty等服务器 。最后,会执行所有实现了 ApplicationRunnerCommandLineRunner 接口的Bean,执行一些自定义的启动后逻辑 。

总结:

判断应用类型(是否为Web应用)、设置初始化器和监听器,

加载 application.propertiesapplication.yml 等配置文件

解析并加载所有符合条件的自动配置类,以及扫描注册自定义Bean注入

启动内嵌的Tomcat、Jetty等服务器

6.2、自动配置原理

自动配置的核心思想是:根据项目类路径下的依赖、已有的Bean以及配置文件,智能地判断并自动创建所需Bean

主要依赖于 @SpringBootApplication 中的一个核心注解 @EnableAutoConfiguration 。它的工作原理可以分为以下几步:

  1. 开启总开关 (@EnableAutoConfiguration):这个注解通过 @Import(AutoConfigurationImportSelector.class) 导入了一个非常重要的组件 。

  2. 获取候选配置名单 (AutoConfigurationImportSelector)AutoConfigurationImportSelector 会去加载所有Jar包中的特定配置文件。

    • Spring Boot 2.7 之前,这个文件是 META-INF/spring.factories

    • Spring Boot 3.0 之后,优化为 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

    • 这个文件中列出了所有可能的自动配置类(如 DataSourceAutoConfigurationWebMvcAutoConfiguration 等)的完整类名 。

  3. 应用条件注解:拿到所有候选名单后,Spring Boot并不会一股脑全部启用。它会逐一检查每个自动配置类上的条件注解@Conditional系列),只有满足所有条件的配置类才会真正生效 。常用的条件注解包括:

    • @ConditionalOnClass: 当类路径下存在指定的类时才生效。例如,只有当你引入了数据库驱动,DataSourceAutoConfiguration 才会生效 。

    • @ConditionalOnMissingBean: 当Spring容器中没有指定的Bean时才生效。这保证了自定义配置的优先级最高——只要你手动定义了一个 DataSource@Bean,Spring Boot就不会再自动创建一个默认的 。

    • @ConditionalOnProperty: 当配置文件中指定的属性值满足条件时才生效。

  4. 注册Bean并绑定属性:最后,通过筛选的自动配置类(本身就是 @Configuration 配置类)会像普通配置类一样,利用 @Bean 注解的方法向容器中注册所需的Bean 。同时,这些Bean的属性(如数据库URL、用户名等)会通过 @ConfigurationProperties 与配置文件(application.properties)中的值进行绑定 。

总结:

扫描META-INF/spring.factories文件中所有的自动配置类,根据条件相应的自动配置

7、Shiro

7.1、三大核心组件
  • Subject:可以理解为当前“用户”。但它不单单指人,也可以是第三方进程、爬虫等任何与当前应用交互的实体。Subject 是一个门面,所有和安全相关的操作(如登录、注销、权限检查)都通过它来发起。

  • SecurityManager:安全管理器,是 Shiro 框架的核心。它就像 Spring MVC 中的 DispatcherServlet,负责协调和管理所有组件,实际执行 Subject 委托的各种安全操作。

  • Realm:可以看作是 Shiro 与应用程序安全数据之间的“桥梁”或“数据源”。当 Shiro 需要验证用户身份或检查用户权限时,它会通过配置的一个或多个 Realm 来从数据库、LDAP 或配置文件等地获取用户、角色和权限信息。

7.2、认证流程
  1. 收集凭证:应用程序收集用户提交的身份(principals,如用户名)和凭证(credentials,如密码),并封装成一个AuthenticationToken(如 UsernamePasswordToken)。

  2. 执行登录:调用 Subject.login(token) 方法。Subject 会将此请求委托给 SecurityManager

  3. 认证委托SecurityManager 将认证工作委托给内部的 Authenticator 组件。

  4. 多 Realm 策略:如果配置了多个 RealmAuthenticator 会根据设定的认证策略(如所有 Realm 都成功、只要一个成功即可等)来协调认证。

  5. Realm 验证Authenticator 会将 token 传递给一个或多个配置好的 Realm。在 RealmdoGetAuthenticationInfo 方法中,会根据 token 中的用户名去数据源查询用户信息。

  6. 结果处理

    • 如果找不到用户信息,则抛出 UnknownAccountException

    • 如果找到用户,则对用户提交的密码和数据库中存储的密码进行比对。若密码错误,则抛出 IncorrectCredentialsException

    • 如果全部校验通过,认证成功。

8、Mysql

8.1、CHARVARCHAR 的区别是什么?

CHAR定长字符串,存储时会用空格填充到指定长度;VARCHAR变长字符串,存储时只占用实际字符长度加1-2字节的额外空间(用于记录长度)

8.2、InnoDB 和 MyISAM 的主要区别是什么?

特性

InnoDB

MyISAM

事务支持

支持(ACID)

不支持

行级锁 / 表级锁

行级锁(高并发友好)

表级锁(写操作阻塞全表)

外键约束

支持

不支持

崩溃恢复

支持(事务日志)

不支持

全文索引

5.6 版本后支持

原生支持

存储结构

聚簇索引(数据存在索引中)

非聚簇索引(数据和索引分离)

8.3、什么是索引?索引的作用是什么?有哪些类型?

索引:MySQL 中用于快速查询数据的数据结构(主流是 B + 树),相当于书籍的目录。

作用:大幅提升查询效率,降低数据库 IO 成本;但会增加插入 / 更新 / 删除的开销(需要维护索引)。

常见类型:

  • 按数据结构:B + 树索引(最常用)、哈希索引、全文索引、R 树索引;

  • 按逻辑分类:主键索引(PRIMARY KEY)、唯一索引(UNIQUE)、普通索引(INDEX)、联合索引(多字段组合)、前缀索引(对字符串字段的部分字符建索引)。

8.4、什么是事务?事务的 ACID 特性分别指什么?
  • 事务:一组不可分割的数据库操作,要么全部执行成功,要么全部失败回滚(比如转账:扣钱 + 加钱必须同时成功 / 失败)。

  • ACID 特性:

    • A(Atomicity)原子性:事务是最小单位,不可拆分;

    • C(Consistency)一致性:事务执行前后,数据库数据的完整性约束不变(比如转账前后总金额不变);

    • I(Isolation)隔离性:多个事务并发执行时,相互隔离,互不干扰;

    • D(Durability)持久性:事务提交后,修改永久保存,不会因崩溃丢失。

8.5、常见的导致索引失效的场景有哪些?

1、索引字段参与函数 / 运算:如where age+1=10where substr(name,1,2)='zh'

2、使用!=/<>/not in(除非是唯一索引);

3、联合索引不满足最左前缀原则:如联合索引(a,b,c),查询where b=1 and c=2会失效;

4、使用like '%xxx'(模糊查询以 % 开头);

5、字段类型不匹配:如索引字段是 int,查询用where id='123'(字符串);

6、使用or连接非索引字段:如where a=1 or b=2(仅 a 有索引)。

9、Redis

高性能:基于内存操作,单线程模型避免了线程切换开销,QPS 可达 10 万 +;

丰富的数据类型:支持 String、Hash、List、Set、ZSet、Bitmap、HyperLogLog 等;

持久化:支持 RDB 和 AOF 两种持久化方式,避免内存数据丢失;

原子性:所有操作都是原子的,支持事务(弱事务);

高可用:支持主从复制、哨兵(Sentinel)、集群(Cluster);

丰富的功能:发布订阅、过期时间、Lua 脚本、流水线(Pipeline)等。

9.1、Redis 的持久化机制(RDB 和 AOF)有什么区别?

维度

RDB(快照)

AOF(追加日志)

原理

定时将内存中的数据快照写入磁盘

记录每一条写命令,重启时重放命令恢复数据

数据完整性

可能丢失最近一次快照后的数

可配置(always/everysec/no),默认 everysec,最多丢 1 秒数据

性能

备份时 fork 子进程,主进程无 IO,性能高

写命令实时追加,IO 开销大,性能略低(可开启重写优化)

文件大小

二进制文件,体积小,恢复快

文本命令,体积大,恢复慢

使用场景

容忍少量数据丢失,追求高性能

对数据完整性要求高

9.2、 Redis 的过期键删除策略是什么?

Redis 采用三种策略结合的方式:

  1. 惰性删除:访问键时才检查是否过期,过期则删除(节省 CPU,但可能导致过期键长期占用内存);

  2. 定期删除:每隔一段时间(默认 100ms),随机抽取部分过期键删除(平衡 CPU 和内存);

  3. 内存淘汰:当内存达到 maxmemory 时,触发淘汰策略(如 volatile-lru:淘汰过期键中最近最少使用的;allkeys-lru:淘汰所有键中最近最少使用的)。

9.3、 Redis 主从复制的原理是什么?

主从复制用于实现数据备份和读写分离,核心流程:

  1. 从库连接主库,发送 SYNC 命令;

  2. 主库收到命令后,执行 bgsave 生成 RDB 快照,同时将后续写命令缓存到内存;

  3. 主库将 RDB 文件发送给从库,从库接收并加载 RDB,恢复数据;

  4. 主库将缓存的写命令发送给从库,从库执行这些命令,实现数据同步;

  5. 后续主库的写命令会实时同步给从库,保持主从数据一致。

补充:Redis2.8 + 支持增量复制,断线重连时仅同步断线期间的增量数据,无需全量同步。

9.4、 Redis 哨兵(Sentinel)的作用是什么?

哨兵是 Redis 的高可用解决方案,核心功能:

  • 监控:持续检查主库和从库是否正常运行;

  • 自动故障转移:主库宕机时,从哨兵集群选举出新的主库,将其他从库指向新主库,同时通知客户端;

  • 配置更新:自动更新配置文件,记录新的主库地址;

  • 通知:通过 API 向管理员 / 应用程序发送故障通知。

9.5、Redis 集群(Cluster)的原理是什么?

Redis Cluster 用于解决单节点容量和性能瓶颈,核心设计:

  • 分片机制:将整个键空间分为 16384 个哈希槽(slot),每个节点负责一部分槽;

  • 哈希分配:通过CRC16(key) % 16384计算键对应的槽,路由到对应节点;

  • 主从架构:每个槽有 1 个主节点和多个从节点,主节点负责读写,从节点备份;

  • 故障转移:主节点宕机时,从节点选举为新主节点;

  • 无中心节点:客户端可直接连接任意节点,节点间通过

评论