Wetts's blog

Stay Hungry, Stay Foolish.

0%

Java-框架、相关技术-0-知识点汇总

框架

Spring

特点

  • 控制反转(IOC)
    • 注入方式
      • 构造器注入
      • setter 注入方式
      • 注解注入
      • 接口注入
    • 循环依赖
      • singletonObjects:第一级缓存,里面放置的是实例化好的单例对象;
      • earlySingletonObjects:第二级缓存,里面存放的是提前曝光的单例对象;
      • singletonFactories:第三级缓存,里面存放的是要被实例化的对象的对象工厂
      • 注入步骤
        • 创建 bean 的时候 Spring 首先从一级缓存 singletonObjects 中获取。
        • 如果获取不到,并且对象正在创建中,就再从二级缓存 earlySingletonObjects 中获取。
        • 如果还是获取不到就从三级缓存 singletonFactories 中取。(Bean 调用构造函数进行实例化后,即使属性还未填充,就可以通过三级缓存向外提前暴露依赖的引用值(提前曝光),根据对象引用能定位到堆中的对象,其原理是基于 Java的 引用传递),取到后从三级缓存移动到了二级缓存完全初始化之后将自己放入到一级缓存中供其他使用。
      • 因为加入 singletonFactories 三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。
        • 构造器循环依赖解决办法
          • 在构造函数中使用 @Lazy 注解延迟加载。在注入依赖时,先注入代理对象,当首次使用时再创建对象。
  • 面向切面编程(AOP)
    • 名词
      • 切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在 Spring AOP 中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
      • 连接点(Join point):指方法,在 Spring AOP 中,一个连接点总是代表一个方法的执行。应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
        • Spring 只支持方法级别的连接点
          • 因为 Spring 基于动态代理,所以 Spring 只支持方法连接点。Spring 缺少对字段连接点的支持,而且它不支持构造器连接点。方法之外的连接点拦截功能,我们可以利用 Aspect 来补充。
      • 通知(Advice):在 AOP 术语中,切面的工作被称为通知。
        • 5 种类型的通知
          • 前置通知(Before):在目标方法被调用之前调用通知功能;
          • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
          • 返回通知(After-returning):在目标方法成功执行之后调用通知;
          • 异常通知(After-throwing):在目标方法抛出异常后调用通知;
          • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
      • 切入点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
      • 引入(Introduction):引入允许我们向现有类添加新方法或属性。
      • 目标对象(Target Object):被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。也有人把它叫做被通知(adviced)对象。 既然 Spring AOP 是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
      • 织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入
        • 编译期:切面在目标类编译时被织入。AspectJ 的织入编译器是以这种方式织入切面的。
        • 类加载期:切面在目标类加载到 JVM 时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5 的加载时织入就支持以这种方式织入切面。
        • 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP 容器会为目标对象动态地创建一个代理对象。SpringAOP 就是以这种方式织入切面。
    • 通过动态代理方式实现
      • JDK 动态代理
        • 实现原理
          • 通过实现 InvocationHandlet 接口创建自己的调用处理器;
          • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理;
          • 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
          • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;
        • 是面向接口的代理模式,如果被代理目标没有接口那么 Spring 也无能为力,Spring 通过 Java 的反射机制生产被代理接口的新的匿名实现类,重写了其中 AOP 的增强方法。
      • CGLib 动态代理
        • Spring 在运行期间通过 CGlib 继承要被动态代理的类,重写父类的方法,实现 AOP 面向切面编程呢。CGLib 动态代理是通过字节码底层继承要代理类来实现(如果被代理类被 final 关键字所修饰,那么会失败)。
    • 配置 AOP 执行顺序
      • 通过实现 org.springframework.core.Ordered 接口
      • 通过 @Order() 注解
      • 通过配置文件配置

Spring 容器

  • 基础组件

    • BeanFactory

      • Spring 底层容器,定义了最基本的容器功能
      • BeanFactory 位于整个容器类体系结构的顶端,其基本实现类为 DefaultListableBeanFactory。之所以称其为核心容器,是因为该类容器实现 IOC 的核心功能:比如配置文件的加载解析,Bean 依赖的注入以及生命周期的管理等。BeanFactory 作为 Spring 框架的基础设施,面向 Spring 框架本身,一般不会被用户直接使用。
    • ApplicationContext

      • 扩展于 BeanFactory,拥有更丰富的功能。例如:添加事件发布机制、父子级容器,一般都是直接使用 ApplicationContext。

      • 通常译为应用上下文,不过称其为应用容器可能更形象些。它在 BeanFactory 提供的核心 IOC 功能之上作了扩展。通常 ApplicationContext 的实现类内部都持有一个 BeanFactory 的实例,IOC 容器的核心功能会交由它去完成。而 ApplicationContext 本身,则专注于在应用层对 BeanFactory 作扩展,比如提供对国际化的支持,支持框架级的事件监听机制以及增加了很多对应用环境的适配等。ApplicationContext 面向的是使用 Spring 框架的开发者。开发中经常使用的 ClassPathXmlApplicationContext 就是典型的 Spring 的应用容器,也是要进行解读的 IOC 容器。

      • 相关类

        • AbstractApplicationContext

          • ApplicationContext 接口的抽象实现类,能够自动检测并注册各种后置处理器(PostProcessor)和事件监听器(Listener),以模板方法模式定义了一些容器的通用方法,比如启动容器的真正方法 refresh() 就是在该类中定义的。

          • refresh() 方法

            • ```
              @Override
              public void refresh() throws BeansException, IllegalStateException {
              synchronized (this.startupShutdownMonitor) {

                // Prepare this context for refreshing.
                // 准备,记录容器的启动时间 startupDate,标记容器为激活,初始化上下文环境如文件路径信息,验证必填属性是否填写
                prepareRefresh();
              
                // Tell the subclass to refresh the internal bean factory.
                // 这步比较重要(解析),告诉子类去刷新 bean 工厂,这步完成后配置文件就解析成一个个 bean 定义,注册到 BeanFactory(但是未被初始化,仅将信息写到了 beanDefination 的 map 中)
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
              
                // Prepare the bean factory for use in this context.
                // 设置 beanFactory 类加载器,添加多个 beanPostProcesser
                prepareBeanFactory(beanFactory);
              
                try {
                  // Allows post-processing of the bean factory in context subclasses.
                  // 允许子类上下文中对 beanFactory 做后期处理
                  postProcessBeanFactory(beanFactory);
              
                  // Invoke factory processors registered as beans in the context.
                  // 调用 BeanFactoryPostProcessor 各个实现类的方法
                  invokeBeanFactoryPostProcessors(beanFactory);
              
                  // Register bean processors that intercept bean creation.
                  // 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
                  // 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
                  // 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
                  registerBeanPostProcessors(beanFactory);
              
                  // Initialize message source for this context.
                  //初始化 ApplicationContext 的 MessageSource
                  initMessageSource();
              
                  // Initialize event multicaster for this context.
                  // 初始化 ApplicationContext 事件广播器
                  initApplicationEventMulticaster();
              
                  // Initialize other special beans in specific context subclasses.
                  // 初始化子类特殊 bean(钩子方法)
                  onRefresh();
              
                  // Check for listener beans and register them.
                  // 注册事件监听器
                  registerListeners();
              
                  // Instantiate all remaining (non-lazy-init) singletons.
                  // 初始化所有 singleton bean
                  finishBeanFactoryInitialization(beanFactory);
              
                  // Last step: publish corresponding event.
                  // 广播事件,ApplicationContext 初始化完成
                  finishRefresh();
                }
              
                catch (BeansException ex) {
                  if (logger.isWarnEnabled()) {
                      logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                  }
              
                  // Destroy already created singletons to avoid dangling resources.
                  destroyBeans();
              
                  // Reset 'active' flag.
                  cancelRefresh(ex);
              
                  // Propagate exception to caller.
                  throw ex;
                }
              
                finally {
                  // Reset common introspection caches in Spring's core, since we
                  // might not ever need metadata for singleton beans anymore...
                  resetCommonCaches();
                }
              

              }
              }

              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              23
              24
              25
              26
              27
              28
              29
              30
              31
              32
              33
              34
              35
              36
              37
              38
              39
              40
              41
              42
              43
              44
              45
              46
              47
              48
              49
              50
              51
              52
              53
              54
              55
              56
              57
              58
              59
              60
              61
              62
              63
              64
              65
              66
              67
              68
              69
              70
              71
              72
              73
              74
              75
              76
              77
              78
              79
              80
              81
              82
              83
              84
              85
              86
              87
              88
              89
              90
              91
              92
              93
              94
              95
              96
              97
              98
              99
              100
              101
              102
              103
              104
              105
              106
              107
              108
              109
              110
              111
              112
              113
              114
              115
              116
              117
              118
              119
              120
              121
              122
              123
              124
              125
              126
              127
              128
              129
              130
              131
              132
              133
              134
              135
              136
              137
                    - AbstractRefreshableApplicationContext
              - 继承 AbstractApplicationContext 的抽象类。内部持有一个 DefaultListableBeanFactory 的实例,使得继承 AbstractRefreshableApplicationContext 的 Spring 的应用容器内部默认有一个 Spring 的核心容器,那么 Spring 容器的一些核心功能就可以委托给内部的核心容器去完成。
              - AbstractRefreshableApplicationContext 在内部定义了创建、销毁以及刷新核心容器 BeanFactory 的方法。
              - ClassPathXmlApplicationContext
              - 最常用的 Spring 的应用容器之一。在启动时会加载类路径下的 xml 文件作为容器的配置信息。
              - BeanFactoryPostProcessor
              - 实现该接口,可以在 Spring 的 bean 创建之前,修改 bean 的定义属性。也就是说,Spring 允许 BeanFactoryPostProcessor 在容器实例化任何其它 bean 之前读取配置元数据,并可以根据需要进行修改,例如可以把 bean 的 scope 从 singleton 改为 prototype,也可以把 property 的值给修改掉。可以同时配置多个 BeanFactoryPostProcessor,并通过设置'order'属性来控制各个 BeanFactoryPostProcessor 的执行次序。
              - BeanFactoryPostProcessor 是在 spring 容器加载了 bean 的定义文件之后,在 bean 实例化之前执行的。接口方法的入参是 ConfigurrableListableBeanFactory,使用该参数,可以获取到相关 bean 的定义信息。
              - Spring 中,有内置的一些 BeanFactoryPostProcessor 实现类,常用的有:
              - org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
              - org.springframework.beans.factory.config.PropertyOverrideConfigurer
              - org.springframework.beans.factory.config.CustomEditorConfigurer:用来注册自定义的属性编辑器
              - BeanPostProcessor
              - BeanPostProcessor,可以在 Spring 容器实例化 bean 之后,在执行 bean 的初始化方法前后,添加一些自己的处理逻辑。
              - 这里说的初始化方法,指的是下面两种:
              - bean 实现了 InitializingBean 接口,对应的方法为 afterPropertiesSet
              - 在 bean 定义的时候,通过 init-method 设置的方法
              - BeanPostProcessor 是在 Spring 容器加载了 bean 的定义文件并且实例化 bean 之后执行的。BeanPostProcessor 的执行顺序是在 BeanFactoryPostProcessor 之后。
              - Spring中,有内置的一些 BeanPostProcessor 实现类,例如:
              - org.springframework.context.annotation.CommonAnnotationBeanPostProcessor:支持 @Resource 注解的注入
              - org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor:支持 @Required 注解的注入
              - org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor:支持 @Autowired 注解的注入
              - org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor:支持 @PersistenceUnit 和 @PersistenceContext 注解的注入
              - org.springframework.context.support.ApplicationContextAwareProcessor:用来为 bean 注入 ApplicationContext 等容器对象
              - Resource
              - bean 配置文件,一般为 xml 文件。可以理解为保存 bean 信息的文件。
              - BeanDefinition
              - BeanDefinition 定义了 bean 的基本信息,根据它来创造 bean。
              - 容器启动步骤
              - ![Spring_容器启动的整个流程](Java-框架、相关技术-0-知识点汇总/Spring_容器启动的整个流程.png)
              1. 资源定位:找到配置文件
              2. BeanDefinition 载入和解析
              3. BeanDefinition 注册
              4. bean 的实例化和依赖注入
              - BeanFactory
              - BeanFactory 定义的标准处理顺序
              1. BeanNameAware's setBeanName
              2. BeanClassLoaderAware's setBeanClassLoader
              3. BeanFactoryAware's setBeanFactory
              4. ResourceLoaderAware's setResourceLoader (only applicable when running in an application context)
              5. ApplicationEventPublisherAware's setApplicationEventPublisher (only applicable when running in an application context)
              6. MessageSourceAware's setMessageSource (only applicable when running in an application context)
              7. ApplicationContextAware's setApplicationContext (only applicable when running in an application context)
              8. ServletContextAware's setServletContext (only applicable when running in a web application context)
              9. postProcessBeforeInitialization methods of BeanPostProcessors
              10. InitializingBean's afterPropertiesSet
              11. a custom init-method definition
              12. postProcessAfterInitialization methods of BeanPostProcessors
              - 第 9 点和 12 点是通过 BeanPostProccessor 接口进行处理的
              - 第 10 点是通过 InitializingBean 接口去实现的
              - BeanFactory 关闭的处理顺序
              1. DisposableBean's destroy
              2. a custom destroy-method definition

              #### Spring 事件监听机制
              - 说明
              - 事件监听机制可以理解为是一种观察者模式,有数据发布者(事件源)和数据接受者(监听器);
              - 在 Java 中,事件对象都是继承 java.util.EventObject 对象,事件监听器都是 java.util.EventListener 实例;
              - EventObject 对象不提供默认构造器,需要外部传递 source 参数,即用于记录并跟踪事件的来源;
              - Spring 事件
              - Spring 事件对象为 ApplicationEvent,继承 EventObject
              - Spring 事件监听器为 ApplicationListener,继承 EventListener
              - 实现 Spring 事件监听有两种方式:
              - 面向接口编程,实现 ApplicationListener 接口;
              - 基于注解驱动,@EventListener(Spring 自定义的注解);
              - Spring 自带的监听器
              - ContextLoaderListener
              - 在启动 Web 容器时,自动装配 Spring applicationContext.xml 的配置信息。
              - 因为它实现了 ServletContextListener 这个接口,在 web.xml 配置这个监听器,启动容器时,就会默认执行它实现的方法。在 ContextLoaderListener 中关联了 ContextLoader 这个类,所以整个加载配置过程由 ContextLoader 来完成。
              - RequestContextListener
              - Spring2.0 中新增了三个 web 作用域:request、session、global session,如果希望 web 容器中的某个 bean 具有新的作用域,除了在 bean 中配置相应的 scope 属性外,还需要在容器中进行额外的初始化配置。

              #### SpringMVC
              - MVC 模式代表 Model-View-Controller(模型-视图-控制器)模式。
              - 原理
              - ![SpringMVC_请求处理的流程](Java-框架、相关技术-0-知识点汇总/SpringMVC_请求处理的流程.png)
              - 执行流程
              - 用户向服务器发送请求,请求会到 DispatcherServlet,DispatcherServlet 对请求 URL 进行解析,得到请求资源标识符(URI),然后根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括一个 Handler 处理器对象、多个 HandlerInterceptor 拦截器对象),最后以 HandlerExecutionChain 对象的形式返回。
              - DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)。 在填充 Handler 的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:
              - HttpMessageConveter: 将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定的响应信息
              - 数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等
              - 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
              - 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Error 中
              - Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象;根据返回的 ModelAndView,选择一个适合的 ViewResolver 返回给 DispatcherServlet;ViewResolver 结合 Model 和 View,来渲染视图,最后将渲染结果返回给客户端。

              #### 注解
              - 注入相关
              - @Qualifier
              - 用处:当一个接口有多个实现的时候,为了指名具体调用哪个类的实现。
              - @Resource
              - 可以通过 byName 和 byType 的方式注入,默认先按 byName 的方式进行匹配,如果匹配不到,再按 byType 的方式进行匹配。
              - 由 JSR-250 提供
              - @Autowired
              - 按 byType 方式注入,如果按 byType 冲突或找不到的话可以用 @Qualifier 来找 byName
              - 由 spring 提供
              - @Inject
              - 由 JSR-330 提供
              - 注意
              - 使用前需要导入 jar 包——javax.inject;
              - 支持 @Primary 注解,而且因为没有精确匹配,@Primary 的优先级最高;
              - 不支持 required=false,即不能注入 null,如果找不到组件肯定报错;
              - 默认按照属性名跟 bean 的名称匹配查找,如果不存在,再按类型匹配查找。

              #### 事务
              - 事务传播机制
              - PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常⻅的选择。
              - PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
              - PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
              - PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
              - PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
              - PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
              - PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行。与 PROPAGATION_REQUIRED 类似的操作。
              - 事务原理
              - Spring 在扫描 bean 的时候会扫描方法上是否包含 @Transactional 注解,如果包含,Spring 会为这个 bean 动态地生成一个子类(即代理类,proxy),代理类是继承原来那个 bean 的。此时,当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用之前就会启动 transaction。
              - ```
              @Service
              class A{
              @Transactinal
              method b(){...}

              method a(){ //标记1
              b();
              }
              }

              //Spring扫描注解后,创建了另外一个代理类,并为有注解的方法插入一个startTransaction()方法:
              class proxy$A{
              A objectA = new A();
              method b(){ //标记2
              startTransaction();
              objectA.b();
              }

              method a(){ //标记3
              objectA.a(); //由于a()没有注解,所以不会启动transaction,而是直接调用A的实例的a()方法
              }
              }
  • @Transactional 注解事务不生效的情况

    • 数据库
      • 事务生效的前提是你的数据源得支持事务,比如 mysql 的 MyISAM 引擎就不支持事务,而 Innodb 支持事务
    • 类内部访问
      • 在一个 Service 内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调用,都不会开启新的事务。
        • 非事务方法 A 调用事务方法 B,方法 B 事务不生效
          • 因为 spring 采用动态代理机制来实现事务控制,而动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了
        • 在事务方法 A 中调用另外一个事务方法 B,被调用方法 B 的事务没起作用
          • spring 是通过代理代管理事务的,当在第一个方法 insertUser1 内直接调用 insertUser2 的时候 ,insertUser2 上的事务是不会起作用的(也就是 insertUser2 是没有开启事务)
    • 私有方法
      • 在私有方法上,添加 @Transactional 注解也不会生效,私有方法外部不能访问,所以只能内部访问,上面的 case 不生效,这个当然也不生效了
    • 异常不匹配
      • @Transactional 注解默认处理运行时异常,即只有抛出运行时异常时,才会触发事务回滚,否则并不会如
    • 多线程
      • 在标记事务的方法内部,另起子线程执行 db 操作,此时事务同样不会生效
    • 传播属性
      • 几种传播方式是不走事务执行的

Bean

  • Bean 的作用域
    • singleton : bean 在每个 Spring ioc 容器中只有一个实例。
    • prototype:一个 bean 的定义可以有多个实例。
    • request:每次 http 请求都会创建一个 bean,该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效。
    • session:在一个 HTTP Session 中,一个 bean 定义对应一个实例。该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效。
    • global-session:在一个全局的 HTTP Session 中,一个 bean 定义对应一个实例。该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效。
  • Bean 的生命周期
    • Spring_bean的生命周期
    1. Spring 启动,查找并加载需要被 Spring 管理的 Bean,进行 Bean 的实例化
    2. Bean 实例化后对将 Bean 的引入和值注入到 Bean 的属性中
    3. 如果 Bean 实现了 BeanNameAware 接口的话,Spring 将 Bean 的 Id 传递给 setBeanName() 方法
    4. 如果 Bean 实现了 BeanFactoryAware 接口的话,Spring 将调用 setBeanFactory() 方法,将 BeanFactory 容器实例传入
    5. 如果 Bean 实现了 ApplicationContextAware 接口的话,Spring 将调用 Bean 的 setApplicationContext() 方法,将 Bean 所在应用上下文引用传入进来。
    6. 如果 Bean 实现了 BeanPostProcessor 接口,Spring 就将调用他们的 postProcessBeforeInitialization() 方法。
    7. 如果 Bean 实现了 InitializingBean 接口,Spring 将调用他们的 afterPropertiesSet() 方法。类似的,如果 Bean 使用 init-method 声明了初始化方法,该方法也会被调用
    8. 如果 Bean 实现了 BeanPostProcessor 接口,Spring 就将调用他们的 postProcessAfterInitialization() 方法。
    9. 此时,Bean 已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
    10. 如果 Bean 实现了 DisposableBean 接口,Spring 将调用它的 destory() 接口方法,同样,如果 Bean 使用了 destory-method 声明销毁方法,该方法也会被调用。
  • 获取 bean 流程
    • Java_Spring_获取bean流程
  • 相关问题
    • Spring 框架中的单例 bean 是线程安全的吗?
      • 不是,Spring 框架中的单例 bean 不是线程安全的。
      • spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。
      • 实际上大部分时候 spring bean 无状态的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean() 了,所以就可以保证线程安全了。

用法

  • application.yml 中的 MySQL 密码可以通过第三方包加密(jasypt)

相关问题

  • Spring 框架中都用到了哪些设计模式?
    • 工厂模式:BeanFactory 就是简单工厂模式的体现,用来创建对象的实例;
    • 单例模式:Bean 默认为单例模式。
    • 代理模式:Spring 的 AOP 功能用到了 JDK 的动态代理和 CGLIB 字节码生成技术;
    • 模板方法:用来解决代码重复的问题。比如:RestTemplate, JmsTemplate, JpaTemplate。
    • 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如 Spring 中 listener 的实现 ApplicationListener。
  • Spring BeanFactory 与 FactoryBean 的区别
    • BeanFactory
      • BeanFactory,以 Factory 结尾,表示它是一个工厂类(接口),用于管理 Bean 的一个工厂。在 Spring 中,BeanFactory 是 IOC 容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
      • Spring 为我们提供了许多易用的 BeanFactory 实现,XmlBeanFactory 就是常用的一个,该实现将以 XML 方式描述组成应用的对象及对象间的依赖关系。XmlBeanFactory 类将持有此 XML 配置元数据,并用它来构建一个完全可配置的系统或应用。
    • FactoryBean
      • 以 Bean 结尾,表示它是一个 Bean,不同于普通 Bean 的是:它是实现了 FactoryBean<T> 接口的 Bean,根据该 Bean 的 ID 从 BeanFactory 中获取的实际上是 FactoryBean 的 getObject() 返回的对象,而不是 FactoryBean 本身,如果要获取F actoryBean 对象,请在 id 前面加一个 & 符号来获取。
      • 例如自己实现一个 FactoryBean,功能:用来代理一个对象,对该对象的所有方法做一个拦截,在调用前后都输出一行 LOG,模仿 ProxyFactoryBean 的功能。
    • BeanFactory 是个 Factory,也就是 IOC 容器或对象工厂,FactoryBean 是个 Bean。在 Spring 中,所有的 Bean 都是由 BeanFactory(也就是 IOC 容器)来进行管理的。但对 FactoryBean 而言,这个 Bean 不是简单的 Bean,而是一个能生产或者修饰对象生成的工厂 Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。
  • 如何指定 bean 的初始化顺序?
    • 构造方法依赖
      • 是最简单也是最常见的使用姿势,但是在使用时,需要注意循环依赖等问题。
      • bean 的注入方式之中,有一个就是通过构造方法来注入,借助这种方式,我们可以解决有优先级要求的 bean 之间的初始化顺序。
      • 虽然这种方式比较直观简单,但是有几个限制
        • 需要有注入关系,如 CDemo2 通过构造方法注入到 CDemo1 中,如果需要指定两个没有注入关系的 bean 之间优先级,则不太合适(比如我希望某个 bean 在所有其他的 Bean 初始化之前执行)
        • 循环依赖问题,如过上面的 CDemo2 的构造方法有一个 CDemo1 参数,那么循环依赖产生,应用无法启动
      • 另外一个需要注意的点是,在构造方法中,不应有复杂耗时的逻辑,会拖慢应用的启动时间
    • @DependOn 注解
      • 这是一个专用于解决 bean 的依赖问题,当一个 bean 需要在另一个 bean 初始化之后再初始化时,可以使用这个注解
      • 在使用这个注解的时候,有一点需要特别注意,它能控制 bean 的实例化顺序,但是 bean 的初始化操作(如构造 bean 实例之后,调用 @PostConstruct 注解的初始化方法)顺序则不能保证
        • 完整的 bean 创建,分成了两块顺序
          • 实例化(调用构造方法)
          • 初始化(注入依赖属性,调用 @PostConstruct 方法)
    • BeanPostProcessor 扩展
      • 非典型的使用方式,如非必要,请不要用这种方式来控制 bean 的加载顺序
  • Spring 如何统计 bean 的数量?
    • 通过实现 BeanFactoryPostProcessor 接口,调用 ConfigurableListableBeanFactory 的 getBeanDefinitionCount() 方法。

SpringBoot

  • maven
    • spring-boot-starter-parent
      • 通过这个作为 parent,可以继承其中定义的配置、各项依赖以及版本号
      • 在本项目中添加依赖时,可以省略版本号

MyBatis

  • 介绍
    • MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
  • 语法
    • #{}${}
      • #{}:使用的是预编译,对应 JBDC 中的 PreparedStatement
      • ${}:不会修改或者转义字符换,直接输出变量值
        • 会引发 SQL 注入问题
  • SqlSession
    • SqlSession 是 Mybatis 最重要的构建之一,可以简单的认为 Mybatis 一系列的配置目的是生成类似 JDBC 生成的 Connection 对象的 SqlSession 对象,这样才能与数据库开启“沟通”,通过 SqlSession 可以实现增删改查(当然现在更加推荐是使用 Mapper 接口形式)。
  • 工作原理
    • MyBatis_工作原理
    1. 读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
    2. 加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
    3. 构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
    4. 创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
    5. Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
    6. MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
    7. 输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
    8. 输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。
  • 一级缓存与二级缓存
    • MyBatis_缓存
    • 一级缓存
      • 该级缓存默认开启,不能关闭;
      • 该级缓存为 SqlSession 级别的缓存,也称为本地缓存;
      • 以下 4 种情况将会导致该级缓存失效:
        • 在不同 SqlSession 中查询数据;
        • 相同 SqlSession 中查询数据,但查询条件不同
        • 相同 SqlSession 中查询数据,但两次查询之间执行了增删改操作
        • 同 SqlSession 中查询数据,但第二次查询前,程序调用 SqlSession 对象 clearCache() 方法手动清除了一级缓存
      • 原理
        • 第一次发出一个查询 sql,sql 查询结果写入 sqlsession 的一级缓存中,缓存使用的数据结构是一个 map。
          • key:MapperID+offset+limit+Sql+所有的入参
          • value:用户信息
    • 二级缓存
      • 该级缓存默认不开启,但如果使用二级缓存需要在每个 XML 映射文件中添加 <cache></cache> 以配置该级缓存(相应实体类要序列化)。二级缓存可以通过在全局配置文件配置 setting 标签来关闭该级缓存。
        • 如果这样配置的话,很多其他的配置就会被默认进行,如:
          • 映射文件所有的 select 语句会被缓存
          • 映射文件的所有的 insert、update 和 delete 语句会刷新缓存
          • 缓存会使用默认的 Least Recently Used(LRU,最近最少使用原则)的算法来回收缓存空间
          • 根据时间表,比如 No Flush Interval,(CNFI,没有刷新间隔),缓存不会以任何时间顺序来刷新
          • 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用
          • 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以很安全的被调用者修改,不干扰其他调用者或县城所作的潜在修改
      • 该级缓存为 namespace 级别的缓存
      • 工作机制:通过 SqlSession 查询数据,这些数据将会放到当前会话的一级缓存中;如果当前会话关闭,则一级缓存中的数据会被保存到二级缓存中,此后新的 SqlSession 将从二级缓存中查找数据;
      • select 标签的 useCache 属性用于设置是否使用二级缓存;insert、update、delete 或 select 标签均有 flushCache 属性,其中增删改默认true,即 sql 执行以后,会同时清空一级和二级缓存,查询默认 false。
      • 为了提高扩展性,MyBatis 定义了 Cache 缓存接口,可以通过实现该缓存接口自定义二级缓存
  • Mybatis 的分页原理
    • Mybatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页,可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
    • 分页插件的原理就是使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内,拦截待执行的 SQL,然后根据设置的 dialect(方言),和设置的分页参数,重写 SQL,生成带有分页语句的 SQL,执行重写后的 SQL,从而实现分页。
  • 相关问题
    • Mybatis 为什么出现?为什么不是直接使用 jdbc?
      • JDBC 操作数据库的步骤
        • 注册驱动;
        • 获取数据库连接;
        • 拼接sql语句,设置sql参数;
        • 执行sql语句;
        • sql返回结果;
        • 执行语句和数据库连接;
      • 直接用 JDBC 每次都要做大量的相同的操作,并且还要对执行 sql 语句过程中所出现的各种异常和资源释放进行处理,而真正涉及到业务功能的代码其实很少,这明显影响了效率。
      • 再有就是当你接收数据库返回的结果集的时候,需要赋值给程序中的实体,使用 JDBC 需要你手动写代码去遍历每一条记录赋值给对应的实体 list 集合中,使用 JDBC 意味着需要更多的代码来提取结果并将它们映射到对象实例中,
      • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
    • 通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应,请问,这个 Dao 接口的工作原理是什么?Dao 接口里的方法,参数不同时,方法能重载吗?
      • Dao 接口,就是人们常说的 Mapper 接口,接口的全限名,就是映射文件中的 namespace 的值,接口的方法名,就是映射文件中 MappedStatement 的 id 值,接口方法内的参数,就是传递给 sql 的参数。Mapper 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个 MappedStatement,举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到 namespace 为 com.mybatis3.mappers.StudentDao 下面 id = findStudentById 的 MappedStatement。在 Mybatis 中,每一个 <select><insert><update><delete>标签,都会被解析为一个 MappedStatement 对象。
      • Dao 接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
      • Dao 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK 动态代理为 Dao 接口生成代理 proxy 对象,代理对象 proxy 会拦截接口方法,转而执行 MappedStatement 所代表的 sql,然后将 sql 执行结果返回。
    • 简述 MyBatis 的插件运行原理,以及如何编写一个插件?
      • Mybatis 仅可以编写针对 ParameterHandler、ResultSetHandler、StatementHandler、Executor 这 4 种接口的插件,Mybatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler 的 invoke() 方法,当然,只会拦截那些你指定需要拦截的方法。实现 Mybatis 的 Interceptor 接口并复写 intercept() 方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,还需要在配置文件中配置你编写的插件。
        • StatementHandler
          • StatementHandler 是数据库会话器,专门用来处理数据库会话的。StatementHandler 内运用了适配器模式和策略模式的思想
        • ResultSetHandler
          • ResultHandler 是一个结果处理器,StatementHandler 完成了查询之后,最终就是通过 ResultHandler 来实现结果集映射,ResultSetHandler 接口中只定义了3个方法用来处理结果。
          • ResultHandler 也默认提供了一个实现类:DefaultResultSetHandler。一般我们平常用的最多的就是通过 handleResultSets 来实现结果集转换。
        • ParameterHandler
          • ParameterHandler 是一个参数处理器,主要是用来对预编译语句进行参数设置的,只有一个默认实现类 DefaultParameterHandler。
          • ParameterHandler 中只定义了两个方法,一个获取参数,一个设置参数。
        • Executor
          • Executor 就是真正用来执行 Sql 语句的对象,我们调用 SqlSession 中的方法,最终实际上都是通过 Executor 来完成的。

ShardingSphere-JDBC

  • Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使用的产品组成。它们均提供标准化的数据水平扩展、分布式事务和分布式治理等功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。

Netty

  • 介绍
    • Netty 是一个 基于 NIO 的 client-server(客户端服务器)框架,使用它可以快速简单地开发网络应用程序。
    • 它极大地简化并优化了 TCP 和 UDP 套接字服务器等网络编程,并且性能以及安全性等很多方面甚至都要更好。
    • 支持多种协议,如 FTP、SMTP、HTTP 以及各种二进制和基于文本的传统协议。
    • 很多开源项目比如我们常用的 Dubbo、RocketMQ、ElasticSearch、gRPC 等等都用到了 Netty。
  • 本质:JBoss 做的一个 Jar 包
  • 目的:快速开发高性能、高可靠性的网络服务器和客户端程序
  • 优点
    • 统一的 API,支持多种传输类型,阻塞和非阻塞的。
    • 简单而强大的线程模型。
    • 自带编解码器解决 TCP 粘包/拆包问题。
    • 自带各种协议栈。
    • 真正的无连接数据包套接字支持。
    • 比直接使用 Java 核心 API 有更高的吞吐量、更低的延迟、更低的资源消耗和更少的内存复制。
    • 安全性不错,有完整的 SSL/TLS 以及 StartTLS 支持。
    • 社区活跃
    • 成熟稳定,经历了大型项目的使用和考验,而且很多开源项目都使用到了 Netty, 比如我们经常接触的 Dubbo、RocketMQ 等等。
  • 应用场景
    • 作为 RPC 框架的网络通信工具
      • 我们在分布式系统中,不同服务节点之间经常需要相互调用,这个时候就需要 RPC 框架了。不同服务节点之间的通信是如何做的呢?可以使用 Netty 来做。比如我调用另外一个节点的方法的话,至少是要让对方知道我调用的是哪个类中的哪个方法以及相关参数吧!
    • 实现一个自己的 HTTP 服务器
      • 通过 Netty 我们可以自己实现一个简单的 HTTP 服务器,这个大家应该不陌生。说到 HTTP 服务器的话,作为 Java 后端开发,我们一般使用 Tomcat 比较多。一个最基本的 HTTP 服务器可要以处理常见的 HTTP Method 的请求,比如 POST 请求、GET 请求等等。
    • 实现一个即时通讯系统
      • 使用 Netty 我们可以实现一个可以聊天类似微信的即时通讯系统。
    • 实现消息推送系统
      • 市面上有很多消息推送系统都是基于 Netty 来做的。
  • 架构
    • Netty_架构图
      • 绿色的部分 Core 核心模块,包括零拷贝、API 库、可扩展的事件模型。
      • 橙色部分 Protocol Support 协议支持,包括 Http 协议、webSocket、SSL(安全套接字协议)、谷歌 Protobuf 协议、zlib/gzip 压缩与解压缩、Large File Transfer 大文件传输等等。
      • 红色的部分 Transport Services 传输服务,包括 Socket、Datagram、Http Tunnel 等等。
    • 构成部分
      • Channel
        • NIO 基本结构,代表一个用于连接到实体如硬件设备、文件、网络套接字或程序组件,能否执行一个或多个不同的 I/O 操作的开放连接。
        • 比较常用的 Channel 接口实现类是 NioServerSocketChannel(服务端)和 NioSocketChannel(客户端),这两个 Channel 可以和 BIO 编程模型中的 ServerSocket 以及 Socket 两个概念对应上。Netty 的 Channel 接口所提供的 API,大大地降低了直接使用 Socket 类的复杂性。
      • Future
        • 提供了另一种通知应用操作已经完成的方式,这个对象作为一个一步操作结果的占位符,他将在将来的某个时候完成并提交结果。
        • Netty 提供自己的实现,ChannelFuture,用于执行异步操作时使用。每个 Netty 的 outbound I/O 操作都会返回一个 ChannelFuture,这样就不会阻塞,这便是 Netty 所谓的“自底向上的异步和事件驱动”。相关实现的步骤如下:
          1. 异步连接到远程对等节点,调用立即返回并提供 ChannelFuture;
          2. 操作完成后通知注册一个 ChannelFutureListener;
          3. operationComplete() 调用时检查操作的状态;
          4. 如果成功就创建一个 ByteBuf 来保存数据;
          5. 异步发送数据到远程,再次返回 ChannelFuture;
          6. 如果有一个错误则抛出 Throwable,描述错误原因。
        • 相关类
          • ChannelFuture
            • Netty 是异步非阻塞的,所有的 I/O 操作都为异步的。
            • 因此,我们不能立刻得到操作是否执行成功,但是,你可以通过 ChannelFuture 接口的 addListener() 方法注册一个 ChannelFutureListener,当操作执行成功或者失败时,监听就会自动触发返回结果。
      • Event 和 Handle
        • Netty 使用不同的事件来通知我们更改的状态或操作的状态,这使我们能够根据发声的事件触发适当的行为。
        • 这些行为可能包括:日志、数据转换、流控制、应用程序逻辑,由于 Netty 是一个网络框架,事件很清晰的跟入栈或出出站数据流相关,因为一些事件可能触发的传入的数据或状态的变化包括:活动或非活动连接、数据的读取、用户事件、错误,出站事件是由于在未来操作将触发的一个动作,这些包括:打开或关闭一个连接到远程、写或冲刷数据到 socket。
        • 每个事件都可以分配给用户实现处理程序类的方法,这些范例可直接转换为应用程序构建块。
        • Netty 的 ChannelHandler 是各种处理程序的基本抽象,每个处理器实例就是一个回调,用于执行各种事件的响应。
        • 相关类
          • ChannelHandler
            • ChannelHandler 是消息的具体处理器。他负责处理读写操作、客户端连接等事情。
          • ChannelPipeline
            • ChannelPipeline 为 ChannelHandler 的链,提供了一个容器并定义了用于沿着链传播入站和出站事件流的 API 。当 Channel 被创建时,它会被自动地分配到它专属的 ChannelPipeline。
            • 我们可以在 ChannelPipeline 上通过 addLast() 方法添加一个或者多个 ChannelHandler ,因为一个数据或者事件可能会被多个 Handler 处理。当一个 ChannelHandler 处理完之后就将数据交给下一个 ChannelHandler 。
          • EventLoop
            • EventLoop 定义了 Netty 的核心抽象,用于处理连接的生命周期中所发生的事件。
            • EventLoop 的主要作用实际就是负责监听网络事件并调用事件处理器进行相关 I/O 操作的处理。
            • Channel 为 Netty 网络操作(读写等操作)抽象类,EventLoop 负责处理注册到其上的 Channel 处理 I/O 操作,两者配合参与 I/O 操作。
          • EventLoopGroup
            • EventLoopGroup 包含多个 EventLoop(每一个 EventLoop 通常内部包含一个线程),上面我们已经说了 EventLoop 的主要作用实际就是负责监听网络事件并调用事件处理器进行相关 I/O 操作的处理。
            • 并且 EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理,即 Thread 和 EventLoop 属于 1 : 1 的关系,从而保证线程安全。
      • 其他
        • Bootstrap 和 ServerBootstrap
          • Bootstrap 是客户端的启动引导类/辅助类
          • ServerBootstrap 客户端的启动引导类/辅助类
  • Netty 的线程模型
    • 对于网络请求一般可以分为两个处理阶段,一是接收请求任务,二是处理网络请求。根据不同阶段处理方式分为以下几种线程模型:
      • 串行化处理模型
        • Netty_线程模型_串行化处理模型
        • 这个模型中用一个线程来处理网络请求连接和任务处理,当 worker 接受到一个任务之后,就立刻进行处理,也就是说任务接受和任务处理是在同一个 worker 线程中进行的,没有进行区分。这样做存在一个很大的问题是,必须要等待某个 task 处理完成之后,才能接受处理下一个 task。
        • 因此可以把接收任务和处理任务两个阶段分开处理,一个线程接收任务,放入任务队列,另外的线程异步处理任务队列中的任务。
      • 并行化处理模型
        • Netty_线程模型_并行化处理模型
        • 由于任务处理一般比较缓慢,会导致任务队列中任务积压长时间得不到处理,这时可以使用线程池来处理。可以通过为每个线程维护一个任务队列来改进这种模型。
    • Netty 具体线程模型
      • Reactor 模式基于事件驱动,采用多路复用将事件分发给相应的 Handler 处理,非常适合处理海量 IO 的场景。
      • Netty_线程模型
      • 每个 NioEventLoop 都绑定了一个 Selector,所以在 Netty 的线程模型中,是由多个 Selecotr 在监听 I/O 就绪事件。而 Channel 注册到 Selector。
      • 一个 Channel 绑定一个 NioEventLoop,相当于一个连接绑定一个线程,这个连接所有的 ChannelHandler 都是在一个线程中执行的,避免了多线程干扰。更重要的是 ChannelPipline 链表必须严格按照顺序执行的。单线程的设计能够保证 ChannelHandler 的顺序执行。
      • 一个 NioEventLoop 的 selector 可以被多个 Channel 注册,也就是说多个 Channel 共享一个 EventLoop。EventLoop 的 Selecctor 对这些 Channel 进行检查。
      • 相关问题
        • 如何理解 NioEventLoop 和 NioEventLoopGroup
          • NioEventLoop 实际上就是工作线程,可以直接理解为一个线程。NioEventLoopGroup 是一个线程池,线程池中的线程就是 NioEventLoop。
          • 实际上 bossGroup 中有多个 NioEventLoop 线程,每个 NioEventLoop 绑定一个端口,也就是说,如果程序只需要监听 1 个端口的话,bossGroup 里面只需要有一个 NioEventLoop 线程就行了。
  • Netty 工作原理
    • server 端工作原理
      • Netty_Server端工作原理
    • client 端工作原理
      • Netty_Client端工作原理
  • Netty 的零拷贝
    • 零复制(英语:Zero-copy;也译零拷贝)技术是指计算机执行操作时,CPU 不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省 CPU 周期和内存带宽。
    • 在 OS 层面上的 Zero-copy 通常指避免在用户态(User-space)与内核态(Kernel-space)之间来回拷贝数据。而在 Netty 层面,零拷贝主要体现在对于数据操作的优化。
    • Netty 中的零拷贝体现在以下几个方面:
      • 使用 Netty 提供的 CompositeByteBuf 类, 可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝。
      • ByteBuf 支持 slice 操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, 避免了内存的拷贝。
      • 通过 FileRegion 包装的 FileChannel.tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel, 避免了传统通过循环 write 方式导致的内存拷贝问题。