• 八股文学习二(spring boot + mybatis)


    三. 架构

    1. spring boot

    Spring Boot是一个依靠大量注解实现自动化配置的全新框架。约定优于配置;独立运行的 Spring 项目,内嵌servlet容器;Spring Boot 框架内部已经实现了与Spring以及其他常用第三方库的整合连接,并提供了默认最优化的整合配置,使用时基本上不需要额外生成配置代码和XML配置文件。在需要自定义配置的情况下,Spring Boot更加提倡使用Java config(Java 配置类)替换传统的XML配置方式,这样更加方便查看和管理。

    (1) ioc & aop

     IOC: 使用Spring框架之后对象的实例不再由调用者来创建,而是直接由Spring容器来创建,Spring容器会负责控制程序之间的关系,而不是调用者的程序代码直接控制。控制权由应用程序转移到Spring容器,控制权发生了反转。

    AOP: 为开发者提供了一种描述横切关注点的机制,并能够自动将横切关注点织入到面向对象的软件系统中,从而实现了横切关注点的模块化。
    AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任,例如事务处理、日志管理、权限控制等,封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性

    实现AOP的主要设计模式就是动态代理。
    Spring的动态代理有两种:一是JDK的动态代理(只能代理接口);

    另一个是cglib动态代理(可以代理类和接口)。

    • JDK动态代理主要涉及java.lang.reflect包下的两个类:Proxy类和InvocationHandler接口。

    JDK代理实现的三个要点:

    ① 通过Java.lang.reflect.Proxy类来动态生成代理类;

    ② 代理类要实现InvocationHandler接口;

    ③ JDK代理只能基于接口进行动态代理的;

    • CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法去技术拦截所有的父类方法的调用,并顺势织入横切逻辑。

    CGLib和JDK的原理类似,也是通过方法去反射调用目标对象的方法。

    (2)spring boot统一异常处理

    Springboot对于异常的处理也做了不错的支持,它提供了一个 @ControllerAdvice注解以及 @ExceptionHandler注解,前者是用来开启全局的异常捕获,后者则是说明捕获哪些异常,对那些异常进行处理。

    1. @ControllerAdvice
    2. public class MyExceptionHandler {
    3. @ExceptionHandler(value =Exception.class)
    4. public String exceptionHandler(Exception e){
    5. System.out.println("发生了一个异常"+e);
    6. return e.getMessage();
    7. }
    8. }

     关于Spring的两三事:傻傻分不清楚的filter和interceptor - 知乎

    Spring系列之Filter and Interceptor - 简书

    spring事务实现原理

    Spring实现原理

    SpringBoot系列(九)统一异常处理与统一结果返回-腾讯云开发者社区-腾讯云

    (3)java注解

    【对线面试官】今天来聊聊Java注解

    1.注解语法

    常用的Java注解如下:

    1、@Deprecated – 所标注内容不再被建议使用;
    2、@Override – 只能标注方法,表示该方法覆盖父类中的方法;
    3、@Documented --所标注内容可以出现在javadoc中;
    4、@Inherited – 只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性;
    5、@Retention – 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性;
    6、@Target – 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性;
    7、@SuppressWarnings – 所标注内容产生的警告,编译器会对这些警告保持静默;
    8、@interface – 用于定义一个注解;

    java Annotation 的组成中,有 3 个非常重要的主干类。它们分别是:

    Annotation.java

    1. package java.lang.annotation;
    2. public interface Annotation {
    3.     boolean equals(Object obj);
    4.     int hashCode();
    5.     String toString();
    6.     Classextends Annotation> annotationType();
    7. }

    ElementType.java

    1. package java.lang.annotation;
    2. public enum ElementType {
    3.     TYPE,               /* 类、接口(包括注释类型)或枚举声明  */
    4.     FIELD,              /* 字段声明(包括枚举常量)  */
    5.     METHOD,             /* 方法声明  */
    6.     PARAMETER,          /* 参数声明  */
    7.     CONSTRUCTOR,        /* 构造方法声明  */
    8.     LOCAL_VARIABLE,     /* 局部变量声明  */
    9.     ANNOTATION_TYPE,    /* 注释类型声明  */
    10.     PACKAGE             /* 包声明  */
    11. }

    RetentionPolicy.java

    1. package java.lang.annotation;
    2. public enum RetentionPolicy {
    3.     SOURCE,            /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了  */
    4.     CLASS,             /* 编译器将Annotation存储于类对应的.class文件中。默认行为  */
    5.     RUNTIME            /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
    6. }

    source和class级别的注解需要继承AbstractProcessor,实现process方法实现我们的自定义注解处理逻辑,lombok的实现原理就在这。

    2.注解原理

    一个注解准确意义上来说,只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。

    而解析一个类或者方法的注解往往有两种形式,一种是编译期直接的扫描,一种是运行期反射。反射的事情我们待会说,而编译器的扫描指的是编译器在对 java 代码编译字节码的过程中会检测到某个类或者方法被一些注解修饰,这时它就会对于这些注解进行某些处理。

    典型的就是注解 @Override,一旦编译器检测到某个方法被修饰了 @Override 注解,编译器就会检查当前方法的方法签名是否真正重写了父类的某个方法,也就是比较父类中是否具有一个同样的方法签名。

    这一种情况只适用于那些编译器已经熟知的注解类,比如 JDK 内置的几个注解,而你自定义的注解,编译器是不知道你这个注解的作用的,当然也不知道该如何处理,往往只是会根据该注解的作用范围来选择是否编译进字节码文件,仅此而已。

    反射注解的工作原理:

    首先,我们通过键值对的形式可以为注解属性赋值,像这样:@Hello(value = "hello")。

    接着,你用注解修饰某个元素,编译器将在编译期扫描每个类或者方法上的注解,会做一个基本的检查,你的这个注解是否允许作用在当前位置,最后会将注解信息写入元素的属性表。

    然后,当你进行反射的时候,虚拟机将所有生命周期在 RUNTIME 的注解取出来放到一个 map 中,并创建一个 AnnotationInvocationHandler 实例,把这个 map 传递给它。

    最后,虚拟机将采用 JDK 动态代理机制生成一个目标注解的代理类,并初始化好处理器。

    那么这样,一个注解的实例就创建出来了,它本质上就是一个代理类,你应当去理解好 AnnotationInvocationHandler 中 invoke 方法的实现逻辑,这是核心。一句话概括就是,通过方法名返回注解属性值

    https://www.cnblogs.com/i-xq/p/13084959.html

    3.lombok注解实现原理

    通过 JDK 6 实现的 JSR 269: Pluggable Annotation Processing API (编译期的注解处理器) ,在编译期时把 Lombok 的注解转换为 Java 的常规方法的,我们可以通过继承 AbstractProcessor 类,重写它的 init() 和  process() 方法,实现一个简易版的 Lombok。但同时 Lombok 也存在这一些使用上的缺点,比如:降低了可调试性、可能会有兼容性等问题,因此我们在使用时要根据自己的业务场景和实际情况,来选择要不要使用 Lombok,以及应该如何使用 Lombok。

    我们知道 Java 的编译过程大致可以分为三个阶段:

    1. 解析与填充符号表
    2. 注解处理
    3. 分析与字节码生成

    编译过程如下图所示:
     

    编译流程.png


    而 Lombok 正是利用「注解处理」这一步进行实现的,Lombok 使用的是 JDK 6 实现的 JSR 269: Pluggable Annotation Processing API (编译期的注解处理器) ,它是在编译期时把 Lombok 的注解代码,转换为常规的 Java 方法而实现优雅地编程的。

    https://www.cnblogs.com/vipstone/p/12597756.html

    4.autowired 与 resource注解

    @Resource和@Autowired都是用来进行依赖注入的注解,但是它们有一些不同之处。

    @Autowired是Spring框架中的注解,它可以用来标注字段、构造函数、方法等,表示需要自动装配。它可以用来注入依赖的bean。如果有多个bean符合条件,可能会抛出异常。

    @Resource是Java自带的注解,它可以用来标注字段、方法等,表示需要自动装配。它可以用来注入依赖的bean。如果有多个bean符合条件,会按照名称进行匹配。

    总结:

    @Autowired 是 Spring 框架中的注解,用来标注需要自动装配的 bean。
    @Resource 是 Java 自带的注解,用来标注需要自动装配的 bean。
    @Autowired 是按类型装配,如果有多个同类型的 bean,会抛出异常; @Resource 是按名称装配,如果名称不存在,会使用类型装配。
    @Qualifier与@Autowired注解配合使用,通过名字挑选符合条件的。

    (4).spring依赖注入方式

    当我们在使用依赖注入的时候,通常有三种方式:

    1.通过构造器来注入;

    2.通过setter方法来注入;

    3.通过field变量来注入;

    https://www.cnblogs.com/chansblogs/p/8343930.html

    使用构造器注入的好处_构造注入的优点_AntChenxi的博客-CSDN博客

    (5)Spring bean的生命周期

    1.生命周期流程图

    Spring Bean的生命周期分为四个阶段:实例化 Instantiation --> 属性赋值 Populate --> 初始化 Initialization --> 销毁 Destruction

    生命周期1.png

    (1)实例化Bean:
    对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean

    (2)设置对象属性(依赖注入):

    实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成属性设置与依赖注入。

    (3)处理Aware接口:

    Spring会检测该对象是否实现了xxxAware接口,通过Aware类型的接口,可以让我们拿到Spring容器的一些资源:

    • 如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,传入Bean的名字;
    • 如果这个Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。
    • 如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
    • 如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;

    (4)BeanPostProcessor前置处理:

    如果想对Bean进行一些自定义的前置处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。

    (5)InitializingBean:

    如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法。

    (6)init-method:

    如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

    (7)BeanPostProcessor后置处理:

    如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

    (8)DisposableBean:

    当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;

    (9)destroy-method:

    最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

    2. 生命周期接口分类

    Bean的完整生命周期经历了各种方法调用,这些方法可以划分为以下几类:

    (1)Bean自身的方法:这个包括了Bean本身调用的方法和通过配置文件中的init-method和destroy-method指定的方法

    (2)Bean级生命周期接口方法:这个包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法

    (3)容器级生命周期接口方法:这个包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”。

    (4)工厂后处理器接口方法:这个包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工厂后处理器  接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。

    (6) @PostConstruct注解

    @PostConstruct是java5的时候引入的注解,指的是在项目启动的时候执行这个方法,也可以理解为在spring容器启动的时候执行,可作为一些数据的常规化加载,比如数据字典之类的。

    被@PostConstruct修饰的方法会在服务器加载Servle的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行

    bean的构造方法-----》属性赋值-----》BeanPostProcessor的postProcessBeforeInitialization方法-----》@PostConstruct注解修饰的方法-----》InitializingBean接口afterPropertiesSet方法---》Init-method方法----》BeanPostProcessor的postProcessAfterInitialization方法。

    Java面试题:Spring Bean的生命周期 - 简书

    2.mybatis

    MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。

    下图是MyBatis架构图:

    (1)mybatis-config.xml是Mybatis的核心配置文件,通过其中的配置可以生成SqlSessionFactory,也就是SqlSession工厂

    (2)基于SqlSessionFactory可以生成SqlSession对象

    (3)SqlSession是一个既可以发送SQL去执行,并返回结果,类似于JDBC中的Connection对象,也是Mybatis中至关重要的一个对象。

    (4)Executor是SqlSession底层的对象,用于执行SQL语句

    (5)MapperStatement对象也是SqlSession底层的对象,用于接收输入映射(SQL语句中的参数),以及做输出映射(即将SQL查询的结果映射成相应的结果)

    mybatis & ibatis

    batis 是 Mybatis 的前身,两者都是优秀的持久层框架。

    区别:

    1、mybatis 实现接口绑定,不需要具体接口实现类。但是需要在xml文件中 的 namespace 绑定具体的接口。

    这个实现原理是JDK的动态代理。

    ibatis 需要定义具体接口的实现,并且在接口实现中获取SqlMapClient以及绑定xml文件。

    2、XML文件不同

    1)mybatis用的是 标签,ibatis 是用 标签

    2)mybatis 支持 ognl 表达式,ibatis不支持

    3)mybatis 接收参数的方式 #{id} ${id}$ ,ibatis 是 #id# $id$  

    ps: #与$ 的区别是# 是作为参数传递,预编译使用

    $ 是作为sql 一部分拼接,会有sql注入的风险

    4)mybatis 参数类型是 parameterType,ibatis 参数类型是parameterClass

    5)动态语句写法不同

    6)mybatis 迭代用的是 foreach ,ibatis用的是iterator

    Mybatis(真的超级超级详细) - 知乎

    3.dubbo

    (1) dubbo架构

    为了学习如何使用Dubbo,首先我们要了解dubbo的架构是怎么样的,如上图所示,dubbo 的核心架构中,分了4个角色:注册中心、服务提供者、服务消费者、监控中心。它们的功能分别是:

    • Registry:注册中心,负责服务地址的注册和查找,服务的Provider和Consumer只在启动时与注册中心交互,注册中心通过长连接感知Provider的存在,在Provider出现宕机的时候,注册中心会立即推送相关事件通知consumer。
    • Provider:服务提供者。在它启动的时候,会向Registry进行注册操作,将自己的地址和相关配置信息封装成Url添加到Zookeeper中。
    • Consumer:服务消费者。在它启动时,会向Registry进行订阅操作。Comsumer和Provider建立的是长连接,且Comsumer会缓存Provider信息,所以一旦建立,即使注册中心宕机,也不会影响已运行的Provider和Consumer。
    • Monitor:监控中心。用于统计服务的调用次数和调用时间,监控中心宕机不会影响Provider和Consumer,只是丢失监控数据。

    (2)dubbo扩展机制

    dubbo支持的协议有:

    1、dubbo 默认协议:

    • 单一 TCP 长连接,Hessian 二进制序列化和 NIO 异步通讯
    • 适合于小数据包大并发的服务调用和服务消费者数远大于服务提供者数的情况
    • 不适合传送大数据包的服务

    2、rmi 协议:

    • 采用 JDK 标准的 java.rmi.* 实现,采用阻塞式短连接和 JDK 标准序列化方式
    • 如果服务接口继承了 java.rmi.Remote 接口,可以和原生 RMI 互操作
    • 因反序列化漏洞,需升级 commons-collections3 到 3.2.2版本或 commons-collections4 到 4.1 版本
    • 对传输数据包不限,消费者和传输者个数相当

    3、hessian 协议:

    • 底层 Http 通讯,Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现
    • 可与原生 Hessian 服务互操作
    • 通讯效率高于 WebService 和 Java 自带的序列化
    • 参数及返回值需实现 Serializable 接口,自定义实现 List、Map、Number、Date、Calendar 等接口
    • 适用于传输数据包较大,提供者比消费者个数多,提供者压力较大

    4、http 协议:

    • 基于 http 表单的远程调用协议,短连接,json 序列化
    • 对传输数据包不限,不支持传文件
    • 适用于同时给应用程序和浏览器 JS 使用的服务

    5、webservice 协议:

    • 基于 Apache CXF 的 frontend-simple 和 transports-http 实现,短连接,SOAP文本序列化
    • 可与原生 WebService 服务互操作
    • 适用于系统集成、跨语言调用

    6、thrift 协议:

    • 对 thrift 原生协议 [2] 的扩展添加了额外的头信息
    • 使用较少,不支持传 null 值

    7、基于 Redis实现的 RPC 协议

    8、基于 Memcached 实现的 RPC 协议

    官方文档:http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-protocol.html

    如何扩展协议? 基于dubbo扩展点机制

    图解Dubbo,六种扩展机制详解-51CTO.COM

    dubbo 大白话系列-扩展点机制 - 简书

    (3)dubbo异常处理

    基于filter进行统一异常处理

    @Activate(group = CommonConstants.PROVIDER, order = 2)

    Dubbo 统一业务异常处理 - 掘金

    Dubbo基本原理和用法讲解 - 知乎

    4.zookeeper

    ZooKeeper 基础知识基本分为三大模块:数据模型、ACL 权限控制、Watch 监控。

    数据模型是最重要的,很多 ZooKeeper 中典型的应用场景都是利用这些基础模块实现的。比如我们可以利用数据模型中的临时节点和 Watch 监控机制来实现一个发布订阅的功能。

    (1)数据模型

      数据模型就是 ZooKeeper 用来存储和处理数据的一种逻辑结构。ZooKeeper 数据模型最根本的功能就像一个数据库。

      ZooKeeper 中的数据模型是一种树形结构,非常像电脑中的文件系统,有一个根文件夹,下面还有很多子文件夹。ZooKeeper 的数据模型也具有一个固定的根节点(/),我们可以在根节点下创建子节点,并在子节点下继续创建下一级节点。ZooKeeper 树中的每一层级用斜杠(/)分隔开,且只能用绝对路径(如“get /work/task1”)的方式查询 ZooKeeper 节点,而不能使用相对路径。

     (2)ACL权限控制

    如何使用 ZooKeeper 的 ACL 机制来实现客户端对数据节点的访问控制?

      一个 ACL 权限设置通常可以分为 3 部分,分别是:权限模式(Scheme)、授权对象(ID)、权限信息(Permission)。最终组成一条例如“scheme:id:permission”格式的 ACL 请求信息。

    客户端在 ACL 权限请求发送过程的步骤比较简单:首先是封装该请求的类型,之后将权限信息封装到 request 中并发送给服务端。而服务器的实现比较复杂,首先分析请求类型是否是权限相关操作,之后根据不同的权限模式(scheme)调用不同的实现类验证权限最后存储权限信息。

    (3)Watch监控

    ZooKeeper 的客户端也可以通过 Watch 机制来订阅当服务器上某一节点的数据或状态发生变化时收到相应的通知,我们可以通过向 ZooKeeper 客户端的构造方法中传递 Watcher 参数的方式实现:

    new ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)
    客户端 Watch 注册实现过程

      我们先看一下客户端的实现过程,在发送一个 Watch 监控事件的会话请求时,ZooKeeper 客户端主要做了两个工作:标记该会话是一个带有 Watch 事件的请求、将 Watch 事件存储到 ZKWatchManager。

    服务端 Watch 注册实现过程

    Zookeeper 服务端处理 Watch 事件基本有 2 个过程:解析收到的请求是否带有 Watch 注册事件、将对应的 Watch 事件存储到 WatchManager。

      ZooKeeper 实现的方式是通过客服端和服务端分别创建有观察者的信息列表。客户端调用 getData、exist 等接口时,首先将对应的 Watch 事件放到本地的 ZKWatchManager 中进行管理。服务端在接收到客户端的请求后根据请求类型判断是否含有 Watch 事件,并将对应事件放到 WatchManager 中进行管理。

      在事件触发的时候服务端通过节点的路径信息查询相应的 Watch 事件通知给客户端,客户端在接收到通知后,首先查询本地的 ZKWatchManager 获得对应的 Watch 信息处理回调操作。这种设计不但实现了一个分布式环境下的观察者模式,而且通过将客户端和服务端各自处理 Watch 事件所需要的额外信息分别保存在两端,减少彼此通信的内容。大大提升了服务的处理性能。

    ZooKeeper - 知乎

    Zookeeper的分布式锁实现

    Zookeeper的应用场景

    5.gradle

    6.git

    git基本操作,一篇文章就够了! - 掘金

    git rebase,看这一篇就够了 - 掘金

    四. 设计模式

    图文详解 23 种设计模式

    设计模式23模式介绍_哪 吒的博客-CSDN博客

    Spring篇-Spring中使用了哪些设计模式 - 知乎

    五. 集合类学习

    六.其他

    (1)Spring支持五个作用域:singleton、prototype、request、session、global session

    1.singleton:默认作用域Spring IOC容器仅存在一个Bean实例,Bean以单例方式存在,在创建容器时就同时自动创建了一个Bean对象。作用域范围是ApplicationContext中。

    2.prototype:每次从容器中调用Bean时,都会返回一个新的实例,即每次调用getBean时。作用域返回是getBean方法调用直至方法结束。

    相当于执行newXxxBean().Prototype是原型类型,再我们创建容器的时候并没有实例化,而是当我们获取Bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。

    3.request:每次HTTP请求都会创建一个新的Bean,作用域范围是每次发起http请求直至拿到相应结果。该作用域仅适用于WebApplicationContext环境。

    4.session:首次http请求创建一个实例,作用域是浏览器首次访问直至浏览器关闭。

    同一个HTTP Session共享一个Bean,不同Session使用不通的Bean,仅适用于WebApplicationContext环境。

    5.global-session:作用域范围是WebApplicationContext中, 作用于集群环境的会话范围。一般用于Portlet应用环境,该运用域仅适用于WebApplicationContext环境。

    作用域范围比较:

    prototype < request < session < global-session < singleton

  • 相关阅读:
    网络学习:邻居发现协议NDP
    深入浅出零钱兑换问题——背包问题的套壳
    关于使用GB28181协议实现与大华摄像机的语音对讲功能小记
    【愚公系列】2022年11月 .NET CORE工具案例-.NET 7中的Quic通信
    2022国赛数学建模思路汇总A题B题C题D题 高教社杯
    华为略施小计,问界M7销量暴增10倍?
    临时记录一下
    雷达频带概述及其应用
    文旅媒体有哪些?如何邀请到现场报道?
    你知道Online DDL吗?
  • 原文地址:https://blog.csdn.net/xyr05288/article/details/132278278