Spring揭秘

Posted by 盈盈冲哥 on March 5, 2020
  • 三种注入方式的比较

    • 接口注入。从注入方式的使用上来说,接口注入是现在不甚提倡的一种方式,基本处于“退役状态”。因为它强制被注入对象实现不必要的接口,带有侵入性。而构造方法注入和setter方法注入则不需要如此。
    • 构造方法注入。这种注入方式的优点就是,对象在构造完成之后,即已进入就绪状态,可以马上使用。缺点就是,当依赖对象比较多的时候,构造方法的参数列表会比较长。而通过反射构造对象的时候,对相同类型的参数的处理会比较困难,维护和使用上也比较麻烦。而且在Java中,构造方法无法被继承,无法设置默认值。对于非必须的依赖处理,可能需要引入多个构造方法,而参数数量的变动可能造成维护上的不便。
    • setter方法注入。因为方法可以命名,所以setter方法注入在描述性上要比构造方法注入好一些。另外,setter方法可以被继承,允许设置默认值,而且有良好的IDE支持。缺点当然就是对象无法在构造完成后马上进入就绪状态。
  • Spring提供了两种容器类型:BeanFactory和ApplicationContext

    • BeanFactory: 默认采用延迟初始化策略。容器启动初期速度较快,所需要的资源有限。
    • ApplicationContext: 除了拥有BeanFactory的所有指出,ApplicationContext还提供了其他高级特性,比如事件发布、国际化信息支持等。默认初始化并绑定完成,要求更多的系统资源。
  • BeanFactory的对象注册与依赖绑定方式

    • 直接编码方式
    • 外部配置文件方式(Properties配置格式、XML配置格式)
    • 注解方式
  • IoC容器初始化流程

    Spring的IoC容器所起的作用,它会以某种方式加载Configuration Metadata(通常也就是XML格式的配置信息),然后根据这些信息绑定整个系统的对象,最终组装成一个可用的基于轻量级容器的应用系统。

    Spring的IoC容器实现以上功能的过程,基本上可以按照类似的流程划分为两个阶段,即容器启动阶段和Bean实例化阶段。

    1. 容器启动阶段

    容器启动伊始,首先会通过某种途径加载Configuration Metadata. 除了代码方式比较直接,在大部分情况下,容器需要依赖某些工具类对加载的Configuration Metadata进行解析和分析,并将分析后的信息编组为响应的BeanDefinition,最后把这些保存了bean定义必要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry,这样容器启动工作就完成了。

    总的来说,该阶段所作的工作可以认为是准备性的,重点更加侧重于对象管理信息的收集。当然,一些验证性或者辅助性的工作也可以在这个阶段完成。

    1. Bean实例化阶段

    经过第一阶段,现在所有的bean定义信息都通过BeanDefinition的方式注册到了Bean DefinitionRegistry中。当某个请求方法通过容器的getBean方法明确地请求某个对象,或者音依赖关系容器需要隐式地调用getBean方法时,就会触发第二阶段的活动。

    该阶段,容器会首先检查所请求的对象之前是否已经初始化。如果没有,则会根据注册的BeanDefinition所提供的信息实例化被请求对象,并为其注入依赖。如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它。当该对象装配完毕之后,容器会立即将其返回请求方使用。如果说第一阶段只是根据图纸装配生产线的话,那么第二阶段就是使用装配好的生产线来生产具体的产品了。

  • AOP走向现实

    • 静态AOP时代

      以AspectJ为代表。

      静态AOP的优点是,Aspect直接以Java字节码的形式编译到Java类中,Java虚拟即可以像通常一样加载Java类运行(因为编译完成的Aspect是完全符合Java类的规范的),不会对整个系统的运行造成任何的性能损失。

      缺点就是不够灵活。如果横切关注点需要改变织入到系统的位置,就需要重新修改Aspect定义文件,然后使用编译器重新编译Aspect并重新织入到系统中。

    • 动态AOP时代

      以Spring AOP为代表。

      Aspect跟Class一样最终以Class身份作为系统的一等公民存在,与静态AOP最大的不同就是,AOP的织入过程在系统运行开始之后进行,而不是预先编译到系统类中,而且织入信息大都采用外部XML文件格式保存。

      但动态AOP在引入灵活性以及易用性的同时,也会不可避免地引入相应的性能问题。因为动态AOP的实现产品大都在类加载或者系统运行期间,采用对系统字节码进行操作的方式来完成Aspect到系统的织入,难免会造成一定的运行时性能损失。但随着JVM版本的提升,对反射以及字节码操作技术的更好支持,这样的性能损失在逐渐减少,大多数情况下,这种性能损失是可以容忍的。

  • Java平台上的AOP实现机制

    • 动态代理

      动态代理机制可以在运行期间,为响应的接口动态生成对应的代理对象。可以将横切关注点逻辑封装到动态代理的InvocationHandler中。

      这种方式实现的唯一缺点或者优点就是,所有需要织入横切关注点逻辑的模块类都得实现响应的接口,因为动态代理机制只针对接口有效。

    • 动态字节码增强

      可以使用ASM或者CGLIB等Java工具库,在程序运行期间,动态构建字节码的class文件。

      可以为需要织入横切逻辑的模块类在运行期间,通过动态字节码增强技术,为这些系统模块类生成相应的子类,而将横切逻辑家都这些子类中,让应用程序在执行期间使用的是这些动态生成的子类,从而达到将横切逻辑织入系统的目的。

      使用动态字节码增强技术,即使模块类没有实现相应的接口,我们依然可以对其进行扩展,而不用像动态代理那样受限于接口。

  • 第一代Spring AOP

    • Order的作用

      Spring在处理同一Jointpoint处的多个Advisor时,实际上会按照指定的顺序和优先级来执行它们,顺序号决定优先级,顺序号越小,优先级越高,优先级排在前面的,将被优先执行。我们可以从0或者1开始,因为小于0的顺序号原则上由Spring AOP框架内部使用。默认情况下,如果我们不明确指定各个Advisor的执行顺序,那么Spring会按照它们的声明顺序来应用它们,最先声明的顺序号最小但优先级最大,其次次之。

    • 如何与ProxyFactory打交道

      使用ProxyFactory只需要指定如下两个最基本的东西。

      • 第一个时要对其进行织入的目标对象。
      • 第二个是Advisor。
  • Spring AOP二世

    • @AspectJ形式的Spring AOP

      • 有两种方式将Aspect定义织入整个目标对象类,实现对其符合Pointcut定义的Joinpoint(也就是方法执行)进行拦截。

        1. 编程方式织入
        2. 通过自动代理织入
      • @AspectJ形式Pointcut

        • @AspectJ形式Pointcut的声明方式

          • Pointcut Expression
          • Pointcut Signature
        • @AspectJ形式Pointcut表达式的标志符

          • execution
          • within只接受类型声明
          • args帮助捕获拥有指定参数类型、指定参数数量的方法级Joinpoint
      • @AspectJ形式的Advice

        • @Before
        • @AfterReturning
        • @AfterThrowing
        • @After (After finally)
        • @Around
    • 基于Schema的AOP

  • 使用Spring进行事务管理

    • 编程式事务管理

      • 使用TransactionTemplate进行编程式事务管理

        • 抛出unchecked exception
        • 将事务标记成rollBackOnly
    • 声明式事务管理

      • XML元数据驱动的声明式事务
      • 注解元数据驱动的声明式事务
  • 迈向Spring MVC的旅程

    • Sevlet

      Servlet运行于Web容器之内,提供了Session和对象声明周期管理等功能。

      弊病式Magic Servlet,开发人员会将各种逻辑混杂于一处,包括流程控制逻辑、视图显示逻辑、业务逻辑、数据访问逻辑等,这就在成了后去系统能以维护等一系列问题。

    • JSP

      为了能够将Servlet中的输入显然逻辑以独立的单元抽取出来,我们通常使用模板化的方法。JSP的提出,成为Java平台上开发Web应用程序事实上的模板化视图标准。

      使用Servlet处理Web请求,我们需要在web.xml文件中,注册相应的请求URL与具体的处理Servlet之间的一个映射关系。