面试十几家公司(小中大企业)总结的[Java八股文],标记重点的一定要掌握,几乎50%概率会被问到。一直不推荐死记硬背,应该结合场景业务代码、手动画图加深理解,传承程序猿开源精神,现分享有需要的人。 **

一,框架篇**

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

       单例Bean 的线程安全性取决于 Bean 的具体实现。一般来说,在spring中的bean都是无状态(没有可修改的成员变量)的对象,就没有[线程安全问题,但如果在bean中定义了可修改的成员变量,是需要考虑线程安全问题的。

       我们可以加锁,使用synchronized关键字进行同步,保证同一时刻只有一个线程能访问该方法;也可以通过 @Lookup 注解设置bean为多例,每次请求都会创建一个bean实例,多个线程操作互不影响,但性能受损;

2.能介绍一下动态代理吗

       动态代理是指在程序运行时通过反射机制自动生成代理对象,确保在不修改目标类的情况下,增强目标类的功能(类增强),Java提供了两种方式来实现动态代理:

       在SpringBoot2之前,Spring会根据目标对象的特性自动选择,比如目标类实现了至少一个接口默认使用JDK动态代理,如果没有实现接口就默认CGLIB代理;而SpringBoot2.2之后,统一使用CGLIB代理(无论目标类是否实现接口)

       JDK动态代理:基于接口的动态代理,只能代理实现了接口的类,创建代理对象速度快(直接根据接口生成字节码并由类加载器加载),方法调用较慢(需要通过Mehtod.invoke( )方法调用,反射方法相对较慢。说明:JDK6方法调用比CGLIB慢,但JDK7/8对反射调用做了优化,不比CGLIB慢);

       CGLIB动态代理:基于类继承的动态代理,生成目标类的子类来实现代理,代理对象创建速度慢(使用ASM字节码生成目标类的子类),方法调用快(直接覆盖目标方法,并在方法中插入拦截逻辑,没有走反射);

动态代理提供了高度的灵活性和扩展性,但也带来了性能和复杂性,广泛应用于AOP、远程代理、Spring的依赖注入等场景。

3.过滤器和拦截器有什么区别?

(1)过滤器是Servlet层面的,随Servlet容器启动初始化;拦截器是Spring框架的,随Spring容器启动初始化;

(2)过滤器在拦截器之前执行,可以处理所有的请求;而拦截器只能处理SpingMVC的Controller请求;

(3)过滤器操作更底层,一般处理跟业务无关的逻辑操作,比如请求/响应的全局编码设置;拦截器更贴近业务,比如用户登陆状态验证;

例子:过滤器设置请求编码 UTF-8

  1. @WebFilter("/*") // 拦截所有请求  
  2. public class EncodingFilter implements Filter {  
  3.     @Override  
  4.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)  
  5.             throws IOException, ServletException {  
  6.         // 设置编码  
  7.         request.setCharacterEncoding("UTF-8");  
  8.         response.setCharacterEncoding("UTF-8");  
  9.   
  10.         // 继续往下走  
  11.         chain.doFilter(request, response);  
  12.     }  

例子:登录拦截器(未登录跳转到登录页)

  1. public class LoginInterceptor implements HandlerInterceptor {  
  2.    @Override  
  3.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  
  4.             throws Exception {  
  5.         // 假设 session 里有 loginUser 才表示登录  
  6.         Object user = request.getSession().getAttribute("loginUser");  
  7.         if (user == null) {  
  8.             response.sendRedirect("/login"); // 跳转到登录页  
  9.             return false; // 拦截  
  10.         }  
  11.         return true; // 放行  
  12.     }  

4.spring事务失效的场景有哪些?

       事务底层是基于AOP实现的,而AOP是通过代理对象实现的。@Transactional注解的实现逻辑,可修饰类或方法:

(1)启动时扫描注解

当Spring容器启动时,TransactionAnnotationParser会扫描@Transactional注解,并将其解析为一个TransactionAttribute对象

(2)创建代理对象

Spring使用AOP为带有@Transactional的类生成代理对象,核心是TransactionInterceptor(事务拦截器)

(3)方法调用拦截

当外部调用代理对象的方法时,事务拦截器会拦截方法调用,拦截器通过TransactionManager来管理事务:判断当前是否存在事务,如果需要新建事务就调用begin( )方法,执行目标方法,方法执行成功则提交事务,异常时根据回滚规则决定是否回滚。

spring事务失效的场景如下:

(1)方法没有用public 修饰,代理默认只会拦截公共方法;

(2)同一个类中通过this来调用事务方法,这时候调用不会经过代理对象(事务的开启、提交、回滚等逻辑都是在代理对象的方法调用链中实现的,this是当前对象本身,不是代理对象)。解决方案:通过将自调用的方法提取到另一个服务类解决;

(3)事务注解标记在接口上而不是实现类上;

(4)自己手动捕获异常没有抛出,事务无法知悉

(5)事务默认只会回滚运行时异常和错误。发生受检查异常时事务无法回滚,可通过在catch中抛出RuntimeException异常;或者通过配置@Transactional(rollbackFor = Exception.class)设置回滚所有异常。

  1. @Service  
  2. public class OrderService {  
  3.    
  4.     @Autowired  
  5.     private OrderRepository orderRepository;  
  6.    
  7.     @Transactional  
  8.     public void updatePriceWrong(Long orderId) {  
  9.         try {  
  10.             // 更新数据库数据  
  11.             orderRepository.updatePrice(orderId, 20);  
  12.    
  13.             // 抛出受检查异常  
  14.             throw new IOException("模拟IO异常");  
  15.         } catch (IOException e) {  
  16.             // 手动捕获,但没有继续抛出  
  17.             System.out.println("捕获异常: " + e.getMessage());  
  18.         }  
  19.         // 此时事务感知不到异常,最终数据会提交!  
  20.     }  
  21. }  

5.spring的IOC容器和bean的创建过程?

IOC容器的创建过程为:

(1)读取配置文件:加载配置文件或配置类中的Bean定义;

(2)创建BeanFactory:初始化 DefaultListableBeanFactory 作为默认的Bean工厂,来管理 Bean的定义(BeanDefinition)和实例;

(3)解析BeanDefinition:Spring解析每个Bean,封装成BeanDefinition对象,存入BeanDefinitionMap中;

(4)注册BeanDefinition到BeanFactory中;

(5)创建单例Bean:ApplicationContext在容器刷新时就创建单例Bean

(1)实例化:Spring容器启动时,根据XML配置文件或注解扫描来获取Bean的定义信息,通过反射调用构造函数来创建Bean实例;

(2)依赖注入:Spring容器根据Bean的配置,通过setter注入、字段注入或构造器注入进行依赖注入;

(3)初始化:Spring容器为每个Bean调用初始化方法,比如Aware接口回调、前置处理等;

(4)使用Bean:在容器中,Bean可以随时被获取和使用;

(5)销毁bean:当容器关闭时,Spring会调用Bean的销毁方法来销毁bean

需要全套面试笔记及答案【扫一扫】                         即可免费获取**

6.能说一下Spring中的循环引用吗,怎么解决

       循环依赖就是两个或多个模块、类(Bean)、组件之间互相依赖,形成一个闭环。循环依赖在spring中是允许的,spring框架利用三级缓存能解决大部分的循环依赖,其关键就是要提前暴露未完全创建好的Bean。

       一级缓存(Singleton Objects Map),单例池,用来存储完全初始化好的单例Bean;二级缓存(Early Singleton Objects Map):用来存储已经实例化但未完全初始化的Bean(用于提前暴露对象);三级缓存(Singleton Factories Map):用来存储对象工厂,可以通过工厂创建早期Bean(用于解决代理对象的创建)。

       一般的循环依赖,比如A和B对象通过setter注入形成循环依赖,首先创建A实例,将这个半成品A加入到二级缓存中,接着注入依赖B,从二级缓存中获取半成品A将B初始化, B创建成功,将B存储到单例池中,在单例池中将B注入给A,A创建成功,存储到单例池中;(但二级缓存不能解决代理对象,所以引入了三级缓存)

       创建A实例,将对象A生成ObjectFactory对象放入到三级缓存中,接着注入B,发现B不存在,实例化B,B也生成ObjectFactory对象放入三级缓存,B从三级缓存中获取A对象工厂创建的代理对象,代理对象放入二级缓存中,将A的代理对象注入给B,B创建成功放入单例池,最后将B注入给A,A也创建成功放入单例池;

以上的方法有两种条件:1.依赖的Bean必须是单例;2.不是通过构造器注入依赖的。对于构造行循环依赖可以使用@Lazy 注解进行懒加载来实现。

7.能说一下SpringMVC的执行流程吗?

       SpringMVC 是Spring框架中用于构建Web应用程序的核心模块,以前端控制器为基础(类似于调度器),提供了一套灵活强大的组件来处理HTTP请求、业务逻辑和视图渲染

(1)发送请求:客户端发送请求,被前端控制器拦截,将请求发送给处理器映射器;

(2)处理器映射:处理器映射器根据URL找到对应的Controller类和方法;

(3)处理器适配:找到处理器后,前端控制器交给处理器适配器执行,处理器适配器处理参数,将请求参数绑定到方法参数或对象里

(4)调用处理器:处理器适配器通过反射调用Controller中的业务方法,返回对象数据;

(5)响应数据:将对象转化为JSON,返回给前端渲染页面;

8.[SpringBoot]的自动配置原理能说一下吗?

       SprintBoot项目中的启动类上有一个注解@SpringBootApplication,这个注解封装@EnableAutoConfiguration注解:用来启动SpringBoot的自动配置机制,通过@Import 导入了 AutoConfigurationImportSelector 在SpringBoot启动时,会扫描META-INF/ spring.factories文件(springboot2)/ AutoConfiguration.import(springboot3),文件里列出了所有自动配置类的路径位置,配置类通过各种@Conditional条件注解动态加载满足条件的bean,将其导入到Spring容器中;

常见的有:

  • @ConditionalOnClass:某个类在 classpath 下才生效(比如 JdbcTemplate)。
  • @ConditionalOnMissingBean:如果用户没有自定义这个 Bean,才注入默认的 Bean。
  • @ConditionalOnProperty:配置文件中存在某个配置项时才生效。

这保证了 默认生效,但允许用户覆盖

9.Spring中的常用注解有哪些?

10.Mybatis的执行流程?

(1)配置加载阶段:解析Mybatis配置文件(如XML文件,或直接添加依赖,在 .yml 文件上配置Mybatis)根据配置文件创建会话工厂SqlSessionFactory;

(2)SQL执行阶段:通过会话工厂创建会话SqlSession实例(封装了JDBC操作,包含了执行SQL语句的方法);通过动态代理生成Mapper接口的实例;通过Executor执行SQL语句:1.将Java对象属性转换为SQL参数,2.进行SQL解析,替换${ } 和 #{ },生成最终的SQL执行,将结果转换为Java对象;

(3)释放资源阶段:Mybatis还提供了事务的管理,支持自动提交和手动控制事务;最后释放SqlSession对象资源,关闭数据库连接

11.spring的核心是什么?(重点)

(1)依赖注入(Dependency Injection,DI)

依赖注入是 Spring 框架的核心功能之一,它是基于控制反转(IoC,Inversion of Control)来实现。传统的开发中,组件通常会显式地创建其依赖的对象,而在 Spring 中, IoC 容器负责管理对象的生命周期及其依赖关系。

简化开发:通过将对象的创建和管理交给 Spring 容器,开发者只需要关注业务逻辑,而无需手动创建和管理对象的实例。

解耦:对象之间的依赖关系由 Spring 容器管理,而不是硬编码在代码中,这样可以降低模块之间的耦合度,提高代码的灵活性和可测试性。

依赖注入方式:

  1. 构造器注入:通过构造器传递依赖对象。
  2. Setter 注入:通过 setter 方法注入依赖对象。
  3. 字段注入:直接在字段上使用注解来注入依赖。

注意:官方推荐使用构造器注入的方式(因为依赖对象定义往往用final修饰,确保了依赖的不可变性和不为null,更加稳定),但企业中常用的是字段注入,更加方便;

  1. 面向切面编程(Aspect-Oriented Programming,AOP)

AOP称为面向切面编程,核心思想是将类中的公共行为(横切关注点)切割出来,封装为一个可重用的模块(切面类),在切面类中定义了切入点和通知。作用是减少系统中的重复代码,降低模块之间的耦合度,提高可维护性和可扩展性;

       而我在项目中也经常用到AOP,比如有很多数据库表都有一些公共字段(createTime, updateTime, createUser, updateUser),每次对这些表进行插入或修改操作都要更新这些公共字段,所以可以利用AOP封装一个模块,根据当前时间、用户ID对属性赋值,在mapper中添加注解就实现了公共字段的自动填充功能。

       而AOP在一些业务场景用的比较多的比如:记录操作日志、缓存处理等等。

(3)Spring 的 IoC 容器

IOC(Inversion of Control),控制反转,这是一种设计模式,核心思想是将对象的创建、依赖注入和生命周期管理交给IOC容器。在传统的编程方式中,我们一般需要在类中显示地创建依赖对象,通过硬编码方式来控制对象的创建和管理,而在Spring中,Bean以及对象之间的依赖关系都交给IOC容器负责,降低了代码之间的耦合度,也提高了系统的灵活性;

Spring中主要是通过XML配置和注解配置(@Component 、@Autowired、@Configuration)两种方式来注册Bean

例如:使用AOP給业务添加日志记录

  1. @Aspect    // 定义为切面类
  2. @Component  
  3. public class LogAspect {  
  4.   
  5.     // 1. 切入点:拦截所有 service 包下的方法  
  6.     @Pointcut("execution(* com.example.service..(..))")  
  7.     public void serviceMethods() {}  
  8.   
  9.     // 2. 前置通知:方法执行前记录  
  10.     @Before("serviceMethods()")  
  11.     public void beforeMethod(JoinPoint joinPoint) {  
  12.         String methodName = joinPoint.getSignature().getName();  
  13.         Object[] args = joinPoint.getArgs();  
  14.         System.out.println("[前置日志] 正在调用方法:" + methodName + ",参数:" + Arrays.toString(args));  
  15.     }  
  16.   
  17.     // 3. 后置通知:方法执行后记录  
  18.     @AfterReturning(value = "serviceMethods()", returning = "result")  
  19.     public void afterMethod(JoinPoint joinPoint, Object result) {  
  20.         String methodName = joinPoint.getSignature().getName();  
  21.         System.out.println("[后置日志] 方法:" + methodName + " 返回值:" + result);  
  22.     }  
  23.   
  24.     // 4. 异常通知:出现异常时记录  
  25.     @AfterThrowing(value = "serviceMethods()", throwing = "ex")  
  26.     public void afterThrowingMethod(JoinPoint joinPoint, Exception ex) {  
  27.         String methodName = joinPoint.getSignature().getName();  
  28.         System.out.println("[异常日志] 方法:" + methodName + " 抛出了异常:" + ex.getMessage());  
  29.     }  
  30. }  

需要全套面试笔记及答案【扫一扫】                         即可免费获取**

12. ObjectFactory 和 BeanFacotry的区别?

BeanFactory实际就是IOC容器,而ObjectFactory的作用如下:

(1)当需要推迟对象的创建来避免循环依赖或优化性能时,可以使用ObjectFactory

(2)在单例Bean中注入一个短作用域的Bean时,可以通过ObjectFactory确保获取最新的实例

13.Spring Bean一共有几种作用域?

       在注解配置@Scope或XML配置scope属性就可以设置Bean的作用域。

(1)singleton(默认),每个IOC容器仅存在一个实例,所有依赖注入共享同一个对象;

(2)prototype:每次注入依赖(或getBean( )方法)都会创建一个新实例;

(3)request:每个HTTP请求创建一个新实例,仅在Web应用中有效;

(4)session:每个HTTP Session创建一个实例,用户会话期间共享;

(5)application:每个ServletContext生命周期内一个实例;

(6)websocket:每个websocket会话一个实例,生命周期和websocket连接一致;

14.Spring拦截链的实现?

       在Spring框架中,拦截链通过责任链模式实现,核心目的是将多个拦截器(或通知)按顺序组织,在目标方法或请求处理的不同阶段依次触发:

       在Spring Web中,拦截器实现HandlerInterceptor接口,编写preHandel、postHandle、afterCompletion方法实现业务执行前后逻辑添加(如权限校验、日志记录),另外需要编写一个实现WebMvcConfigurer接口的配置类,注册拦截器,设置哪些路径需要拦截和放行,

执行步骤如下:

(1)初始化链:根据Hander(处理器/controller层)对应的URL和拦截器列表配置,创建处理器执行链;

(2)preHandel阶段:按拦截器配置顺序依次调用preHandle方法。如果某个拦截器返回false,终止后续拦截器和handler的执行;

(3)执行handler:调用Controller方法处理请求;

(4)postHandler阶段:按拦截器配置的逆序调用postHandle( );

(5)渲染视图:处理ModelAndView,生成相应内容;

(6)afterCompletion阶段:无论请求成功或异常,按拦截器配置的逆序调用afterCompletion方法

1. 编写拦截器类

  1. import org.springframework.stereotype.Component;  
  2. import org.springframework.web.servlet.HandlerInterceptor;  
  3. import javax.servlet.http.HttpServletRequest;  
  4. import javax.servlet.http.HttpServletResponse;  
  5.   
  6. @Component  
  7. public class LogInterceptor implements HandlerInterceptor {  
  8.   
  9.     /** 
  10.      * 在请求处理之前执行(Controller方法调用前) 
  11.      * @return true 表示继续流程,false 表示拦截请求 
  12.      */  
  13.     @Override  
  14.     public boolean preHandle(HttpServletRequest request,  
  15.                              HttpServletResponse response,  
  16.                              Object handler) throws Exception {  
  17.         String uri = request.getRequestURI();  
  18.         System.out.println("拦截器日志:请求 URI 为:" + uri);  
  19.         return true;  
  20.     }  
  21.   
  22.     /** 
  23.      * 请求处理之后执行(Controller方法调用之后) 
  24.      */  
  25.     @Override  
  26.     public void postHandle(HttpServletRequest request,  
  27.                            HttpServletResponse response,  
  28.                            Object handler,  
  29.                            org.springframework.web.servlet.ModelAndView modelAndView) throws Exception {  
  30.         System.out.println("拦截器日志:请求处理完成(postHandle)");  
  31.     }  
  32.   
  33.     /** 
  34.      * 请求完全完成之后执行(包括视图渲染后) 
  35.      */  
  36.     @Override  
  37.     public void afterCompletion(HttpServletRequest request,  
  38.                                 HttpServletResponse response,  
  39.                                 Object handler,  
  40.                                 Exception ex) throws Exception {  
  41.         System.out.println("拦截器日志:请求完全结束(afterCompletion)");  
  42.     }  
  43. }  

2. 注册拦截器(配置类)

  1. import org.springframework.beans.factory.annotation.Autowired;  
  2. import org.springframework.context.annotation.Configuration;  
  3. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;  
  4. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;  
  5.   
  6. @Configuration  // Spring MVC 配置类  
  7. public class WebConfig implements WebMvcConfigurer {  
  8.   
  9.     @Autowired  
  10.     private LogInterceptor logInterceptor;  
  11.   
  12.     @Override  
  13.     public void addInterceptors(InterceptorRegistry registry) {  
  14.         registry.addInterceptor(logInterceptor)       // 注册拦截器  
  15.                 .addPathPatterns("/**")          // 拦截所有请求路径  
  16.                 .excludePathPatterns("/login", "/error"); // 排除某些路径  
  17.     }  
  18. }  

15.spring用到了哪些设计模式?

(1)工厂模式:BeanFactory和ApplicationContext是Spring的核心容器,负责创建和管理Bean实例;

(2)单例模式:Spring默认将Bean的作用域设置为单例,保证容器中只有一个实例;

(3)代理模式:Spring AOP使用动态代理生成代理对象增强类;

(4)模板方法模式:jdbcTemplate、RestTemplate等模板类封装了固定流程,用户只需实现具体逻辑(SQL执行、HTTP请求);

(5)适配器模式:在SpringMVC中适配不同类型的控制器

16. Spring Bean注册到容器的方式有哪些?

(1)基于XML配置文件,显示声明Bean的类和依赖关系;

(2)基于注解扫描注册,通过@Component、@Service、@Controller注解定义为Bean,通过组件扫描(@ComponentScan)注册为Bean

(3)基于Java配置类注册,在@Configuration注解的类中的方法添加@Bean注解;

17. @Qualifier注解有什么作用?

       @Qualifier注解用于解决依赖注入的歧义性问题,当容器中存在多个相同类型的Bean时,@Autowired注解无法自动选择具体的Bean注入,此时需要配合@Qualifier来指定具体的Bean名称。

       @Qualifier注解和@Primary注解的区别:@Primary用来标记某个Bean为默认优先注入的候选者,而@Qualifier用来显示指定具体Bean的名称;

  1. @Component  
  2. public class UserService {  
  3.   
  4.     @Autowired  
  5.     @Qualifier("mysqlUserRepository") // 精确指定注入哪个 Bean  
  6.     private UserRepository userRepository;  

18. @Bean和@Component有什么区别?

(1)作用目标:@Component注解作用在类上;@Bean注解作用在方法上,一般在配置类;

(2)使用场景:@Service、@Controller等注解都是@Component的特化形式,一般用于自己编写的类;而@Bean一般用于第三方库的类或需要手动控制实例化的对象;

(3)使用方式:@Component标记一个类,让Spring自动扫描并创建Bean;@Bean需要显示地配置和创建,返回一个对象作为Bean

19. @Component、@Controller、@Repository、@Service注解的区别是什么?

(1)@Component是它们的通用注解,标记任意类为Bean,没有特定的功能,是其他注解的元注解;

(2)@Controller用于标记控制层,处理HTTP请求,支持请求URL映射;

(3)@Service用于标记业务类,没有额外功能,区分职责提高代码可读性;

(4)@Repository用于标记持久层,自动转换数据访问异常

20.spring启动过程?

(1)容器初始化:调用Application.run( )方法启动应用,创建ApplicationContext;

(2)配置加载:解析配置类、扫描组件、注册Bean的定义;

(3)Bean实例化和依赖注入:根据Bean的定义创建对象并注入依赖;

(4)扩展处理:在Bean初始化前后插入逻辑(如AOP代理),实现生命周期回调增强功能;

(5)容器就绪:完成启动,通过事件机制实现扩展点

21. springBoot启动过程?

(1)初始化:调用Application.run( )方法启动应用,加载配置器和器;

(2)准备环境:加载配置文件(如application.properties)

(3)创建ApplicationContext:初始化容器;

(4)刷新容器:加载Bean定义、执行自动配置、启动内嵌容器、Bean的依赖注入初始化;

(5)执行自定义执行后逻辑;

(6)发布 ApplicationReadyEvent:标志应用完全就绪

22. application.properties和application.yml和application.ymal有什么区别?

(1)语法格式:.properties文件使用键值对格式,通过 . 表示层级关系;yml和ymal文件基于YAML格式,使用换行缩进表示层级关系,可读性更高;

(2)加载顺序:同目录下,.properties 文件的优先级高于 .yml/.yaml ; 不同目录下,按目录优先级覆盖(例如根目录的配置覆盖类路径的配置)

(3)复杂数据结构:.properties文件对于列表需要逗号隔开,对象之间需要手动拆分;yml和ymal文件对于多个对象只需要换行,对于列表只需要换行加短横线,操作更加方便;

示例:application.properties

  1. # 普通字符串  
  2. app.name=DemoApp  
  3.   
  4. # 列表  
  5. app.servers=192.168.1.1,192.168.1.2,192.168.1.3  
  6.   
  7. # 嵌套对象  
  8. app.datasource.url=jdbc:mysql://localhost:3306/test  
  9. app.datasource.username=root  
  10. app.datasource.password=123456  

示例:application.yml

  1. app:  
  2.   name: DemoApp  
  3.   servers:  
  4.     - 192.168.1.1  
  5.     - 192.168.1.2  
  6.     - 192.168.1.3  
  7.   
  8.   datasource:  
  9.     url: jdbc:mysql://localhost:3306/test  
  10.     username: root  
  11.     password: 123456  

需要全套面试笔记及答案【扫一扫】                         即可免费获取**

23.能说一下SpringBoot吗?

SpringBoot是一个基于Spring框架的开源工具,旨在简化Java应用(尤其是Web应用)的初始搭建和开发过程。通过“约定优于配置”的理念,大幅度减少传统Spring开发中的复杂配置,让开发者能快速构建独立运行、生产级的应用。

说明:传统的Spring需要大量的XML或@Bean配置,而Spring Boot内置了一套默认的约定,如果没有提供配置,Spring Boot就会按照“约定”的默认逻辑工作,只有当你的需求不同于默认约定时,才需要额外配置。比如,在pom.xml引入spring-boot-starter-data-jpa依赖,SpringBoot自动创建DataSource的Bean,默认使用application.yml中的配置。

它的特性如下:

(1)[自动配置]:SpringBoot根据@EnableAutoConfiguration注解开启自动配置机制,扫描AutoConfiguration.imports文件根据条件动态加载预定义的配置类;传统的Spring需要手动编写配置类,显示定义dataSource等一些Bean。

(2)起步依赖:通过预定义集成的依赖包(一系列Starter),自动引入所有相关依赖包,避免版本冲突;传统的Spring需要手动指定每个依赖及其兼容版本;

(3)内嵌服务器:添加spring-boot-starter-web依赖后,就会内嵌Tomcat、SpringMVC服务器,应用可直接打包为可执行JAR包,无需额外部署到web服务器;传统的Spring需要打包为WAR包,部署到独立的Tomcat服务器;

(4)生产就绪功能:SpringBoot提供坚控和管理端点(如健康检查、性能指标),方便运维;传统的Spring需要自行配置健康检查、指标收集等;

(5)简化配置:约定优于配置原则,默认提供合理的配置参数(如端口号、数据库连接池参数)和创建默认配置类,可通过properties文件或yml文件做修改覆盖;

24. 说一下Mybatis的缓存机制?

       Mybatis的缓存机制分为一级缓存(本地缓存)和二级缓存(全局缓存),通过缓存减少数据库查询次数来提升性能;

(1)一级缓存仅在同一个SqlSession中生效(默认开启),生命周期与Sqlsession绑定,会话结束或执行写操作时、事务提交回滚缓存都会失效;

(2)二级缓存在同一个Mapper中生效,多个SqlSession共享缓存(需手动开启,在对应XML 文件中添加  标签),生命周期跟SqlsessionFactory一致

       开启二级缓存后,查询时会先查一级缓存,未命中则查二级缓存,如果二级缓存也未命中就访问数据库,并将结果存入一级和二级缓存;需要注解一级和二级缓存都可能导致脏读,频繁修改的数据不适合开启二级缓存。

25. Mybatis和Hibernate有什么区别?

(1)对象关系映射:Hibernate完全将数据库表映射为Java对象,开发者几乎无需手动编写SQL,他自动生成SQL;Mybatis中需要手动编写SQL,通过XML或注解将SQL映射到Java方法中;

(2)SQL控制权:Hibernate自动生成SQL,开发者无法直接干预,可能会生成冗余SQL,性能较差;Mybatis中开发者完全控制SQL,需要自行维护SQL;

(3)性能:Hibernate内置强大的一级缓存和二级缓存减少数据库访问,适合读多写少场景;Mybatis手动编写高效SQL,避免不必要的查询,适合高性能场景;

Hibernate 配置(hibernate.cfg.xml)

  1.         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"  
  2.         
  3.   
  4.       
  5.         <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver  
  6.         <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/testdb  
  7.         <property name="hibernate.connection.username">root  
  8.         <property name="hibernate.connection.password">root  
  9.         <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect  
  10.         <property name="hibernate.hbm2ddl.auto">update   
  11.         <mapping class="User"/>  
  12.       
  13.   

保存数据的代码

  1. public class HibernateTest {  
  2.     public static void main(String[] args) {  
  3.         // 加载配置文件  
  4.         Configuration cfg = new Configuration().configure();  
  5.         SessionFactory sessionFactory = cfg.buildSessionFactory();  
  6.   
  7.         // 打开会话  
  8.         Session session = sessionFactory.openSession();  
  9.         session.beginTransaction();  
  10.   
  11.         // 创建对象  
  12.         User user = new User();  
  13.         user.setId(1);  
  14.         user.setUsername("admin");  
  15.         user.setPassword("123456");  
  16.   
  17.         // 保存对象  
  18.         session.save(user);  
  19.   
  20.         // 提交事务  
  21.         session.getTransaction().commit();  
  22.   
  23.         // 关闭会话  
  24.         session.close();  
  25.         sessionFactory.close();  
  26.     }  
  27. }  

26.能说一下全局异常捕获嘛?

说明:受检查异常和运行时异常

1.受检查异常

在编译阶段,Javac编译器会强制检查方法中是否显示地处理这些异常,如果没有使用try-catch捕获或没有通过throws向上抛出,编译就会报错。这种异常一般是外部资源或环境问题引起的,这样做的目的是提醒开发者写代码时考虑这些异常情况。

  1. public void readFile(String path) throws IOException {  
  2.     FileReader fr = new FileReader(path);  // 可能抛出 IOException  
  3.     BufferedReader br = new BufferedReader(fr);  
  4.     String line = br.readLine();  
  5. }  

如果不写 throws IOException 或 try-catch,编译就过不了。

受检查异常常见例子:

IOException:文件不存在、读写失败

SQLException:数据库连接失败、SQL 执行错误

ClassNotFoundException:类加载失败

InterruptedException:线程被中断

2.运行时异常

继承了RuntimeException,在运行阶段才会被发现,编译器不会强制要求处理,通常是编程错误或逻辑缺陷导致的。

运行时异常常见例子:

NullPointerException(空指针)

ArrayIndexOutOfBoundsException(数组越界)

ArithmeticException(除以 0)

ClassCastException(类型转换错误)

如何实现全局异常捕获,方法如下:

首先使用@ControllerAdvice注解将类注册为一个全局异常处理器组件,作用范围是所有的控制层;在SpringMVC的请求处理链中有一个关键接口HandleExceptionResolver(处理异常解析器),所有实现了该接口的类在出现异常时会依次调用该接口,将异常转化为响应;

  1. public interface HandlerExceptionResolver {  
  2.     ModelAndView resolveException(HttpServletRequest request,  
  3.                                   HttpServletResponse response,  
  4.                                   Object handler,  
  5.                                   Exception ex);  
  6. }  

步骤如下:1、控制层抛出异常;2、Spring捕获异常后,调用HandlerExceptionResolverComposite(处理异常解析器组合);3、遍历所有注册的HandleExceptionResolver实例(例如ExceptionHandleExceptionResolver);4、ExceptionHandleExceptionResolver查找对应的@ExceptionHandler方法;5、匹配成功后执行该方法,返回结果;

  1. import org.springframework.web.bind.annotation.ExceptionHandler;  
  2. import org.springframework.web.bind.annotation.RestControllerAdvice;
  3. import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;  
  4. import org.springframework.http.converter.HttpMessageNotReadableException;  
  5.   
  6. @RestControllerAdvice  // 等价于 @ControllerAdvice + @ResponseBody  
  7. public class GlobalExceptionHandler {  
  8.   
  9.     /** 
  10.      * 处理自定义业务异常 
  11.      */  
  12.     @ExceptionHandler(BusinessException.class)  
  13.     public Result<?> handleBusinessException(BusinessException ex) {
  14.         return Result.fail("业务异常:" + ex.getMessage());  
  15.     }  
  16.   
  17.     /** 
  18.      * 参数类型不匹配 
  19.      */  
  20.     @ExceptionHandler(MethodArgumentTypeMismatchException.class)  
  21.     public Result<?> handleTypeMismatch(MethodArgumentTypeMismatchException ex) {  
  22.         return Result.fail("参数类型错误:" + ex.getMessage());  
  23.     }  
  24.   
  25.     /** 
  26.      * 请求体解析错误 
  27.      */  
  28.     @ExceptionHandler(HttpMessageNotReadableException.class)  
  29.     public Result<?> handleMessageNotReadable(HttpMessageNotReadableException ex) {  
  30.         return Result.fail("请求参数格式错误");  
  31.     }  
  32.   
  33.     /** 
  34.      * 捕获所有其他异常 
  35.      */  
  36.     @ExceptionHandler(Exception.class)  
  37.     public Result<?> handleOtherException(Exception ex) {  
  38.         ex.printStackTrace();  // 可记录日志  
  39.         return Result.fail("服务器内部错误:" + ex.getMessage());  
  40.     }  
  41. }
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com