指定学习目标,简单讲讲spring的ioc和aop

目标

  • spring ioc
  • spring aop
  • spring 注解

spring学习版本

org.springframework:spring-context:3.2.18.RELEASE
作为3.x系列最后一个版本,3.2.18支持的ioc、aop已经很完整,也支持注解。为避免新版本造成的复杂(听说对大部分人来讲,并不复杂),(疲于精力和脑力)所以选该版本进行造轮子。

开篇前讲讲spring的logo吧,aop和ioc。不急,先看看这两个大概是个什么东吸。

什么是ioc

  • 翻译和历史
    ioc全名:Inversion of Control,控制反转;
    软件教父Martin Fowler更形象称之为-DI:Dependency Injection,依赖注入。
  • 简单说说ioc有什么用吧
    • 如果一个java对象依赖另一个java对象,那么你只需简单声明一下a依赖b(三种配置信息声明),容器就可以把这个依赖自动设置好,这个容器就是Spring (ioc)容器。
    • 参考上面的图:
      1. 业务类提供给容器类的信息,spring容器就可以实例化我们的业务类;
      2. spring容器根据配置信息进行装配,配置信息里我们需要设定一个类对另一个类到底是怎么依赖的,是set注入、构造器还是注解注入,创建的实例是单例还是多例等等。
      3. 容器读取这些配置信息后就可以自动把依赖配置好,我们从容器中取bean得时候就一切都准备好了(易于反掌,为所欲为)

配置信息

业务类的配置信息支持xml、注解、配置类三种方式。

  • 在基于xml的配置信息中依赖了accountDao和itemDao这两个bean,如上面1示
  • 2是基于注解的方式配置依赖
  • 3是基于java的spring配置,也就是javabaseconfiguration配置类

什么是aop

aop的起源也是现实的编程问题,比如下面的例子:

一个系统通常分为两大模块:功能性需求模块和非功能性需求模块,比如上面的用户订单支付这三个模块都是功能性需求,每个功能性模块都可能需要调用非功能的模块,比如日志、安全、事务等等,如下图:


ps:我们可以功能与非功能模块配合使用的过程比作“下地干活”,下地干活是功能需求,那非功能需求是什么呢?干活工具-锄头?是的!按照aop的思想,锄头是凭空出现在干活人手上的,而非aop就需要人走过去拿锄头。突然有点扯

我们按照上面的图示来连接各功能模块和非功能模块,如果其中的某个非功能模块接口发生变更,显然是需要更改所有对应的功能模块,有没有一种方法是不需要变动功能模块的呢?有,这就是aop,执行逻辑如下图示:

把功能性代码和非功能性代码完全隔离开来,这就是所谓的正交关系,线性无关。相当于xy坐标轴,当x发生变化时y轴不会变,相反亦然。
在具体代码中需要把非功能性代码置入到功能性代码中,用到的技术就有CGLIB和java动态代理。spring在代码运行时将非功能性代码置入,运行时创建了一个代理类,让代理类来做具体操作

上面的就是spring中IOC和AOP的简单说明

顺便说说

  • java的编译期和运行期
    一个java源文件(以.java结尾滴)会经过词法/语法编译器的编译 ---> 生成对应的java字节码(以.class结尾的) ---> 然后虚拟机(比如hotspot)就会加载字节码 - 具体就是使用c1/c2等解释器采用jit,aot等等解析技术对代码解释或者编译执行。

  • 那么问题来了
    上面提到的解释执行和编译执行是怎么回事呢??

      1. 解释执行
        说到解释执行,得扯java的logo:书写一次,到处执行。最初的实现就是使用解释的方式支持应用程序可移植性目标的。解释执行:将每个java指令转译为对等的微处理器指令,并根据转译后的指令先后次序依序执行,一个java指令可能对应十几或者几十个对等微处理指令,运行的时候还要先解释,硬件条件差的话,速度xxxxxx
      1. 编译执行
        java的编译执行又分为 JIT(动态编译)和AOT(静态编译)。
        ····
        先说说两者的区别吧,通常我们理解的动态和静态编译好像都是从c/c++开始的。
        动态编译,程序运行时需要一个额外的链接库,在编译的时候需要连接到库中调用需要的接口/方法。
        而静态编译则将程序运行需要的方法从库中抽取出来,再链接到程序中,这样运行时就不需要调用依赖库咧。
        我们在window下常见的:动态版(DLL)和静态版(LIB)
        ····
        说回java,为了解决解释执行效率问题,首先出现的是JIT技术,当java执行runtime环境时,每遇到一个class,JIT就会对这个类进行编译,生成相当精简的二进制码,花费少许的编译时间来换取后续虚拟机编译的执行速率。jit又叫即时编译。
        那使用jit,程序速度是不是就提升了很多呢??听说并没有,因为有些java文件是很少执行的,还要编译这些东西,所以整体上,时间并没有减少很多。是不是感觉JIT还缺少什么,动态表现在哪??基于JIT的经验,又出来了动态编译器(dynamic compiler/应该整合进了JIT),动态预判哪些需要编译,那些最频繁执行的方法,也叫热点代码。ps:jit和动态编译时间上不知道有没有出错,勿喷。
        ····
        再来说说aot,由于动态编译的不断改进,JIT在很多程序中可以与c/c++静态编译的性能相当了,但是。
        很多开发人员基于经验或者传闻,认为动态编译可能会严重干扰程序的操作,因为动态编译器必须要与程序共用cpu(emmmm,'很多开发人员'都是大牛,知道所以然),这时一些程序员就呼吁对java代码搞静态编译。既然有需求了就来折腾吧,于是某些公司就基于某些适合的领域/环境,搞出了aot。
        aot编译又叫提前编译。
      1. 字节码又是上面东吸?
        java通过javac程序编译生成本地与平台无关的格式来达到程序到处执行的目的,这个与平台无关的格式就是字节码.class文件,这些文件定义了执行 Java 程序所需的所有信息。
        ····
        媒介有了,那么用什么执行呢??
        ····
        就是java执行引擎,我们熟知的JRE-java运行时环境。每个特定的本地平台都可以实现Java虚拟机,比如基于 Linux 的 Intel x86 平台、Sun Solaris 平台和 AIX 操作系统上运行的 IBM System p 平台等~等,每个平台都拥有一个 JRE,这些 JRE 实现了所有的本地支持,从而可以正确执行为 Java 平台编写的程序。
        ····
        等等,java运行时环境是什么鬼,c/c++之类的没有类似的虚拟机也可以在电脑上执行呀,只能说是java为了符合“书写一次到处执行”而设的语言设(对比我们的人设)。因为java为各平台都能执行java代码,具体实现就是加了个中间层-虚拟机,通过虚拟机来生成机器码,并在此基础上创建了一个数据交换格式-.class文件。
        ····
        前面说了一大堆,跟字节码都是有关系的,那么字节码内部结构是什么呢?(说了一堆,是不是感觉现在才进入正题吖)
        字节码序列,描述了 Java 类中每个方法所执行的操作,使用的是一个理论上无限大的操作数堆栈来描述计算。(JVM规范定义了这些字节码的执行。执行 Java 程序时,用于任何特定本地平台的任何 JRE 都必须遵守 JVM 规范中列出的规则。)
        这也是什么为什么需要java虚拟机的原因,因为基于堆栈的系统很少,而字节码文件就是基于堆栈的,所以大多数本地平台不能直接执行 Java 字节码。漏个例外:Intel X87 浮点数协处理器。有兴趣看看。
        ····
        总结:
        一:字节码就是一个基于堆栈的数据格式;(我们一般电脑都是用寄存器的,至于堆栈和寄存器区别,无能,自查)
        二:因为要多平台兼容,所以加了个中间层-jvm。(好像就这两句总结就说完咧喔)
      1. 还有其他的,c1c2之类的
        自查吧,说太多跑题了(我也还没学那么深)

参考:
https://www.jianshu.com/p/ac079e7fc412
https://www.cnblogs.com/tinytiny/p/3200448.html