Java后端开发进阶知识点整理

一、Java后端基础知识

1、redis知识架构整理

1.1、五大基本数据类型相关

  • String类型的底层实现

    基于SDS这个类型。优势在于:

    • C语言字符存储本身的字符长度,O(n); 存储字符长度,只需要读取,O(1)

    • C语言字符串拼接,如果没有分配到足够长的内存空间就会内存溢出,【SDS】能根据len提前判断是否满足,如果空间不够就会扩展,避免缓冲区溢出的问题

    • SDS还提供了空间预分配惰性空间释放两种策略。

      惰性空间释放:free记录需要释放的空间,等到后面使用的时候再释放。

      具体的空间预分配策略原则是:当修改字符串后的长度len小于1MB,就会预分配和len一样长度的空间,即len = free; 若是len大于1MB,free分配的空间大小就为1MB

    • SDS是二进制安全的,可以存储二进制文件(如图片、音频等);C语言字符串必须以**\0空字符作为结束符**,而某些二进制文件中间可能含有空字符。

  • Hash类型的底层实现

    ziplisthashtable,hashtable原理:

    在新增时会通过Key值计算出数组下标(和 Hash Map类似),不同之处在于,hashtable在计算出hash值之后,还要通过sizemask属性和哈希值再次得到数组下标。

    • rehash渐进式rehash

      ht[0] 和 ht[1] 两个hash表交替扩展。

      扩展操作:ht[1]扩展的大小是比当前 ht[0].used 值的二倍大的第一个 2 的整数幂;收缩操作:ht[0].used 的第一个大于等于的 2 的整数幂。

      当ht[0]上的所有的键值对都rehash到ht[1]中,会重新计算所有的数组下标值,当数据迁移完后ht[0]就会被释放,然后将ht[1]改为ht[0],并新创建ht[1],为下一次的扩展和收缩做准备。

      当rehash操作开始时会将该值改成0,在渐进式rehash的过程**「更新、删除、查询会在ht[0]和ht[1]中都进行」**,比如更新一个值先更新ht[0],然后再更新ht[1]。

      而新增操作直接就新增到ht[1]表中,ht[0]不会新增任何的数据,这样保证**「ht[0]只减不增,直到最后的某一个时刻变成空表」**,这样rehash操作完成。

    • ziplist 压缩列表

      压缩列表是一块连续的内存,「压缩列表并不是以某种压缩算法进行压缩存储数据,而是它表示一组连续的内存空间的使用,节省空间」

      hash键只包含较少的key-value对,且key-value对都是小整数型或者较短的字符串时,会使用ziplist作为存储结构

    附:可以用作高并发下唯一ID的生成。

  • List类型的底层实现

    ziplistlinkedList 后又引入了 3.2之后quicklist

    • linkedlistquicklist都是双向链表,链表都定义了自己的长度信息,获取长度的时间复杂度为o(1);

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P8RW1go3-1692088161164)(…/…/杂七杂八/Typora图片存储/image-20230724134949743.png)]

  • Set类型的底层实现

    htintzet

    intzet ?

  • Zset类型的底层实现\

    ziplistskiplist,

    • skiplist 跳跃表 即兼顾查询效率,也保证了顺序查询
关于底层实现的几个数据结构的补充:

参考(169条消息) Redis学习(一)简单动态字符串(SDS)_wifiiiiiiii的博客-CSDN博客

包括:SDS,链表,字典(hash),跳跃表(skiplist),整数集合,压缩列表,对象。

  • 链表(list)

    redis对于链表的处理,在原listNode的基础上,还引入了**list(包含head, tail, length, dup, free, match)**类型,用于简化对链表的操作。其中,dup:复制链表节点所保存的值;free:释放链表节点所保存的值;match:对比链表节点所保存的值和另一个输入值是否相等。

  • 字典(hash) hash

    typedef struct dict {
        // 类型特定函数
        dictType *type;
        
        // 私有数据
        void *privdata;
        
        // 哈希表
        dictht ht[2];
        
        // rehash索引
        // 当 rehash 不在进行时,值为 -1
        int rehashidx;
    } dict;
    
    // 哈希表数组
    typedef struct dictht {
        dictEntry **table;
        
        // 哈希表大小
        unsigned long size;
        
        // 哈希表大小掩码,用于计算索引值
        // 总是等于 size - 1
        unsigned long sizemask;
        
        // 该哈希表已有节点的数量
        unsigned long used;
    }dictht;
    
    //哈希表节点
    typedef struct dictEntry {
        // 键
        void *key;
        
        // 值
        union {
            void *val;
            uint64_t u64;
            int64_t s64;
        } v;
        
        // 指向下个哈希表节点,形成链表
        struct dictEntry *next;
    } dictEntry;
    
    
  • 跳跃表(skiplist)

  • 整数集合

  • 压缩列表(ziplist)

1.2、redis持久化相关

  • AOF (append only file)

    特点:只会在文件末尾添加写入和修改操作命令。默认未开启。

    aof文件写入过程:

    命令传播 -》 缓存追加 -》文件写入和保存

    AOF重写

  • rdb(redis database)

    特点:快照读RDB保持redis在某一时刻的数据的快照

    触发条件:

    1. 符合自定义配置的快照规则
    2. 执行save或者bgsave命令
    3. 执行flushall命令
    4. 执行主从复制操作(第一次)

    原理:

    1. Redis父进程首先判断:当前是否在执行save,或者bgsave/ bgwriteaof(aof重写命令)的子进程,如果在执行则bgsave命令返回;
    2. 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令;
    3. 父进程fork后,bgsave命令返回”Background save started" 信息并不再阻塞父进程,并可以响应其他命令;
    4. 子进程创建RDB文件,根据父进程的内存快照生成临时快照文件,完成后对原有文件进行原子替换(RDB始终完整);
    5. 子进程发送信号给父进程表示完成,父进程更新统计信息。
    6. 父进程fork子进程后,继续工作。

    优缺点:

    • RDB是二进制压缩文件,占用空间小,便于传输(传给slaver);
    • 主进程fork子进程,最大化Redis性能
    • 使用RDB文件来恢复数据比较快

    缺点:

    • 不保证数据的完整性,会丢失最后一次快照后所有数据
    • 父进程在fork子进程的时候,如果主进程比较大会阻塞很久。

1.3、redis集群相关

三种形式:主从模式哨兵模式、**集群模式shuuredis集群采用Hash Slot 数据切片的方式,而不是采用一致性哈希。原理:redis集群包含16438个插槽,在存入KV时,首先根据Key%16438计算该键属于哪个插槽。集群里的节点,各自保持一段的插槽,这样就将不同的KV对平均分配到各个节点里。

例如:有A、B、C三个节点,其中节点和插槽的对应:A(0 - 5000)、B(5001 - 10000)、C(10001 - 16438)。当有KV插入时,根据CRC16(key)%16438 计算插槽值,并存入到对应的服务器节点,比如A;当B、C或者其他节点读取该KV时,会根据对应的算法计算出该KV处于A节点,就从A节点读取到KV,并返回客户端;当新增或者删除节点时,只需要将对应的插槽进行移动即可。从用户角度来看,整个集群就像是一体的。

redis集群策略在CAP的体现:

A:采用插槽的方式,节点增删改,只需要移动对应的插槽即可,不会影响对应的数据查询和删除,整体代价非常小。可扩展性极高。

P:为了保证一定的分区容错性。redis设计了主从复制模式 和 哨兵模式 。slave of.当主机挂掉,没有节点处理主机对应的插槽时,从机会自动升级为主机替代主机进行工作。

C:redis并不能保证数据的强一致性。 原因如下:

  • redis集群采用异步复制作为主从复制的方式。写操作过程:

    • 客户端向主节点B写入一条命令.

    • 主节点B向客户端回复命令状态.

    • 主节点将写操作复制给他得从节点 B1, B2 和 B3

  • cluster节点通信原理

    采用 Gossip通信协议。

    Gossip协议基本思想就是:一个节点想要分享一些信息给网络中的其他的一些节点。于是,它周期性随机选择一些节点,并把信息传递给这些节点。这些收到信息的节点接下来会做同样的事情,即把这些信息传递给其他一些随机选择的节点。一般而言,信息会周期性的传递给N个目标节点,而不只是一个。这个N被称为fanout(这个单词的本意是扇出)

  • 哨兵模式

  • 等等……

2、 MySQL知识架构整理

参考文章:(170条消息) MySQL数据库面试题总结(2022最新版)_mysql面试题_程序猿周周的博客-CSDN博客

2.1、SQL相关

  • SQL语句

    SQL 编写相关,select, update, insert, delete. join on left join on inner join, right join

    连接(join)联合(union)。前者是左右拼接,后者是上下拼接。

    连接: 内连接,只返回匹配的行,外连接,返回全表,不匹配为NULL。

  • SQL语句的执行顺序,explain语句的原理

    SQL执行顺序:先处理表(from on jion),再削行(where group by),(处理列值avg, sum),然后再削列(select),最好唯一和排序。

    1. from
    2. on
    3. jion
    4. where
    5. group by
    6. avg, sum……
    7. having
    8. select
    9. distinct
    10. order by
  • SQL调优技巧:

    1. 慢查询日志
    2. explain
    3. 分库分表(垂直分表【切字段】,水平分表【切行数】)

2.2、数据库原理与应用相关

  • 事务的ACID性质,原子性,一致性,持续性,隔离性。

    • 原子性:MySQL通过undolog实现原子性,即回滚操作。

    • 持久性:持久性是指事务一旦提交后,对数据库的改变就是永久的。基于redolog实现。背景,MySQL写数据时,是先将数据放入缓冲池,再定期将数据刷入磁盘,以达到充分利用CPU和内存资源,减少线程阻塞。但是也引发了新的问题,就是当事务提交后,数据存放在内存还没来得及刷入磁盘时,数据库宕机,就会导致事务丢失。于是引入redolog。事务在提交后,是先在redolog里写入操作命令,再更新到Buffer Pool。这样即使数据库宕机,重启之后仍然可以通过重读redolog恢复。

    • 隔离性:指并发事务,执行结果应该跟串行化执行相等。

      脏读,不可重复读,幻影读 - 》 读未提交,读已提交,可重复读,串行化。

    • 一致性:一致性是指事务执行结束后,**数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态。**数据库的完整性约束包括但不限于:实体完整性(如行的主键存在且唯一)、列完整性(如字段的类型、大小、长度要符合要求)、外键约束、用户自定义完整性(如转账前后,两个账户余额的和应该不变)。

  • MySQL隔离级别(),实现原理?

    隔离级别表

    加锁、InnoDB的MVCC多版本并发控制:

    笔记:

    当前读:读取的是记录的最新的版本。(InnoDB引擎默认是可重复读)

    快照读: 简单的select(不加锁)就是快照读,读取的是数据的可见版本。

    MVCC实现原理:

    • 数据库记录的隐藏字段

      DB_TRX_ID:最近修改事务ID,记录插入这条记录或者最后一次修改改记录的事务ID。

      DB_ROLL_PIR:回滚指正,指向这条记录的上一个版本,用于配合undo log,指向上一个版本。

      DB_ROW_ID:隐藏主键,如果表结构没有指定主键,就会生产该隐藏字段。

    • undolog

      undo log版本链

    • readview

      ReadView,记录并维护系统当前活跃的事务(未提交的)ID

      m_ids:当前活跃的事务ID集合

      min_trx_id:最小活跃事务ID

      max_trx_id: 预分配事务ID,当前最大事务ID+1

      creator_trx_id: ReadView创建者的事务ID

      版本链数据访问原则:(trx_id:代表是当前事务ID)

      1. trx_id == creator_trx_id ? 可以访问该版本
      2. trx_id < min_trx_id ? 可以访问该版本
      3. trx_id > max_trx_id ? 不可以访问该版本,说明该事务是在ReadView生成之后才开启的
      4. min_trx_id <= trx_id <= max_trx_id ? 如果trx_id 不在m_ids中是可以访问该版本的 成立,说明事务已经提交。

      不同的隔离级别,生成ReadView的时机不同:

      **Read Commited:**在事务中每一次执行快照读时生成ReadView。

      REPEATABLE READ: 仅在事务中第一次执行快照读时生成ReadView, 后续复用该ReadView.

2.3、引擎相关(主要是innodb)

  • MySQL有哪些引擎
  • MVCC多版本并发控制的实现原理

2.4、索引

  • 索引的底层实现

    B+树,

    1. 为什么使用B+树作为索引,而不是hash, 二叉树,B树。
  • 索引失效的原因(13种)

    1. **select ***

    2. 不满足最左匹配法则(联合索引)

    3. 索引列上有计算

    4. 索引列使用了函数

    5. 类型隐式转换

      查询字符串时,对字符串条件未加引号,例如:select name from student where height = 13; (注意,height在MySQL里为varchar类型),则此时会走全表扫描

    6. LIKE 左边包含 %

    7. 列对比

    8. 使用了OR关键字

    9. NOT IN和NOT EXISTS关键字

    10. order by 在某些情况下也不会走索引

      • 没加where或者limit条件
      • 对不同的索引进行ORDER BY
      • 不满足最左匹配原则
    11. 不等于比较

    12. IS NOT NULL

    13. 优化器发现走索引效率不如全表扫描

  • 最左前缀法则

2.5、数据备份

  • binlog
  • redolog
  • undolog
  • errorlog
  • slow query log
  • (中继日志) relay log:在从节点中存储接收到的 binlog 日志内容,用于主从同步。

2.6、MySQL集群

  • 主从复制:基于binlog实现的。

3、Java

3.1、Java集合源码

  • 常用集合类及其实现原理

    ArrayList LinkedList Stack (Queue) HashMap TreeMap ProtieQueue等

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Y85kPCl-1692088161165)(…/…/杂七杂八/Typora图片存储/v2-1fb29db41cbe03a0ad3b6a7947f2cd0a_r.jpg)]

    • ArrayList扩容机制

    • LinkedList底层原理?

    • HashMap自动扩容机制和哈希冲突的解决办法(红黑树)

      红黑树有五条原则。

  • HashMap(非并发安全)VS ConcurrentHashMap(并发安全)

    面试题:HashMap是并发安全的吗,为什么?如何解决HashMap的非并发安全(使用ConcurrentHashMap)?ConcurrentHashMap的底层原理是什么?

    作答:HashMap不是并发安全的,举例,如果同时有两个线程对Map的同一个Key值设置value值,那么因为,并发线程的执行顺序是未知的,所以会导致HashMap对应的Key值也未知,或者说某个线程的更新丢失。

    ConcurrentHashMap,使用CAS锁和synchronized同步锁来保证并发安全,同时为了保证查询效率,使用了分段锁的形式。只synchronized某个节点,来提高插入效率。

  • HashTable VS HashMap (HashTable已经被Org官方宣布弃用)

3.2、Java多线程技术(重点synchronized和CAS,ThreadLocal线程相关,线程池技术)

  • 语言级实现机制

    在Java中,主要使用synchronizedReentrantLock来实现悲观锁,使用CAS算法(AtomicInteger类型)来实现乐观锁(并行自增一致)

    1. volite使用及其实现原理

    2. synchronized同步锁的实现原理

      原理:synchronized在JVM的实现原理,JVM基于moniter进入和退出来实现对代码块和方法区的同步实现,但二者的实现细节有区别。代码块同步是使用moniterenter和moniterexit来实现的,方法同步是另一种方式,细节在JVM规范里并没有详细说明,但方法的同步同样可以使用这两个指令来实现。monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter
      指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

    3. CAS(Compare And Sweep)乐观锁原理及其Atomic类的底层原理

      AtomicInteger类型如何保证原子自增?

  • Thread线程技术:

    1. Java开启多线程的三个方式(new Thread(), 实现Runable类,实现Cal……类

    2. ThreadLocal详解

      • ThreadLocal实现原理:

        ThreadLocal的 get()set(T value)方法都是基于ThreadLocalMap(每个线程内部的Map),Map中的key为ThreadLocal实例的弱引用,value为对应设置的value(线程变量的副本),这些对象之间的引用关系如下:

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3sB55SkM-1692088161165)(…/…/杂七杂八/Typora图片存储/v2-e2d4b8eac152596232d3e32313927d59_r.jpg)]

                HashMap<Object, String> map = new HashMap<>();
                map.put(null, "hello");
                System.out.println(map.get(null));
        //输出为:hello,证明null也能作为Key
        
      • ThreadLocal内存泄漏

        从上图可以看出,当ThreadLocal不存在外部强引用后,势必会被JVM回收,这时ThreadLocalMap存储的Key值就变成了null,但因为value是强引用,所以不会被JVM回收。但如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:

        Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value

        永远无法回收,造成内存泄漏。

  • 线程池技术:

    1. 怎么使用线程池:ThreadPoolExetuter类执行,初始化方法的参数有 corePoolSize, maximumPoolSize, workQueue, keepAliveTime, unit, threadFactory(线程生成策略), handler(饱和策略,或者任务拒绝策略);

      线程池需要解决的问题是,减少线程频繁创建销毁带来的性能开销

      ThreadPoolExecuter类的几个比较重要的参数理解:

      • corePoolSize
      • maximumPoolSize
      • workQueue
      • keepAliveTime
      • unit
      • threadFactory
      • handler

    线程池设计一共需要处理三个问题:

    线程池如何维持自身的状态?如何管理任务?如何管理线程?

    1. 线程池如何维持自身的状态?

    1.1 线程池内部用一个ctl的变量存储了runstate 线程池状态 和 workerCount线程池线程数量 两个变量,其中前三位bit 存储线程池状态,后29位bit存储线程池数量。

    线程池状态分为:RUNNING、SHOTDOWN、STOP、TIDYING?、TERMINATED?;

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dMR8rcFu-1692088161165)(…/…/…/…/杂七杂八/Typora图片存储/image-20230725152045210.png)]

    1. 线程池如何管理任务?
    • 任务调度:

    • 任务缓冲:(阻塞队列)

      • 数组有界
      • 链表有界
      • 优先级
      • 延迟队列
      • 同步队列(只有一个位置)
      • 链表无界队列
      • 双向链表阻塞队列
    • 任务申请:(线程申请任务执行)

      getTask()方法实现

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XaVWWbIu-1692088161166)(…/…/…/…/杂七杂八/Typora图片存储/image-20230725153842951.png)]

    • 任务拒绝:

      任务拒绝策略,即ThreadPoolExcuter,初始化参数里的handler参数填入的部分。

      拒绝策略是一个接口,设计如下:

      public interface RejectedExecutionHandler {
      void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
      }

      用户即可以自行实现该接口,也可以选择JDK提供的四种拒绝策略

      • 拒绝其报错,默认 ThreadPoolExecuter.AbortPolicy
      • 拒绝但不报错 ThreadPoolExecuter.DiscardPolicy
      • 抛弃阻塞队列里最老的任务,并对当前任务重新申请线程 ThreadPoolExecuter.DiscardOldestPolicy
      • 由创建任务的线程去执行任务 ThreadPoolExecuter.CallerRunsPolicy
    1. 线程池如何管理线程?

    线程池设计了worker线程,来作为线程池内部的执行任务线程。其设计如下:

    private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
    final Thread thread;//Worker持有的线程
    Runnable firstTask;//初始化的任务,可以为null
    }

    • woker线程的声明周期

      为了便于判断worker线程的状态(是否处于执行状态),编写者让worker线程继承了AQS, 使用AQS来实现独占锁的功能。线程回收时,会去询问worker线程的AQS锁,如果无法获得独占锁,则

    • woker线程的新增

    • woker线程回收

      线程池不会销毁线程,而是交给JVM来回收。线程池只会维护对线程的引用,当需要回收线程时,就放弃对线程的引用。 核心线程可以无限等待获取任务,非核心线程则只能限时等待。

    • woker线程执行任务

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7LcDg5hm-1692088161166)(…/…/…/…/杂七杂八/Typora图片存储/image-20230725163955630.png)]

3.3、 深入理解JVM:GC机制及类加载机制

  • Java内存模型

    运行时数据区域
    注:除了程序计数器,其他区域包括直接内存区域都会有OutOfMemoryError,故对应省略,不代表没有。

    • 程序计数器(线程私有,线程同周期)
      当前线程所执行的字节码的行号指示器。唯一一个没有OutOfMemoryError的区域

    • Java虚拟机栈(线程私有,线程同周期,线程方法)StackOverFlowError
      Java虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧,用于存储局部变量表操作数栈动态链接方法出口等信息。
      局部变量表存放了编译期间可知的各种Java虚拟机基本数据类型、对象引用类型和returnAddress类型(指向了一条字节码的地址)

    • 本地方法栈(为本地方法服务)StackOverFlowError
      本地方法栈和Java虚拟机栈类似,区别在于:虚拟机栈为虚拟机执行Java方法服务,而本地方法栈为虚拟机使用到的本地(Native)方法服务。
      附:java方法: 是由java语言编写,编译成字节码,存储在class文件中的。 java方法是与平台无关的。 本地方法: 本地方法是由其他语言(如C、C++ 或其他汇编语言)编写,编译成和处理器相关的代码。 本地方法保存在动态连接库中,格式是各个平台专用的,运行中的java程序调用本地方法时,虚拟机装载包含这个本地方法的动态库,并调用这个方法。

    • Java堆(所有线程共享,存放对象的实例)‘
      主要存储Java实例化的对象和数组,是虚拟机进行GC的主要区域。

    • 方法区(线程共享,存储常量、静态变量等)
      用于存储已被虚拟机加载的类型信息常量静态变量即时编译器编译后的代码缓存等数据。

    • 运行时常量池(方法区的一部分)

    • 直接内存

  • HotSpot虚拟机在Java堆中对象分配、布局和访问的全过程

    new 先检查使用有类的符号引用以及类是否被加载,如果没有则进行相应的类加载

    类加载检查通过之后,虚拟机将为新生对象分配内存。

    • 根据GC收集器是否有压缩整理内存的能力,来决定是否进行指正碰撞式的内存分配
    • 同时,内存分配时需要考虑到并发问题(解决方案是为每个线程分配一个线程缓冲池,先在缓冲池内实例化对象)
    • 分配完内存后,需要对该块内存进行初始化。
    • 然后设置一些必要的信息(比如说,这个对象是哪个类的实例,如何才能找到类的元数据信息,对象的hashCode,对象的GC分代年龄信息等信息),存放在对象头里(Object Header)
  • 垃圾回收以及内存分配策略

    垃圾识别算法

    • 引用计数法:对象头存储对它的引用计数,当计数为0,则可回收。实现简单,额外开销较小,但是存在循环引用的问题。现已弃用
    • 可达性分析:

    垃圾回收算法:

    • 分代收集理论:
    • 标记-清除算法:
    • 标记-复制算法:
    • 标记-整理算法:
  • 几大垃圾收集器:

    • Serial 最原始的垃圾回收策略,新生代采用复制算法,老年代采用整理算法,流程采用单线程,用户线程全停止的策略。

    • ParNew + CMS 最经典的商业垃圾回收策略 JDK1.7及以前

      ParNew 是新生代垃圾收集器,主要使用标记复制算法,采用多线程并发标记,并发清理的策略。

      CMS是一款以低时延为设计目的老年代垃圾收集器,主要使用标记整理算法,有初始标记- 并发标记- 重新标记(增量标记的策略)并发清除四个阶段。

    • G1 (Garbage First)JDK1.8默认的垃圾收集器。以停顿模型(指定一个M毫秒的时间段,其垃圾收集时间大概率不超过N毫秒),的更加可控稳定的GC。
      创造性的使用了基于Region的堆内存布局(不在使用分代的形式,而是将堆内存划分为若干个大小相同的Region),仍然采用了CMS并发标记并发整理的算法形式,变动点在于:

      1. 对每个Region维护一个卡表(意味着G1需要使用Java堆内存的10%-20%用来维护算法)以及
      2. 在后台维护一个优先级队列,每次整理就不再是Full Gc,而是仅针对部分Region的GC。
      3. 对于并发标记,CMS使用的是增量标记的策略,而G1则是采用原始快照(SATB)算法来解决的。
        初试标记并发标记最终标记筛选回收四个阶段。
        附:G1除了并发标记,其余阶段都需要暂停用户线程。其中最终标记和筛选回收是多线程进行。
    • Shenanadoah收集器 (仍然基于Region的堆内存分区,但是不再使用卡表而是维护一个全局的指向矩阵,减少额外内存开销)

    • ZGC 是一款基于Region的内存分区,不设分代的,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法的,以低延时为首要目标的垃圾收集器。
      特点:

      1. ZGC的region分为小、中、大三个大小,用于存储某些大对象。
  • JVM调优技巧:

注:基于Java堆是虚拟机控制的最大的一块内存区域,而Java堆上的主要问题为GC回收,故JVM调优技巧主要为如何根据当前服务器的硬件条件和运行要求,选择合适的垃圾收集器以及使用合适的参数来确保与GC充分配合,软件运行不会崩溃。但是不代表JVM调优只有GC方面一块,肯定还包括查看程序运行时线程占用是否合理等等。

  • JDK常用的命令:jps(虚拟机进程状况工具),jstat(虚拟机统计信息监视工具),jinfo(Java配置信息工具), jmap(java内存映射工具),jhat(虚拟机堆转储快照分析工具),jstack(Java堆栈跟踪工具)
  • **-Xmx:指定应用程序可用的最大堆大小; - Xms指定应用程序可用的最小堆大小,-Xmn 堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn,-Xss 设置每个线程可使用的内存大小,即栈的大小。
  • JVM类加载机制和双亲委派模型

    class文件(二进制字节流)加载过程:加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载

    1. 类加载机制:

      在类加载过程中,《虚拟机规范》只规定了要通过class文件的全限定名加载二进制流文件,并没有规定加载的方式和加载文件的实际类型。

      这就让类加载有了很大的可扩展性:

      • 从压缩文件中读取,是WAR,EAR,JAR包的基础支撑
      • 从网络空间读取,最典型的应用是Applet
      • 运行时计算生成,这就是动态代理的基础
      • 由其他文件生成,最典型的应用就是JSP应用,由JSP文件生成对应的类。

      类加载器和类的唯一性:在虚拟机中,类的唯一性是由类的加载器和这个类本身共同决定的。这就意味着,如果某个类是由两个类加载器加载出来的两个类,则这两个类在虚拟机中会被认为是不同的。

    2. 双亲委派模型:

3.4、JavaIO技术(最近好像考的比较少)

考的比较少就简单聊聊吧,IO技术主要涉及文件读取

JavaIO按读取的数据流分为

  • 字节流

    InputStream(), OutputStream();

  • 字符流

    Reader(), Writer();

按读取方式又分为

  • 阻塞式同步IO
  • 非阻塞式同步IO
  • 阻塞式异步IO(NIO)
  • 非阻塞式异步IO

4、计算机网络

注:计算机网络的知识相对来说只有大厂考察较多,但相对靠底层(相比考研计网的要求要低许多),故有必要进行一次结构性的梳理。

4.1 OSI七层模型和TCP/IP五层模型

从上往下依次为:

  • 应用层(TCP/IP五层模型,为应用层、表示层、会话层 共同为 应用层HTTP、HTTPS、SMTP
  • 表示层
  • 会话层
  • 传输层 TCP UDP
  • 网络层 IP
  • 链路层 ARP
  • 物理层

4.2 从一次浏览器访问服务并显示页面的过程浅谈七层协议

5、操作系统

6、RabbitMQ

RabbitMQ是基于AMQP(高级消息队列协议)编写的一款开源的消息队列中间件。

  • 简单模式
  • work模式
  • 发布订阅模式
  • 简单路由模式
  • 匹配路由模式

透彻rabbitmq - 知乎 (zhihu.com)

docker pull rabbitmq:management

docker run -id --name=rabbitmq -v rabbitmq-home:/var/lib/rabbitmq -p 15672:15672 -p 5672:5672 -e RABBITMQ_DEFAULT_USER=swsk33 -e RABBITMQ_DEFAULT_PASS=123456 rabbitmq:management

6.1、死信队列的实现方案

  • 什么是死信队列:

    消费失败的消息存放的队列。

    消息消费失败的原因:

    • 消息被拒绝并且消息没有重新入队(requeue=false)
    • 消息超时未消费
    • 达到最大队列长度

6.2、消息丢失

生产者生产的消息到RabbitMQ Server消息丢失,RabbitMQ Server存储的消息丢失和Rabbit MQ到消费者的信息丢失

对应的解决方案:生产者确认机制、消费者确认机制和持久化

  • 生产者确认机制

    事务机制。 在一条消息发送之后会是发送端堵塞,等待RabbitMQ的回应,之后才能继续发送下一条信息,性能差。

    开启生产者确认机制。 只要消息到达路由端,就给生产者发送一条ack确认,如果没收到,就会发送nack提示发送失败

  • 消费者手动确认:

    消费者设置为手动确认消息。消费者处理完逻辑之后再给broker回复ack,表示消息已经成功消费,可以从broker中删除。当消息者消费失败的时候,给broker回复nack,根据配置决定重新入队还是从broker移除,或者进入死信队列。只要没收到消费者的 acknowledgment,broker 就会一直保存着这条消息,但不会 requeue,也不会分配给其他 消费者。

    消费者设置手动ack:

    ##设置消费端手动 ack
    spring.rabbitmq.listener.simple.acknowledge-mode=manual
    
  • 持久化:

    消息持久化需要满足以下条件:

    1. 消息设置持久化。发布消息前,设置投递模式delivery mode为2,表示消息需要持久化。
    2. Queue设置持久化。
    3. 交换机设置持久化。
  • 消息重复消费怎么处理?

  • 消费端怎么进行限流

docker run -id --name=rabbitmq -v rabbitmq-home:/var/lib/rabbitmq -p 15672:15672 -p 5672:5672 -e RABBITMQ_DEFAULT_USER=swsk33 -e RABBITMQ_DEFAULT_PASS=123456 rabbitmq:management

7、设计模式

一共有23种设计模式,可分为三大类:创建型,结构型,行为型

7.1、创建型模式。(4种)

  1. 单例模式

    保证在程序中只有一个实例存在,并且能全局访问到。

  2. 工厂模式

    包括 简单工厂,工厂方法,抽象工厂这3种细分模式。

    每个对象的创建逻辑都比较复杂的时候,会考虑使用工厂模式,将实例的创建和使用分割开来。

  3. 建造者模式

    用来创建复杂对象,(主要是单个对象)。

  4. 原型模式(不常用):

    如果对象的创建成本比较大,同一个类,不同对象之间差别不大(大部分字段相同),这种情况就可以利用已有对象(原型)进行拷贝复制的方式,来创建新对象。

7.2、结构型模式。

  1. 代理模式:

    在不改变原始类接口的条件下,为原始类定义一个代理类,主要目的是控制访问,而非加强功能,这是它跟装饰器模式最大的不同

  2. 桥接模式:(不常用)

    将抽象和实现解耦,让它们能独立开发。

  3. 装饰器模式:

    主要解决继承关系过于复杂的问题,通过组合来代替继承,给原始类添加增强功能。

  4. 适配器模式:

    代理模式、装饰器模式提供的都是跟原始类相同的接口,而适配器提供跟原始类不同的接口。适配器模式是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。适配器模式是一种事后的补救策略,用来补救设计上的缺陷。

  5. 门面模式:

  6. 组合模式:

  7. 享元模式:

7.3、行为型设计模式。

主要解决的就是“类或对象之间的交互问题”。

  1. 观察者模式:
  2. 模板模式:
  3. 策略模式:
  4. 职责链模式:多个处理器依次处理同一个请求。 多用于实现过滤器,拦截器。
  5. 迭代器模式:
  6. 状态模式:
  7. 访问者模式:
  8. 备忘录模式:
  9. 命令模式:
  10. 解释器模式:
  11. 中介模式:

7.4、设计模式的六大原则:

  1. 开闭原则(对扩展开放,对修改关闭

  2. 里氏代换原则(面向对象的基本原则)

    任何基类出现的地方,子类一定可以出现。是实现抽象化的具体步骤的规范。

  3. 依赖倒转原则(针对接口编程,依赖于抽象而不依赖于具体)开闭原则的基础

  4. 接口隔离原则:

    使用多个隔离的接口,比使用单个接口要好。

  5. 迪米特法则(最少知道原则)

    一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

  6. 合成复用原则

    原则是尽量使用合成/聚合的方式,而不是使用继承。

8、分布式:

8.1、理论&算法&协议

CAP理论:

  • C,一致性:所有节点访问同一份最新的数据副本

  • A,可用性:非故障的节点在合理的时间内做出合理的响应

  • P,分区容错性:指分布式系统出现网络分区时,仍然能够对外提供服务。(网络分区是指,多个节点之间因为某些故障,不连通了,整个网络出现了几个区域。)

  • AP和CP的矛盾

    这里其实是有一个误解,当不存在网络分区时,也就是不需要保证P时,C和A是可以同时保证的;当出现网络分区时,如果对某个分区A进行写数据操作,假设分区B与A不连通,且存在与A相同的数据副本,此时,如果要保证数据的一致性,则需要关闭B节点的服务,即放弃了保证A;如果要保证两个分区的可用性,则就不能保证在两个分区的数据一致性,即无法保证C。

BASE理论:

Basically Available(基本可用),Soft-Status(软状态)和Eventually Consistant(最终一致性)的短语缩写。该理论是对AP策略的扩展。

​ 基本思想是,即使无法做到强一致性,也可以采取适当的方法来使系统达到最终一致性。

共识算法:Paxos算法,Raft算法

Gossip协议。

8.2、ZooKeeper

9、ES

定义:是一款实时分布式搜索和分析引擎。 是面向文档的非关系型数据库。

elasticsearch(集群)可以包含多个索引(数据库),每个索引中可以包含多个类型(表),每个类型下可以包含多个文档(行),每个文档中又包含多个字段(列)。

重要概念:

索引-》类型-》文档:

IK分词器: 用于拆分搜索关键词。

10、算法:

10.1、排序算法:

  • 选择排序

  • 插入排序

  • 冒泡排序

  • 希尔排序(通过增量,将数组分成多个子表,对每个子表采用插入排序,)

  • 归并排序

  • 快速排序(归并排序的思想,子表采用交换类排序)

  • 堆排序

    基于堆的排序算法,(不稳定),基本思路为,首先基于数组生成大顶堆(或者小顶堆),每次将堆顶元素拿出作为排序好的元素,再将叶子节点最左边的数放到堆顶,并调整堆,直到堆只剩一个元素,排序完成。

    补充:

    1. 大顶堆和小顶堆基本都可以通过数组实现。以大顶堆为例,大顶堆是父节点大于子节点值,的完全二叉树。意味着根结点一定是最大值。

11、Docker

概念,镜像,容器,DockerFile

流程,先拉取镜像,在运行容器。

docker pull [name]:[version] #默认最新

docker build -t [image_name] . #通过Dockerfile文件构建容器

docker run -d --name=communnity -p 8080:8080 -v /temp/data:/temp/config [image]

docker exec -it [container] /bin/bash #以控制台的方式进入Docker容器

docker ps (-a) [container] #查看容器

12、Spring及其全家桶系列

笔者编:本身Spring框架应该收入到Java那一节中,但是考虑到Spring系列在面试八股中已经占了相当重要的一部分,单独作为一节来进行总结。

12.1 一切的初始 之 Spring框架

Spring框架,是基于约定大于配置原则,拥有着IoC(inverse of Controll, 控制反转) 和AOP两大特性的,基于Bean的企业级开发框架。

  • 控制反转(IoC):

    控制反转是通过DI,即依赖注入来实现的,实现的思想为,将对象的实例通过各种方式注册为Bean,由IoC容器统一进行管理,当需要某个Bean时,只需要通过依赖注入就可以获取对应的对象实例,而不需要考虑对象实例的生成,配置和销毁。以达到代码解耦等目的。

    1. 如何声明Application容器,以及如何声明Bean?

      ioc 容器初始化过程:BeanDefinition 的资源定位、解析和注册。

      Bean的创建方式有三种:

      1. 基于xml文件的方式:

        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
        	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        	   xmlns:context="http://www.springframework.org/schema/context"
        	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        
        	<bean id="mobian3" class="pers.mobian.springseventh.MoBian3XML">
        </beans>
        
        public class MainTestXML1 {
        	public static void main(String[] args) {
        		//启动容器
        		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-test.xml");
        		System.out.println(context.getBean("mobian3"));
        	}
        }
        
      2. 基于注解:

        @Component
        @Service
        @Controller
        @Dao
        @Mapper
        ……等
        //在容器类上添加注解:
        @ComponentScan("pers.mobian.firstTest")
        
      3. 基于注解 + xml的方式

        主要是在xml里声明包的扫描路径。在包类中使用注解声明Bean

        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
        	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        	   xmlns:context="http://www.springframework.org/schema/context"
        	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        
        	<context:component-scan base-package="pers.mobian.firstTest"/>
        </beans>
        
    2. Bean的生命周期?

      1. 对Bean进行实例化

      2. 依赖注入

      3. 配置Bean的各种属性:

        4.如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()

        5.如果Bean实现了ApplicationContextAware接口,Spring容器将调用setApplicationContext()

        6.如果存在BeanPostProcessor,Spring将调用它们的postProcessBeforeInitialization(预初始化)方法,在Bean初始化前对其进行处理

        7.如果Bean实现了InitializingBean接口,Spring将调用它的afterPropertiesSet方法,然后调用xml定义的 init-method 方法,两个方法作用类似,都是在初始化 bean 的时候执行

        8.如果存在BeanPostProcessor,Spring将调用它们的postProcessAfterInitialization(后初始化)方法,在Bean初始化后对其进行处理

      4. Bean初始化完成,交给应用使用,直到应用被销毁

        10.如果Bean实现了DisposableBean接口,Spring将调用它的destory方法,然后调用在xml中定义的 destory-method方法,这两个方法作用类似,都是在Bean实例销毁前执行。

    3. 单例Bean的线程安全问题?循环依赖的解决方案?

  • 面向切面编程(AOP):

    AOP 编程思想是指在不改变源代码的前提下,对代码进行增强。

    1. 实现:静态代理动态代理

      动态代理是指在程序运行时生成一个代理对象,在运行期间对业务方法进行增强,不会生成新类。

      JDK动态代理

      通过implment接口实现,目标类必须实现接口,如果某个类没有实现接口,就不能用JDK动态代理。

      CGLIB动态代理

      通过继承实现,CGLIB可以在运行时动态生成类的字节码,动态创建目标类的子类对象,在子类中增强目标类。

    2. AOP术语:

      1. 切面:切面是切点和通知的结合。通知和切点共同定义了切面的全部内容。
      2. 连接点:指方法,连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法、抛出异常时、甚至是修改一个字段时。
      3. 通知:在AOP术语中,切面的工作被称为切点。
      4. 切入点:切点的定义会匹配通知所要织入的一个或者多个连接点。
      5. 引入:引入运行我们向现有类添加新方法或属性。
      6. 目标对象:被一个或者多个切面所通知的对象,通常是一个代理对象。
      7. 织入:织入是把切面应用到目标对象并创建新的代理对象的过程。

12.2 web编程之SpringMVC

MVC为web编程的一种工作模式,即Model(模型, 通常为数据模型),View(视图,通常为静态资源,如HTML文件),Controller(控制器,通常为请求的映射器,用来决定对某个请求返回什么样的Model And View)

SpringMVC 其实就是一种基于Servlet的MVC模型:

  • SpringMVC原理 OR web请求的执行流程:

    1. 客户端请求分发到 -> DispatchServlet(前端控制器);

    2. DispatchaServlet控制器通过HandlerMapping(底层是一个url -> Controller方法的map),找到处理请求的controller

      3、处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

      4、 DispatcherServlet调用HandlerAdapter处理器适配器。

      5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

    3. DispatchaServlet将请求交给Controller处理

    4. controller处理完逻辑后,将ModelAndView返回给DispatchaServlet;

    5. DispatchServletModelAndView交给 ViewResolver(视图解析器),处理成View。

    6. DispatchServlet将结果(View)封装成HTTP,返回响应数据

    7. 浏览器绘制页面。

12.3 快速搭建应用之SpringBoot

SpringBoot这个框架,以Spring框架为底层,完全基于约定大于配置的设计思路,通过自动配置,注解等方式,自动处理了Application容器,和大多数Bean的创建声明,大大简化了搭建一个Spring框架的流程以及开发Java应用过程中用于配置Bean的消耗。

改变:在原有Spring框架的基础上,推崇以Java注释的方式声明Bean,支持application.properties / .yml 配置文件的方式,快速配置一些Bean的属性,支持集成多种Spring框架,包括SpringMVC,SpringData, SpringSecurity;

  • SpringBoot容器的启动流程,@SpringBootApplication注解的原理

    指示一个配置类,该类声明一个或多个@Bean方法,还触发自动配置和组件扫描。这是一个方便的注释,相当于声明@Configuration、@EnableAutoConfiguration和@ComponentScan。

    @SpringBootApplication注解主要由三个部分组成

    1. @ComponentScan:Spring注解,用于声明应该在哪些包路径下扫描基于Java注解的Bean,用于创建注入到Application容器里。

    2. @SpringBootConfiguration

      主要是对Spring的@Configuration的封装:

    3. @EnableAutoConfiguration(主要部分)

      官方说明:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EBhOyP2q-1692088161167)(…/…/杂七杂八/Typora图片存储/image-20230815161726840.png)]

      翻译:

      ​ 启用Spring应用程序上下文的自动配置,尝试猜测和配置您可能需要的bean。自动配置类通常基于类路径和您定义的bean来应用。例如,如果您的类路径上有tomcat-embedded.jar,那么您可能想要一个TomcatServlet WebServerFactory(除非您定义了自己的Servlet WebServerFactoryBean)。当使用@SpringBootApplication时,上下文的自动配置会自动启用,因此添加此注释不会产生额外效果。自动配置试图尽可能地智能化,并将随着您定义更多自己的配置而退出。您总是可以手动排除()任何您永远不想应用的配置(如果您没有访问权限,请使用excludeName())。您也可以通过spring.autoconfig.exclude属性排除它们。自动配置总是在用户定义的bean注册后应用。

      ​ 用@EnableAutoConfiguration注释的类的包(通常通过@SpringBootApplication)具有特定的意义,通常用作“默认”。例如,它将在扫描@Entity类时使用。通常建议您将@EnableAutoConfiguration(如果您没有使用@SpringBootApplication)放在根包中,以便可以搜索所有子包和类。自动配置类是常规的Spring@Configurationbean。它们使用SpringFactoriesLoader机制进行定位(针对此类进行键控)。通常,自动配置bean是@Conditionalbean(最常用的是@ConditionalOnClass和@ConditionalOnMissingBean注释)。

二、各种锁的学习汇总以及实战经验

乐观锁,悲观锁,自旋锁?,偏向锁。

1、MySQL锁实战:

1.1 MySQL乐观锁

**复习:**乐观锁常用的算法为CAS(Compare And Sweep),该算法存在的问题ABA 问题,解决方案,获得锁时只能原子操作 + 1

确保pms_product 的 stock字段在扣值之前,保持读取判断时的值。

  • 考虑实现方案:CAS乐观锁

    具体过程:

  • MySQL行锁

    具体过程:

1.2 MySQL悲观锁

  • 表级锁
  • 行级锁

2、Java并发锁实战:

2.1、

三、项目实战经验

1、mall实战经验之基于SpringSecurity和JWT的token的用户权限管理以及用户登录注册管理

1.1、基于JWT的token用户登录管理

token原理:

所谓的Token,其实就是服务端生成的一串加密字符串、以作客户端进行请求的一个“令牌”

用户登录时服务器生成一个token发送给用户,用户再次登录时就只需要使用这个token进行登录,对于服务器来说也不需要维护session。

而JWT(JSON Web Tokens)是一种Token的实现方案:

一个JWT包含三个部分:头部,负载,签名

  • header(头部):是一个JSON对象,包含算法和token类型。

  • Payload(负载):用来存放实际需要传递的数据。

    前面五个字段都是JWT的标准所定义的,也支持自定义字段。

    • iss: 该JWT的签发者
    • sub:该JWT面向的用户
    • aud:接收该JWT的用户
    • exp:什么时候过期,这里是一个unix时间戳
    • iat:在什么时候签发的
    • [自定义字段] name : hello
  • Signature(签名):把前面两端拼接起来,并用算法和仅服务器指定的私钥加密,从而确保token不会被篡改。

    例如:HMACSHA256(base64UrlEncode(header) + “.” +base64UrlEncode(payload),secret)

生成的形式如下:

eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE2MTQ2Puk.v6cyfk0B1j7vbIqw_Q

1.2、基于SpringSecurity的用户认证和授权

  • SpringSecurity学习

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9DNrTFDn-1692088161167)(…/…/杂七杂八/Typora图片存储/image-20230806123456712.png)]

客户端向应用程序发送请求时,会通过一个Filter Chain, 其中的每个Filter可以完成如下工作

  • 防止下层的Filter被调用
  • 修改传给下层FilterHttpServerletRequestHttpServerletResponse
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
	// do something before the rest of the application
    chain.doFilter(request, response); // invoke the rest of the application
    // do something after the rest of the application
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rMUKtU3G-1692088161167)(…/…/杂七杂八/Typora图片存储/image-20230806124804115.png)]

Spring提供了一个叫DelegatingFilterProxyFilter实现,然后DelegatingFilterProxy会从ApplicationContext查找Bean Filter,对应的伪代码实现如下:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
	Filter delegate = getFilterBean(someBeanName);
	delegate.doFilter(request, response);
}
1延迟地获取被注册为Spring Bean的 Filter。 对于 DelegatingFilterProxy 中的例子,delegateBean Filter0 的一个实例。
2将工作委托给 Spring Bean。

DelegatingFilterProxy 的另一个好处是,它允许延迟查找 Filter Bean实例。这一点很重要,因为在容器启动之前,容器需要注册 Filter 实例。然而, Spring 通常使用 ContextLoaderListener 来加载 Spring Bean,这在需要注册 Filter 实例之后才会完成。

Spring Security 的 Servlet 支持包含在 FilterChainProxy 中。FilterChainProxy 是 Spring Security 提供的一个特殊的 Filter,允许通过 SecurityFilterChain 委托给许多 Filter 实例。由于 FilterChainProxy 是一个Bean,它通常被包裹在 DelegatingFilterProxy 中。

SecurityFilterChainFilterChainProxy 用来确定当前请求应该调用哪些 Spring Security Filter 实例。

总结:

  1. Spring Security的思路是基于拦截器链(Filter Chain)实现的,通过在客户端和服务器之间搭建起拦截器链实现了两个效果:防止一直不需要的请求进入到Serverlet 以及 修改对应的HttpServerletRequestHttpServerletResponse来实现一些特定的功能
  2. 由于Filter Chain是在Serverlet注册的同时就生成了,比ApplicationContext内容早,故Spring Sercurity通过在拦截器链中加入了一个DelegatingFilterProxy(先生成),再通过DelegatingFilterPorxy去延迟加载bean的思路
  3. DelegatingFilterProxy延迟加载的往往是FilterChainProxy这个bean。 FilterChainProxy作为Spring Security组件的核心和起点。,用来插入Security自有的FilterChain-----SecurityFilterChain
  4. SecurityFilerChain可以自定义,内部的 SecurityFiler是bean,基于SpringSecurity的自定义注册,登录,权限控制等功能都可以在其中实现。
  5. SecurityFiler是通过SecurityFilterChainAPI插入到FilterChainProxy中的。可以用于认证、登录、授权、漏洞保护等等。Filter是按照特定的顺序执行的,可以在FilterOrderRegistration的代码中查看Filter的执行顺序。
  • 认证功能:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-omRXhWk4-1692088161168)(…/…/杂七杂八/Typora图片存储/image-20230806131700199.png)]

tip:你可以从 OncePerRequestFilter 中继承,而不是实现 Filter,这是一个基类,用于每个请求只调用一次的 filter,并提供一个带有 HttpServletRequestHttpServletResponse 参数的 doFilterInternal 方法。

2、x项目实战学习之Mybatis持久层框架学习:

SqlSessionFactoryBuilder -> SqlSesstionFactory -> SqlSession

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
如果你想在Java后端开发学习,以下是一些建议可以考虑: 1. 深入学习Java高级特性:掌握Java的高级特性,如多线程编程、并发编程、Lambda表达式、函数式编程等,可以提升你的编程能力和代码质量。 2. 学习Linux基础知识:Linux是大数据开发中常用的操作系统,了解Linux的基本命令和操作可以帮助你更好地开发和调试。 3. 掌握大数据技术栈:大数据开发需要掌握一系列的技术和工具,包括Hadoop、HBase、Hive、Kafka、Storm、Scala、Python、Spark等。逐步学习和实践这些技术,可以帮助你构建大数据应用和行数据分析。 4. 学习机器学习算法:了解常用的机器学习算法,如分类、回归等,可以帮助你在大数据开发行数据挖掘和分析。 5. 实践项目经验:通过参与实际的项目开发,可以锻炼你的技术能力和解决问题的能力。可以尝试在实际项目中应用所学的技术和工具,积累项目经验。 总之,要学习Java后端开发中的大数据方向,需要系统地学习和实践相关的技术和工具。不断提升自己的技术能力和项目经验,可以帮助你在大数据领域取得更好的发展。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [Java面试题目,外包Java后端开发三年,算法太TM重要了](https://blog.csdn.net/m0_56662547/article/details/116647529)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [从 Java 后端开发,如何转到大数据开发(Hadoop/Kafka/Spark)?](https://blog.csdn.net/github_38592071/article/details/103826043)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值