中间件&Linux

一、Spring&SpringBoot

1.IoC、依赖注入、@Autowired&@Resource

①什么是IoC?

IoC控制反转是一种思想,即将对象创建和管理的权力交给IoC容器,用户在需要使用对象时不需要使用构造器自行创建对象,直接通过依赖注入获取创建好的实例对象即可;
 
 

②说一说Spring IoC实现的原理

IoC/DI的实现可以主要分成两个阶段,第一阶段完成容器的创建和Bean的加载,第二阶段完成Bean的初始化;
在第一阶段中,
  首先创建一个工厂接口BeanFctory的实现类的实例对象作为容器,这个容器中包含一个以Bean id为key、以BeanDefinition为value的哈希表,叫beanDefinitionMap,用于存放当前容器中注册的各个Bean的各项属性,还有一个按注册顺序存储Bean id的ArrayList,叫beanDefinitionNames;
  然后读取并解析xml配置文件中各个Bean的信息,以BeanDefinition的形式和Bean id 一起注册到容器中的beanDefinitionNames和beanDefinitionMap中;
 
在第二阶段中,主要完成对单例、非懒加载的Bean的初始化,具体来说,
  在容器中还有一个以Bean id 为key、以Bean实例对象为value的哈希表,叫singletonObjects,这个哈希表专门用于存放单例对象,在进行Bean初始化时会先查询这个哈希表,
  若对象存在则直接返回,
  若不存在则通过反射获取运行时类的构造器,然后使用构造器创建对象并与其id一起存入singletonObjects中;
  后面使用geyBean()方法获取实例对象时,就可以直接从singletonObjects中获取;
 至此就完成了依赖注入的过程;
 
 

③依赖注入的三种方式

属性注入:最常用,但可能会出现空指针异常;
setter方法注入:不常用,因为setter方法可能会被多次调用,注入对象有被修改的风险;
构造器注入:可以注入final修饰的对象(前两种注入方式都不可以),可以确保注入对象不会被修改;
 
 

④@Autowired和@Resource的区别

Ⅰ. @Autowired来自Spring框架,@Resource来自JDK;
Ⅱ. @Autowired支持属性、setter方法和构造器注入,@Resource只支持属性、setter方法注入;
Ⅲ. @Autowired默认采用byType按照类型的方式查找Bean,@Resource默认采用byName按照名称的方式查找Bean;
Ⅳ. 对于有多个实现类的接口的依赖注入,@Autowired需要通过@Qualifier注解的value属性来指定名称,@Resource可以直接通过其name属性来指定名称;

 

 

2.AOP、动态代理

①什么是AOP?

AOP面向切面编程就是将那些与业务代码无关,但却被业务模块共同调用的逻辑比如日志管理、权限控制等封装起来,
这样做的好处是可以减少系统的重复代码,降低模块间的耦合度;
Spring AOP是基于动态代理实现的,
  对于有接口的对象,可以使用JDK 动态代理;        
  对于没有接口的对象,可以使用Cglib动态代理;
 
 

②JDK 动态代理和 CGLIB 动态代理对比

Ⅰ.实现原理:
JDK动态代理是jdk原生的实现方式,基于反射机制实现,在运行时通过实现目标接口的方式来代理目标类,因此要求目标类必须实现一个或多个接口,
而CGLIB动态代理是第三方CGLIB库提供的实现方式,在运行时通过继承目标类来代理目标类,因此要求目标类不能是被final修饰的类,或是有被final修饰的方法;
Ⅱ.性能表现:
JDK动态代理因为要实现目标类的接口,所以性能相对较低;
 
 

③SpringAOP实践

参考文章:https://blog.csdn.net/qq_40454136/article/details/126098759

首先通过@Aspect+@Component标注切面类,

然后通过@Pointcut:定义一个切面位置,通常是某个方法;

之后就可以通过@Before:前置通知、@After:后置通知、@Around:环绕通知等来定义切面前后要执行的操作;

(也可以不用@Pointcut定义切面位置,直接使用@Around+@Annotation来对某个自定义注解进行环切)

 

 

④AOP思想的体现2——HandlerInterceptor(同时也是Spring MVC的应用)

参考文章:https://blog.csdn.net/qq_38586496/article/details/111590660

通过实现HandlerInterceptor接口自定义拦截器,并在实现了WebMvcConfigurer接口的控制类中配置拦截器应用的url和执行次序,即可对指定url的执行进行拦截,在其执行前后进行特定操作;

HandlerInterceptor中有三个方法:

  preHandle:在执行Controller方法前执行,只有当返回true时才会继续往下执行,返回false就不会继续执行Controller方法了——可以在此方法中进行权限校验等操作;

  postHandle:在执行Controller方法中的逻辑,return视图之前执行——可以在此方法中添加对视图的操作

  afterCompletion:在Controller方法return视图之后执行——

原理:在DispatcherServlet的doDispatch方法中,会获取拦截器链,并在执行Controller方法前后,执行每个拦截器中的对应方法;

 

 

⑤AOP思想的体现3——Filter

参考文章:https://blog.csdn.net/ShuSheng0007/article/details/129330360

通过实现Filter接口及其中的doFilter方法自定义过滤器,并加上@Component注解,即可对所有url进行拦截,并执行doFilter方法中的操作;

(还可以通过@Order来规定多个过滤器的执行顺序,越小越先执行,还可以在配置类中创建FilterRegistrationBean<自定义过滤器>的Bean,并在其中对过滤器的拦截范围进行配置)

原理:在HttpRequest到达servlet之前进行拦截,在HttpResponse到达客户端之前进行拦截;

 

和HandlerInterceptor的区别:

Filter过滤器是在Spring MVC外层对请求进行拦截,可以拦截所有请求,而HandlerInterceptor是在Spring MVC内部对请求进行拦截(有图见参考文章),只能拦截通过DispatcherServlet进行处理的请求

正因如此,Filter更具通用性,粒度更粗,在Filter中只能拿到http请求相关的信息,拿不到应用层面的信息——常用于检查用户请求,过滤非法请求

而HandlerInterceptor则通用性差一些,但粒度更细,可以获取应用层面的信息,例如类目方法名等——常用于

最后在使用层面,Filter在doFilter一个方法中定义预处理和后处理逻辑,在方法中通过filterChain.doFilter进行分隔,而HandlerInterceptor将预处理和后处理逻辑拆分成两个方法,即preHandle、postHandle方法;

 

 

⑥Filter过滤器和Interceptor拦截器使用到了哪些设计模式

AOP体现了代理模式,

拦截器链、过滤器链体现了责任链模式;

 

 

 

 

 

3.Spring MVC

①什么是Spring MVC?

MVC是模型model、视图view、控制器controller的简写,其核心思想是通过将数据、显示、业务逻辑分离开来组织代码;
Spring MVC中通常将后端项目分为controller控制层,负责返回数据给前端页面、service层处理业务、dao层对数据库进行操作、entity层实体类;
 
 

②Spring MVC的原理?

1. 首先,当客户端发出请求时,DispatcherServlet会进行拦截,并根据请求信息调用HandlerMapping;
2. 然后HandlerMapping就会根据URL去匹配能够处理的Handler(也就是我们常写的Controller),并将请求涉及的拦截器和Handler一起封装;
3. 之后DispatcherServlet会调用HandlerAdapter,后者进一步调用Handler来处理请求;
4. 请求处理完毕后,Handler返回逻辑视图,DispatcherServlet再调用ViewResolver来解析并渲染真正的视图,最后响应客户端;
 
 

③Spring MVC有哪些核心组件?

DispatcherServlet:最核心的组件,负责接收和分发请求,调用其它各组件,最后将响应返回给客户端;
HandlerMapping:负责根据URL匹配能够处理的Handler,并将请求涉及的拦截器和Handler封装到一起;
HandlerAdapter;负责适配和调用Handler;
Handler:也就是我们常写的Controller,负责处理请求,处理完毕后返回逻辑视图;
ViewResolver:负责将逻辑视图渲染成为真正的视图;

 

 

 

 

4.自动装配、Bean、SpringBoot的常用注解

①什么是SpringBoot的自动装配?原理是什么?

自动装配指的是在引入starter后,只需要通过少量注解和配置就可以使用第三方组件提供的功能了;
原理:
SpringBoot在启动时,会首先读取所有依赖的starter的META-INF目录下的spring.factories文件中的自动配置类;
然后根据每个配置类通过@Conditional注解设置的加载条件,有选择地加载那些满足条件的自动配置类到Bean到容器中;
这样 Spring Boot 启动完毕后,就已经完成了各种配置。

 

②SpringBoot的常用注解

@Component、@Repository、@Service、@Controller——将一个类声明为 Bean 的注解
@Bean——将方法返回的对象声明为 Bean;
@Autowired 、@Resource——依赖注入;
@RequestMapping,以及衍生的@GetMapping/@PostMapping/@PutMapping/@DeleteMapping——指定请求与处理方法之间的映射关系;
@PathVariable、@RequestParam——获取请求中的参数;
@RequestBody——将json/xml格式的请求参数转换为java对象;
@ResponseBody——将方法的返回值直接或转换为json格式再响应给浏览器;
@RestController——作用相当于@ResponseBody+@Controller;
@Value、@ConfigurationProperties——读取配置信息;
@SpringBootApplication:标识该类为Spring Boot应用程序的入口点,包含了多个注解,如@Configuration、@EnableAutoConfiguration和@ComponentScan;
 
 
 
 

5.Bean

①Bean 是线程安全的吗?

这取决于其作用域和是否有状态:
在prototype 作用域下,每次获取都会创建一个新的 bean 实例,不存在资源竞争问题,因此线程安全。
在singleton 作用域下,IoC 容器中只有唯一的 bean 实例,如果 Bean 没有状态(就是不包含可变的成员变量),那也是线程安全的,反之如果有状态,那就是线程不安全的,
解决办法有:
           将可变成员变量保存在ThreadLocal中,将变量变为每个线程私有;
           或是通过synchronized等锁来实现线程同步;
 
 

Bean的生命周期

参考文章;https://zhuanlan.zhihu.com/p/638361741

首先,在Spring应用启动时,IOC容器就会读取配置文件,并通过反射机制为每个非懒加载的Bean创建实例和属性赋值;

之后,用户可以通过各种方法进行依赖注入来获得Bean的实例对象,并进行使用;

最后当应用程序关闭时,IOC容器也会执行销毁方法来销毁Bean,销毁方法中还可以添加一些资源释放的操作,例如关闭连接、清理缓存等;

 

③Bean的属性有哪些

class:类的全限定名;

name:Bean的名称;

scope:Bean的作用域,常用的就是singleton单例、prototype每次都重新创建,默认为单例;

lazy-initialization mode:是否懒加载,指是否在创建容器时就加载Bean到容器中,还是等使用到的时候再加载;

 

④Bean的作用域

Spring 中 Bean 的作用域通常有下面几种:
  • singleton : IoC 容器中只有唯一的 bean 实例,也是Spring 中默认的作用域;
  • prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续 getBean() 两次,得到的是不同的 Bean 实例;

 

 

6.循环依赖、全局异常处理

①什么是循环依赖?如何解决?

循环依赖就是两个或多个Bean之间互相持有对方,形成了一个环,比如A中包含B,B中包含C,C中又包含了A;

可以通过采用构造器注入的方式进行依赖注入,这样如果存在循环依赖在启动项目时就会报错,对发生循环依赖的类进行修改即可;

 

全局异常处理怎么做?

首先创建一个用@ControllerService标注的全局异常处理类,可以在参数值指定拦截范围(比如某个包)

然后在其中创建各种用@ExceptionHandler标注的处理异常的方法,并在参数中指定要处理的异常类型;

这样Spring就会给所有Controller添加异常处理的逻辑,当发生异常时就会去异常处理类下去找可以处理异常的方法,并取其中匹配度最高的; 

 

 

 

二、RabbitMQ

1.消息队列、优缺点、常用消息队列对比

① 什么是消息队列?

消息队列就是使用一个缓冲区队列来实现进程间异步通信的组件;
 

②消息队列的优点&缺点

优点:可以让不同系统之间的通信操作解耦,由消息队列异步完成,还可以通过适当积压消息来达到流量削峰的效果;
缺点:系统复杂性提高,需要考虑一致性问题、如何保证消息的可靠性传输以及不被重复消费,可用性降低,消息队列服务器的宕机也会影响业务正常进行;
 

③常见消息队列的对比,为什么选用RabbitMQ?

对于目前常用的三种消息队列,
从吞吐量来看:RabbitMQ吞吐量为万级,RocketMQ和Kafka为十万和百万级;
从可用性来看:都可以实现高可用,RabbitMQ基于主从架构,RocketMQ和Kafka基于分布式架构;
从时效性来看:RabbitMQ基于Erlang开发,所以并发能力很强,延时很低达到微秒级,RocketMQ和Kafka都是毫秒级;
从功能支持来看:RabbitMQ和RocketMQ都比较完备,Kafka功能较为简单,主要支持简单的MQ功能;
从消息丢失来看:RabbitMQ丢失的可能性非常低,RocketMQ和Kafka理论上不可能丢失消息;
我选用RabbitMQ是因为我主要使用MQ来异步更新ES中的小说副本数据,修改小说的操作只能由作者完成,因此对吞吐量要求不高,但希望消息的时效性尽可能高,因此选用RabbitMQ;

 

 

三、Linux

1.常用的shell命令有哪些?

①目录操作的命令:

ls查看当前目录文件列表,pwd显示当前目录,cd进入目录,mkdir创建目录等;

②文件编辑相关命令:

vi/vim编辑文件,rm删除文件,cp复制文件,touch创建文件;

③系统管理相关命令:

ifconfig查看ip地址,ping测试连通性,kill杀死进程,ps查看进程状态;

 

查看当前进程使用到的端口:

netstat -tunlp | grep <进程ID>

  1. netstatnetstat 是一个用于显示网络连接、路由表和网络接口等网络系统状态的命令。在这个命令中,-tunlp 参数用于显示当前系统的网络连接情况,具体含义如下:
    • -t:显示 TCP 协议的连接。
    • -u:显示 UDP 协议的连接。
    • -n:直接使用 IP 地址,而不进行域名解析。
    • -l:仅显示监听状态的连接。
    • -p:显示正在使用的进程/程序的 PID 和名称。
  2. grepgrep 是一个用于在文本中搜索指定模式的命令。在这个命令中,grep <进程ID> 用于过滤出包含特定进程ID的行。




ls命令:显示当前目录文件列表,-a显示所有文件包括隐藏文件;
cd命令:切换目录,cd dir切换到dir目录,cd /切换到根目录,cd ...切换到上一级目录;
cp命令:copy文件,cp source target将文件source复制为target,cp /root/source将/root目录下的文件source复制到当前目录;
rm命令:删除文件或目录,rm file删除某个文件, rm -rf dir删除当前目录下叫dir的整个目录;
pwd命令:获取当前地址;
mkdir:创建文件夹;

 


2. Linux有哪些IO机制?

阻塞IO和非阻塞IO?——阻塞和非阻塞IO强调准备数据阶段线程是否会被阻塞

阻塞IO:当线程发起read调用,如果内核没有准备好数据,线程会进入阻塞状态,直至内核准备好数据,再根据同步还是异步来决定由谁将数据拷贝到用户态;
非阻塞IO:当线程发起read调用,如果内核没有准备好数据,线程不会进入阻塞状态,而是会去执行其他操作,同时以轮询的方式向内核发起read调用,待内核准备好数据,再根据同步还是异步来决定由谁将数据拷贝到用户态;
 
同步和异步IO?——同步和异步强调拷贝数据的操作是需要线程自己执行,还是内核调用其他线程异步执行
同步IO:指将数据从内核态拷贝到用户态这一操作,需要当前线程来执行;
异步IO:指将数据从内核态拷贝到用户态这一操作会由内核调用其他线程异步执行;
(同步:发出一个调用之后,在没有得到结果之前, 该调用就不可以返回,一直等待。异步:调用在发出之后,不用等待返回结果,该调用直接返回;)
 
还有IO多路复用——见Redis为什么这么快;

 

 

 

四、设计模式

1.代理模式、静态代理&动态代理

①什么是代理模式?

就是通过添加代理对象来控制对目标对象的访问,进而实现一些额外的功能,例如访问计数、日志记录、权限控制等;
代理模式的实现方式主要有静态代理和动态代理;
 
 

②静态代理和动态代理的区别

静态代理在编译期间就确定了代理对象,需要为每一个代理对象创建一个代理类;
动态代理在运行期间根据需要动态生成代理对象,不需要手动创建代理类;
(Spring AOP功能就用到了动态代理,例如创建HandlerInterceptor的实现类来对访问URL进行拦截,实现权限控制)
 
 

③动态代理中JDK 动态代理和 CGLIB 动态代理对比

Ⅰ.实现原理:
JDK动态代理是jdk原生的实现方式,基于反射机制实现,在运行时通过实现目标接口的方式来代理目标类,因此要求目标类必须实现一个或多个接口,
而CGLIB动态代理是第三方CGLIB库提供的实现方式,在运行时通过继承目标类来代理目标类,因此要求目标类不能是被final修饰的类,或是有被final修饰的方法;
Ⅱ.性能表现:
JDK动态代理因为要实现目标类的接口,所以性能相对较低;
 
 

④手动演示一下静态代理、两种动态代理的代码

 
 
 
 

2.单例模式

①什么是单例模式?

就是保证一个类仅有一个实例,并提供一个全局访问点,
例如线程池、数据库连接池都是单例模式;
其优点在于单个实例可以减少内存开销、避免对资源的多重占用,设置全局访问点,严格控制访问;
 

②单例模式需要满足的条件

Ⅰ. 私有构造器;
Ⅱ. 线程安全(保证多线程环境下的单例);
Ⅲ. 延迟加载(不要在编译阶段就初始化,会浪费内存空间);
Ⅳ. 防止反射攻击(要求通过反射也无法创建出多个实例);
Ⅴ. 序列化和反序列化也不能破坏单例性质;
 

③推荐使用的两种单例模式实现方式

Ⅰ. 改进后可以防范反射攻击、序列化和反序列化的的内部类式单例;
静态内部类的静态常量,保证了线程安全和延迟加载;
通过给私有构造器添加一个判断条件:若静态内部类的INSTANCE不为空,直接抛出异常,避免利用反射通过私有构造器创建非法实例;
通过在单例类中添加一个readResolve()方法来解决,保证反序列化之后的对象和序列化前的对象是同一个对象;
 
Ⅱ. 枚举类式单例;
枚举类型天生具有线程安全性,
序列化和反序列化不可破坏单例(因为枚举类型通过类名和常量名确定枚举常量,而这里常量名及其对应的枚举常量是唯一的,故不会创建新的对象),
反射不可破坏单例(无法通过反射来创建枚举类型,会直接抛出异常IllegalArgumentException:""Cannot reflectively create enum objects""),
且相比改进的内部类式单例,代码优雅;
缺点在于不支持延迟加载,加载时就会创建对象,不过影响不大,是非常推荐的一种单例式写法;
 
总结:
当需要单例时,只要不是特别重的对象,都建议使用枚举类式实现(因为加载时就会创建对象,对象太重会浪费大量内存空间),其他情况下使用改进的内部类式单例;

 

 

3.工厂模式、适配器模式、观察者模式、策略模式

①什么是工厂模式?

将对象的创建和使用分离,客户端只需知道产品的接口,无需关心具体实现;
例如MyBatis 中的SqlSessionFactory,专用于创建SqlSession,还有Spring 使用工厂模式通过 BeanFactory 和 ApplicationContext 来创建和管理 bean 实例,将对象的创建和获取解耦;
 
 

②什么是适配器模式?代理模式和适配器模式的区别?

通过添加适配器类,将一个类的接口转换成客户端所期望的另一个接口,来解决接口不匹配的问题;
Spring MVC 中的 HandlerAdapter 就是适配器模式的应用,用于将不同类型的处理器适配到框架中统一的处理器接口;

代理模式是通过添加代理对象,来控制对目标对象的访问,进而实现一些额外的功能,如访问计数、权限控制等,
适配器模式是通过添加适配器类,将一个类的接口转换成客户端所期望的另一个接口,来解决接口不匹配的问题,通常在对旧系统进行升级改造时用到;

 

③什么是观察者模式?

又叫发布者-订阅者模式,定义对象间的的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新;
使用场景:订阅通知场景,如消息队列、Redis的发布订阅功能;

 

④什么是策略模式?

指将一系列功能类型、对外接口都相同的算法单独封装起来作为一个个策略,通过一个上下文类来切换不同的策略,使得相同的接口在不同策略下可以实现不同的功能" 设计模式 设计模式分类 设计模式分类

 

⑤设计模式分类

设计模式按照设计目的可以分为三类:创建型模式、结构型模式、行为型模式,
分别对应了面向对象开发的三个问题:如何创建对象、如何组合对象、如何处理对象间的动态通信和职责分配;
其中创建型模式常见的有单例模式、工厂模式,
结构型模式常见的有代理模式、适配器模式,
行为模式常见的有策略模式、观察者模式;
 
 
 

4. Spring框架中用到了哪些设计模式?

①工厂模式:Spring通过BeanFactory、ApplicationContext创建bean对象就用到了工厂模式;

②单例模式:Spring中的Bean默认的作用域就是单例;

③代理模式:Spring AOP功能的实现就是通过动态代理来实现的;

④适配器模式:SpringMVC中DispatcherServlet通过调用HandlerAdapter来进一步调用各种Handler,HandlerAdapter就应用到了适配器模式;

⑤观察者模式:Spring的事件驱动模型就用到了观察者模式;

 

 

5.什么是责任链模式? 什么是责任链模式?

参考文章:https://blog.csdn.net/lishuzhen5678/article/details/125657672

用于将请求的发送者和接收者解耦,并按照一定顺序依次处理请求

 

 

spring中的Filter使用从Filter机制看设计模式(责任链模式,装饰器模式)

Spring中的过滤器filter,通过实现Filter接口中的init()、doFilter()、destroy()方法,在请求进入控制器之前、响应返回客户端之前进行预处理或者后处理。

过滤器常用于实现一些通用的功能,比如日志记录、权限验证等;

还有拦截器intercepter也是用到了责任链模式;

二者功能类似,区别主要在于Filter更加通用,可以用于任何Servlet容器中,而Interceptor更加专注于Spring MVC框架中的请求处理流程; 

 

 

五、场景题

1.订单秒杀怎么做的?

①超卖问题如何解决?

超卖问题本质上是因为在高并发场景下,多个线程同时判断还有剩余商品,于是都进行了减少商品库存操作,导致商品数量被减

了负数,也就是超卖;

解决问题的核心在于保证查询商品数量+减少商品库存操作的原子性,

对于单体应用,可以通过本地锁synchronized/ReentranLock等来实现;

对于分布式应用,可以通过Redis set NX实现的分布锁、Redission框架提供的分布式锁、或是zookeeper提供的分布锁来实现;

如果不直接对数据库进行操作,而是对缓存进行操作的话,也可以使用Lua脚本来保证查询商品数量+减少商品库存操作的原子性

之后再通过MQ异步地将数据更新到数据库中;

 

在一个分布式系统中,如何设置订单ID?保证订单ID单调递增?(瞎蒙了一个时间戳+一个自增数值)
+
那么如果在某天,系统中获取时间戳的部分出问题,无法获取正确的时间戳,怎么保证订单ID单调递增?" "在一个分布式系统中,如何设置订单ID?保证订单ID单调递增?(瞎蒙了一个时间戳+一个自增数值)




那么如果在某天,系统中获取时间戳的部分出问题,无法获取正确的时间戳,怎么保证订单ID单调递增?

posted @   Avava_Ava  阅读(8)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示