Java - 程序员面试笔记记录 & 实现 - Part5

7.1 Struts

优点:

1. MVC模式实现了表现与逻辑的分离,扩展性高。

2. 提供页面导航功能,通过配置文件建立整个系统各部分之间的联系。

3. 集成了一些常用处理功能。

缺点:

1. 仅面向 Web 应用程序开发

2. Action 非线程安全,资源需要考虑同步。

7.1 补充 - MVC

MVC模式的基本组成:

  1. Model(模型):代表数据和业务逻辑。它负责数据的存储、检索和逻辑处理,通常与数据库进行交互。

  2. View(视图):代表用户界面。它显示数据(通过Model提供)并收集用户的输入,但不处理业务逻辑。

  3. Controller(控制器):充当Model和View之间的中介。它接收用户的输入,调用Model来处理数据,然后选择合适的View来显示结果。

MVC2模式的特点:

  • 分离关注点:MVC2模式将数据、界面和逻辑分离,使得开发和维护更加清晰和简单。

  • 可维护性:由于关注点分离,修改任何一个部分(数据、界面或逻辑)时,其他部分不受影响,提高了代码的可维护性。

  • 可扩展性:MVC2模式使得扩展应用程序变得更加容易,可以独立地扩展Model、View或Controller。

  • 可测试性:分离的架构使得对各个组件进行单元测试和集成测试变得更加简单。

7.1 补充 - Servlet

Servlet是Java EE(Java Enterprise Edition)规范的一部分,它是一种运行在服务器端的Java Web应用程序组件,用于处理客户端请求并生成动态响应。Servlet提供了一种将Java代码与HTTP协议交互的方式,使得开发者可以创建能够处理Web请求的Web应用程序。

Servlet的主要特点:

  1. 平台无关性:Servlet可以运行在任何支持Java EE Web规范的服务器上,例如Apache Tomcat、JBoss、GlassFish等。

  2. 请求处理:Servlet可以处理不同类型的HTTP请求,如GET、POST、PUT、DELETE等。

  3. 生命周期管理:Servlet容器管理Servlet的生命周期,包括初始化、服务、销毁等。

  4. 可重用性:Servlet可以被设计为可重用的组件,可以在多个Web应用程序中使用。

  5. 集成性:Servlet可以与其他Java EE组件(如EJB)集成,实现复杂的企业级应用。

Servlet的生命周期:

  1. 实例化:Servlet容器创建Servlet实例。

  2. 初始化:容器调用init()方法初始化Servlet实例。

  3. 服务:容器调用service()方法来处理客户端请求。

  4. 销毁:容器在销毁Servlet实例前调用destroy()方法。

  5. 垃圾回收:Servlet实例被垃圾收集器回收。

Servlet的工作原理:

  1. 客户端发送HTTP请求到Servlet容器。

  2. Servlet容器创建对应的Servlet实例(如果尚未创建)。

  3. Servlet容器调用Servlet的service()方法,并将请求和响应对象作为参数传递。

  4. service()方法根据请求类型(如GET或POST)调用相应的doGet()doPost()方法。

  5. Servlet处理请求,生成响应,并通过响应对象发送回客户端。

7.3 Struts 响应用户请求的工作流程

Web 启动时,会加载并初始化 ActionServlet,ActionServlet 从 config中读取配置信息,存放到 ActionMappings 对象中。当 ActionServlet 收到客户请求时:

1. 检索和用户请求匹配的 ActionMapping 实例,不存在则返回路径无效。

2. ActionFrom 实例化,保存用户提交的表单。

3. 根据配置信息进行表单验证。

4. ActionServlet 根据 ActionMapping 包含的映射关系转发给 Action。

5. Action 的 execute 方法返回一个 ActionForward 对象,ActionServlet 再把客户请求转发给ActionForward 对象指向的 JSP组件。

6. ActionForward 对象指向的 JSP 组件生成动态页面,返回给客户。

7.10 补充 - Struts2 配置文件的加载顺序

后加载文件中的配置会将先加载的文件中的配置覆盖

1. default.properties 常量

2. struts -default.xml 配置了 bean,interceptor,result等

3. struts-plugin.xml 配置了插件

4. struts.xml 开发者定义的配置文件

5. strusts.properties 自定义常量

6. web.xml 

8.1 MyBatis

通过封装 JDBC 的操作来实现与数据库的交互。使开发者只需要关注 SQL 语句不需要再去编写驱动等操作。通过XML或者注解的方式配置和映射原生信息,内部通过JDBC执行SQL并根据配置信息把执行结果映射成 Java 对象返回。

8.2 MyBatis 分页

物理分页:数据库本身提供了分页的功能。

逻辑分页:通过游标实现的分页,但效率低下。

8.2 补充 - Mybatis 和 Hibernate 的不同

都是持久层框架,都通过SessionFactoryBuilder 由XML 配置文件生成 SessionFactory,然后由 SessionFactory 生成 Session,最后由 Session 来开启执行事务和语句。区别主要是 Hibernate 是全自动的,完全通过对象关系模型实现对数据库的操作。MyBatis 是半自动的,需要手动指定映射关系。

8.2 补充 - MyBatis 实现自定义插件

Executor:拦截内部执行器,负责调用 StatementHandler 操作数据库,并把结果通过 ResultSetHandler 进行自动映射,还处理了二级缓存操作。

StatementHandler:拦截SQL语法构建的处理。也实现了一级缓存。

ParamenterHandler:拦截参数处理。

ResultSetHandler:拦截结果集的处理。

8.2 补充 - 执行器

SimpleExecuter:每执行一次 update 或者 select 就开启一个 statement 对象,完成后就立刻关闭 Statement 对象。

ReuseExecutor:执行 update 或 select,以SQL作为 key 查找 Statement 对象,如果找到就直接使用,否则创建新的,使用完成后不会关闭,而是存放在 Map 中供下一次使用。

BatchExecutor:执行 update,通过 addBatch 把所有SQL都添加到批处理中。

9.1 Redis

Redis 是一个开源的,键值对型的数据存储系统。可以作为数据库,缓存,消息中间件。

9.2 Memcache 与 Redis 的区别

Memcache 是一个自由开源的、高性能、分布式内存对象缓存系统。

1. 支持的数据比较单一。

2. 数据保存在内存,一旦出现故障无法恢复数据。

3. 多线程操作。

9.4 Redis 分布式锁

1.  INCR 加锁:当 key 不存在,那么 key 初始化为 0,再执行 INCR操作进行加1,如果用户执行INCR后返回值大于1则说明这个锁正在被使用中并执行 decr命令还原。

2. setnx 加锁:使用 setnx 争抢锁,抢到后用 expire 给锁加一个过期时间。

3. 用set 加锁:相当于合成了 setnx 和 expire 两个语句。

9.6 Redis 持久化

1. RDB 持久化

在指定的时间间隔内将内存的数据写入磁盘,实际操作过程是创建一个子进程先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

2. AOF 持久化

以日志的形式记录服务器处理的写、删除操作,记录到 AOF文本文件。

9.6 - 补充 rewrite

AOF重写的过程是创建一个新的AOF文件,它包含恢复当前数据库状态所需的最小命令集合,而不会包含任何冗余命令。这通常使得新AOF文件的体积比旧文件小很多。

重写后AOF文件变小的原因包括:

  • 移除了已经超时的数据。
  • 删除了旧AOF文件中的无效命令。
  • 合并了多条写命令为一个更高效的命令,例如,将多次LPUSH操作合并为一条22。

为了确保子进程进行的操作与主进程最终结果的一致性,Redis采用了以下机制:

  1. 数据副本:在子进程开始执行持久化或重写操作之前,它会通过fork()系统调用复制主进程的内存数据副本。这个副本是主进程在fork()时刻的数据一致性快照。

  2. AOF重写缓存:在AOF重写过程中,主进程会维护一个AOF重写缓存。当主进程接收到写命令时,它会将这些命令不仅追加到现有的AOF文件中,同时也追加到AOF重写缓存中。

  3. 子进程完成:子进程完成AOF文件的重写后,它会通知主进程。此时,主进程会将AOF重写缓存中的内容追加到子进程生成的新AOF文件末尾。

  4. 替换旧AOF文件:主进程将新生成的AOF文件重命名,替换掉旧的AOF文件。这个过程是原子操作,确保了在任何时刻,客户端都能看到一个一致的AOF文件。

  5. RDB持久化与复制:在使用RDB持久化时,子进程创建的快照文件在完成后,会替换掉旧的RDB文件。同时,Redis的主从复制机制也会确保从节点拥有与主节点一致的数据副本。

  6. 无锁设计:Redis的持久化和重写操作不需要对数据加锁,因为子进程拥有数据的副本,并且主进程对数据的修改不会直接影响到子进程的数据副本。

  7. 写时复制(Copy-On-Write, COW):在fork()之后,如果操作系统支持写时复制,那么在父子进程共享的内存页上,任何写操作都会导致操作系统复制那一页的数据,然后对复制的数据进行修改。这确保了父子进程的数据隔离。

9.8 Redis 过期的删除策略

主节点的删除:

1. 定时删除

2. 惰性删除:每次获取 key 时检查是否过期。

3. 定期删除

从节点的删除:

主节点的 key 到期时,会在 AOF文件中增加一条 del 指令。AOF 文件同步到从节点后,从节点根据指令删除。会有短暂的不同步。

9.9 缓存穿透

大量请求未命中 redis 击穿到数据库。

1. 布隆过滤器:将查询参数都存到 bitmap 中,查询缓存前对参数进行验证。

2. 缓存空对象。

9.10 哨兵

Sentinel 用于监控 Redis 集群中状态

1. 监控:不断监控节点运作是否正常

2. 提醒:当节点出现问题进行通知

3. 自动故障迁移:将失效的 master的其中一个 slave 升级为新的 master。

9.10 - 补充 同步机制

Redis 会首先尝试进行增量同步,如不成功,则进行全量同步。

增量同步:主服务器每执行一个写命令就会向从服务器发送相同的写命令。

全量同步:一般发生在 slave 初始化阶段。对数据做全量同步。

10.1 Kafka 的消息传递模式

点对点模式:使用消息队列实现,一条消息只能被一个消费者消费,消费后删除。

发布订阅模式:数据持久化到 topic 中,同一条数据可以被多个消费者消费,被消费后不会立即删除。

10.2 组件

Producer:消息生产者。

Broker:一个 Kafka 节点就是一个 Broker ,多个 Broker 可以组成一个集群。

如果某个 topic 有 n 个 partition 而集群有 n 个 broker,每个 broker 会存储该 topic 的一个 partition.

Topic:消息类别。

Partition:topic 的物理分区。一个 topic 可以分为多个 partition,每个 partition 是一个有序的不可变的记录序列 。单一主体的分区有序,但无法保证主体内所有分区有序。

Consumer:消费者。

Consumer Group:每个 Consumer 都属于一个 Consumer Group,每个消息只能被 Consumer Group 的一个 Consumer 消费。

Replica:Partition 的副本。保证 Patition 高可用。

Zookeeper:通过 Zookeeper 存储集群的 meta 信息。

10.3 分区原则

1.指明 partition 的情况下直接作为 partition 的值。

2. 没有指明 partition 但是有 key,则将 key 的 hash值与 topic 的 partition 值取余来计算

3. 以上两个值都没有的情况则在第一次调用的时候随机生成一个整数,将这个值与 topic 可用的 partition 总数取余。

10.3 补充 - ACK机制

acks = 0: 不等待 broker 同步完成的确认就继续发送。

acks = 1: Leader 已经成功收到并确认后发送下一条,不等待 follow 的同步。

acks = -1:所有 Follow 都确认后才成功。

10.3 补充 - 消息有序性

Apache Kafka 是一个分布式流处理平台,它被设计为一个高吞吐量的发布-订阅消息系统。在 Kafka 中,消息以流的形式进行传输,并且可以保证在单个分区内的消息有序性。以下是 Kafka 保证消息有序性的几种方式:

  1. 分区内有序:Kafka 保证在单个分区(Partition)内消息是有序的。每个分区是一个有序的、不可变的消息序列,这个序列在 Kafka 中被称为一个日志。

  2. 消息顺序由分区键决定:当生产者发送消息时,可以通过指定分区键(Partition Key)来决定消息发送到哪个分区。如果所有具有相同键的消息都发送到同一个分区,那么这些消息将保持发送顺序。

  3. 单线程发送:在同一分区内,如果生产者以单线程方式发送消息,那么消息将严格按照发送顺序被写入日志。

  4. 消费者组:在 Kafka 中,消费者通过消费者组(Consumer Group)来消费消息。消费者组内的消费者会协调彼此,以确保每个分区只分配给组内的单个消费者。这样,对于每个分区,消息将按照偏移量(Offset)的顺序被消费。

  5. 偏移量管理:Kafka 为每个消息维护一个偏移量,这是消息在分区日志中的索引。消费者可以跟踪自己读取到的最后一个偏移量,并从该位置继续读取,以保证消息的顺序性。

  6. 幂等生产者:Kafka 提供了幂等生产者的特性,它确保消息在发送过程中不会因为生产者端的故障而重复发送,从而避免了消息的重复。

  7. 事务:Kafka 支持事务,可以确保一组消息要么全部成功发送,要么全部失败,这有助于保持消息的一致性和顺序性。

  8. 有序的消费者订阅:消费者在订阅主题时,可以指定只订阅主题的某些分区,这样消费者就可以控制它读取消息的顺序。

然而,需要注意的是,Kafka 不保证跨分区的消息有序性。如果一个主题有多个分区,并且生产者没有指定分区键,那么消息可能会被发送到不同的分区,这种情况下 Kafka 不能保证这些消息的全局顺序。

为了在 Kafka 中实现严格的全局消息顺序,可能需要使用单个分区,但这会限制系统的并行性和吞吐量。通常,在设计 Kafka 应用时,需要在消息顺序性和系统吞吐量之间做出权衡。

10.3 补充 - Zookeeper 的作用

Broker 在启动时都需要在 Zookerper 注册,由 Zookerper 统一协调管理。任何节点失败,可以通过 Zookerper 从之前的偏移量中恢复。Topic 的分区信息及与 Broker 的对应关系也是 Zookerper 在进行维护。

10.4 Kafka 提供的保证

1. 生产者向特定主体分区发送的信息的顺序相同。

2. 消费者实例按存储在日志中的顺序查看记录。

3. 能容忍最多 N -1 个服务器故障。

10.5 Message 格式

定长的 header + 变长的消息体。

1. 每个日志包含一个4字节整形(Message 长度)

2. 1个字节的 magic(版本协议号)

3. 1个字节的CRC 32值,用于校验。

4. N个字节的消息体。

10.6 RocketMQ VS Kafka

11.1 Spring

Spring 是 Java 开源框架,核心是 IOC( Inversion Of Control)和 AOP(面向切面编程)。

1. 面向接口编程,耦合性低。

2. 容器可以管理所有托管对象的生命周期和其依赖关系。

3. 面向切面编程,主要逻辑和次要逻辑分开。

4. 提供声明式的事务管理支持。

5. 集成其他系统方便。

6. 提供了一些 API 的封装。

11.1 补充 代理模式

代理模式(Proxy Pattern)是一种常用的软件设计模式,它为另一个对象提供一个代替或占位符的对象,以便控制对它的访问。代理模式在不直接访问实际对象的情况下,提供了对目标对象的间接访问。这种模式涉及的角色通常包括:

  1. 主题(Subject):定义了由代理和实际对象实现的接口,这样代理和实际对象可以互换。

  2. 实际对象(Real Subject):定义了代理所代表的真实对象,实现了主题接口。

  3. 代理(Proxy):包含对实际对象的引用,实现了与实际对象相同的接口,并在访问实际对象之前或之后进行额外处理。

代理模式有几种不同的形式,包括:

  • 远程代理(Remote Proxy):为远程对象(位于不同的地址空间)提供局部代表。
  • 虚拟代理(Virtual Proxy):延迟初始化资源密集或计算成本高的对象。
  • 保护代理(Protection Proxy):控制对原始对象的访问,进行权限检查。
  • 智能引用(Smart Reference):在访问对象时执行额外操作,如引用计数。

代理模式的主要优点包括:

  • 控制访问:可以在访问实际对象之前或之后添加额外的操作,如日志记录、权限检查等。
  • 解耦依赖:客户端不需要知道实际对象的实现细节,只需要知道代理的接口。
  • 延迟初始化:可以在真正需要时才创建实际对象,节省资源。
  • 增加额外功能:可以在不修改实际对象的情况下,通过代理增加额外的功能。
// 主题接口
interface Subject {
    void request();
}

// 实际对象
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// 代理类
class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy() {
        this.realSubject = null;
    }

    public void setRealSubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        System.out.println("Proxy: Logging before request.");
        realSubject.request();
        System.out.println("Proxy: Logging after request.");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.request(); // 通过代理访问实际对象
    }
}

11.1 补充 依赖注入

依赖注入(DI)是实现控制反转的一种方法,调用方不直接使用依赖,而是使用传递的方式将依赖注入给调用方。

11.1 补充 IOC 容器的初始化过程

IOC 容器中管理的对象称为 Bean ,Spring 启动时读取 Bean 的配置信息,并在 Spring 中生成一份相应的配置注册表,根据这张注册表实例化 Bean,配置好 Bean 之间的依赖关系。

11.1 补充 AOP 的注解

1. Aspect

使用在 Bean Class 上,被注释的类为 AOP 的配置类。

2. Pointcut

使用在方法上,定义哪些方法需要被拦截或者增强。

3. Before

前置通知,在方法执行之前执行。

4. After

后置通知,在方法执行之后执行。

5. AfterRunning

返回通知,在方法正常返回结果之后执行。

6. AfterThrowing

通知异常,在方法抛出异常之后执行。

7. Around

包围一个连接点的通知,可以在方法调用前后完成自定义的行为。

11.1 补充 Spring Bean

作用域

1. Singleton:单例模式

2. Prototype:原型模式,每次注入,容器都将创建一个新的 Bean实例。

3. Request(Web):每次 HTTP 请求会创建一个新的实例。

4. Session(Web):对于每次Session创建一个新的实例。

5. GlobalSession(Web):全局 Session 只创建一个实例。

三种配置方式

1. 基于 xml 文件配置

2. 基于注解进行配置

3. 基于 Java 类的配置方式 

11.2 Spring Boot

Spring Boot 是 Spring 的一套快速配置脚手架。

  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值