2024年java面试准备--spring篇续集(二)

简介: 2024年java面试准备--spring篇续集(二)

2024年java面试准备--spring篇续集(一)https://developer.aliyun.com/article/1393093

面试问题:

1.如果一个接口有多个实现类,在springboot中如何调用不同实现类中的方法

public interface Animal {
    //动物的叫声
   public void call();
    //动物吃的东西
   public void eat();
}
实现类1
@Service("dogImpl")
public class Dog implements Animal {
  
    @Override
    public void call() {
        System.out.println("汪汪汪......");
    }
    @Override
    public void eat() {
        System.out.println("骨头");
    }
}
实现类2
@Service("catImpl")
public class Cat implements Animal {
  
    @Override
    public void call() {
        System.out.println("喵喵喵......");
    }
    @Override
    public void eat() {
        System.out.println("鱼");
    }
}

方法1 指明实现类的优先级

在写实现类的时候事先指明实现类的优先级,注入的时候就会使用优先级高的实现类。在调用的类中注入接口,默认使用的是Primary 标注的实现类的方法


@Service("dog")
@Primary
public class Dog implements Animal {
    .......
}

方法2 通过@Autowride和@Qualifier两个注解配合使用

在调用处使用这两个注解

@Autowired
@Qualifier("dog")
private Animal animal;    //正常启动

注:注解@Qualifier内的值是实现类的默认名

方法3 使用@Resource注解,默认类名区分

在调用处使用此注解

@Resource(name = "dog")
private Animal animal;     //正常启动

注:注解@Qualifier内的值是实现类中@Service指定的名字

2.拦截器的配置

  1. 实现HandlerInterceptor接口的拦截器
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
        // 在请求处理之前进行拦截处理
        return true; // 返回true表示继续执行请求处理,返回false表示中断请求处理
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {
        // 在请求处理之后进行拦截处理
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) throws Exception {
        // 在请求处理完成之后进行拦截处理
    }
}
@Configuration
public class SpringMvcSupport extends webMvcConfigurationSupport {
    @Autowired
    private MyInterceptor myInterceptor;
    @verride
    protected void addInterceptors( InterceptorRegistry registry){          
        registry.addInterceptor(projectInterceptor).addPathPatterns("/路径");
    }
}
  1. 使用注解的拦截器
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
        // 判断请求方法是否有MyAnnotation注解
        if (handler instanceof HandlerMethod) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        if (method.isAnnotationPresent(MyAnnotation.class)) {
        // 在请求处理之前进行拦截处理
        return true; // 返回true表示继续执行请求处理,返回false表示中断请求处理
    }
}
    return true;
}
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {
        // 在请求处理之后进行拦截处理
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) throws Exception {
        // 在请求处理完成之后进行拦截处理
    }
}
@Controller
public class MyController {
    @RequestMapping("/test")
    @MyAnnotation
    public String test() {
        // 处理请求
        return "test";
    }
}


3.分页功能实现

@Test
void testGetPage(){
    IPage page = new Page( "current": 1,"size": 5);
    bookDao.selectPage(page, "queryWrapper": null);
    page.getCurrent();//获取当前页
    page.getSize();//获取每页数量
    page.getTotal();//获取总共数据条数
    page.getPages();//获取一共多少页
    page.getRecords();//获取的数据
}
//配置拦截器实现分页
@Configuration
public class MPConfig{
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor( ));
        return interceptor;
    }
}

4.如何定义一个全局异常处理类?

想要定义一个全局异常处理类的话,我们需要在这个类上添加@ContaollerAdvice注解,然后定义一些用于捕捉不同异常类型的方法,在这些方法上添加@ExceptionHandler(value = 异常类型.class)和@ResponseBody注解,方法参数是HttpServletRequest和异常类型,然后将异常消息进行处理。

如果我们需要自定义异常的话,就写一个自定义异常类,该类需要继承一个异常接口,类属性包括final类型的连续id、错误码、错误信息,再根据需求写构造方法;

5.说出Spring或者SpringMVC中常用的5个注解

@Component 基本注解,标识一个受Spring管理的组件 @Controller 标识为一个表现层的组件 @Service 标识为一个业务层的组件 @Repository 标识为一个持久层的组件 @Autowired 自动装配 @Qualifier("") 具体指定要装配的组件的id值 @RequestMapping() 完成请求映射 @PathVariable 映射请求URL中占位符到请求处理方法的形参

6.简述SpringMVC中如何返回JSON数据

Step1:在项目中加入json转换的依赖,例如jackson,fastjson,gson等

Step2:在请求处理方法中将返回值改为具体返回的数据的类型, 例如数据的集合类List等

Step3:在请求处理方法上使用@ResponseBody注解

7.Spring循环依赖问题

常见问法

请解释一下spring中的三级缓存

三级缓存分别是什么?三个Map有什么异同?

什么是循环依赖?请你谈谈?看过spring源码吗?

如何检测是否存在循环依赖?实际开发中见过循环依赖的异常吗?

多例的情况下,循环依赖问题为什么无法解决?

什么是循环依赖?

循环依赖就是在创建 A 实例的时候里面包含着 B 属性实例,所以这个时候就需要去创建 B 实例,而创 建 B 实例过程中也包含着 A 实例。 这样 A 实例还在创建的过程当中,所以就导致 A 和 B 实例都创建不出来。

image.png

spring通过三级缓存来解决循环依赖:

一级缓存:缓存经过完整的生命周期的Bean

二级缓存 :缓存未经过完整的生命周期的Bean

三级缓存:缓存的是ObjectFactory,其中存储了一个生成代理类的拉姆达表达式

我们在创建 A 的过程中,先将 A 放入三级缓存 ,这时要创建B,B要创建A就直接去三级缓存中查找,并且判断需不需要进行 AOP 处理,如果需要就执行拉姆达表达式得到代理对象,不需要就取出原始对象。然后将取出的对象放入二级缓存中,因为这个时候 A 还未经 过完整的生命周期所以不能放入一级缓存。这个时候其他需要依赖 A 对象的直接从二级缓存中去获取即可。当B创建完成,A 继续执行生命周期,当A完成了属性的注入后,就可以放入一级缓存了

问题1:为什么构造器注入属性无法解决循环依赖问题?

由于spring中的bean的创建过程为先实例化 再初始化(在进行对象实例化的过程中不必赋值)将实例化好的对象暴露出去,供其他对象调用,然而使用构造器注入,必须要使用构造器完成对象的初始化的操作,就会陷入死循环的状态

问题2:一级缓存能不能解决循环依赖问题? 不能

在三个级别的缓存中存储的对象是有区别的 一级缓存为完全实例化且初始化的对象 二级缓存实例化但未初始化对象 如果只有一级缓存,如果是并发操作下,就有可能取到实例化但未初始化的对象,就会出现问题

问题3:二级缓存能不能解决循环依赖问题?

理论上二级缓存可以解决循环依赖问题,但是需要注意,为什么需要在三级缓存中存储匿名内部类(ObjectFactory),原因在于 需要创建代理对象 eg:现有A类,需要生成代理对象 A是否需要进行实例化(需要) 在三级缓存中存放的是生成具体对象的一个匿名内部类,该类可能是代理类也可能是普通的对象,而使用三级缓存可以保证无论是否需要是代理对象,都可以保证使用的是同一个对象,而不会出现,一会儿使用普通bean 一会儿使用代理类

相关文章
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
92 2
|
2天前
|
Java Spring
Java Spring Boot监听事件和处理事件
通过上述步骤,我们可以在Java Spring Boot应用中实现事件的发布和监听。事件驱动模型可以帮助我们实现组件间的松耦合,提升系统的可维护性和可扩展性。无论是处理业务逻辑还是系统事件,Spring Boot的事件机制都提供了强大的支持和灵活性。希望本文能为您的开发工作提供实用的指导和帮助。
36 15
|
1月前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
61 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
2月前
|
Java 程序员
Java社招面试题:& 和 && 的区别,HR的套路险些让我翻车!
小米,29岁程序员,分享了一次面试经历,详细解析了Java中&和&&的区别及应用场景,展示了扎实的基础知识和良好的应变能力,最终成功获得Offer。
87 14
|
2月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
1月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
57 2
|
1月前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
2月前
|
Java 编译器 程序员
Java面试高频题:用最优解法算出2乘以8!
本文探讨了面试中一个看似简单的数学问题——如何高效计算2×8。从直接使用乘法、位运算优化、编译器优化、加法实现到大整数场景下的处理,全面解析了不同方法的原理和适用场景,帮助读者深入理解计算效率优化的重要性。
39 6
|
2月前
|
Java 数据库连接 API
Spring 框架的介绍(Java EE 学习笔记02)
Spring是一个由Rod Johnson开发的轻量级Java SE/EE一站式开源框架,旨在解决Java EE应用中的多种问题。它采用非侵入式设计,通过IoC和AOP技术简化了Java应用的开发流程,降低了组件间的耦合度,支持事务管理和多种框架的无缝集成,极大提升了开发效率和代码质量。Spring 5引入了响应式编程等新特性,进一步增强了框架的功能性和灵活性。
55 0
|
2天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
34 17

热门文章

最新文章