什么是spring?
![13bb0ae13ead2583544e9950517ecb04.png](https://i-blog.csdnimg.cn/blog_migrate/486fca807167f358c35a5d36a4a09a92.png)
早在2007年 ,一个基于Java语言的开源框架正式发布,取了一个非常有活力且美好的名字,叫做Spring。它是一个开源的轻量级Java SE (Java标准版本)/Java EE (Java企业版本)开发应用框架,其目的是用于简化企业级应用程序开发。众所周知,一个完整的应用是由—组相互协作的对象组成。所以开发一个应用除了要开发业务逻辑之外,最多的是关注如何使这些对象协作来完成所需功能,而且要低耦合、高聚合。业务逻辑开发是不可避免的,那如果有个框架出来帮我们来创建对象及管理这些对象之间的依赖关系。于是Spring在此需求之下应运而生。而Spring的核心思想是IOC(控制反转)和AOP(面向切面编程)。
你可能正在想"Spring不过是另外一个的framework"。当已经有许多开放源代码(和专有)J2EE framework时,我们为什么还需要Spring Framework? 因为Spring给你的工程提供了一站式解决方案,Spring框架除了帮我们管理对象及其依赖关系,还提供像通用日志记录、性能统计、安全控制、异常处理等面向切面的能力,还能帮我管理最头疼的数据库事务,而且还能非常简单的与第三方Web框架集成。从这里我们可以认为Spring是一个超级粘合大平台,除了自己提供功能外,还提供粘合其他技术和框架的能力,从而使我们可以更自由的选择到底使用什么技术进行开发。不管是JAVASE(C/S架构)应用程序还是JAVA EE( B/S架构)应用程序都可以使用这个平台进行开发。如今的Spring已经不再是一个框架,早已成为了一种生态。
Spring的诞生
时间回溯到2002年,当时正是Sun公司(已经被Oracle公司收购)的 EJB(Enterprise Java Bean ) 大行其道的时候,EJB的主要思想翻译成大白话就是“把你编写的软件中那些需要执行制定的任务的类,不放到客户端软件上了,而是给他打成包放到一个服务器上”,虽然EJB是Sun公司主推的技术,但是仍然存在很多问题:
开发一个EJB需要大量的接口和配置文件,直至EJB2.0的年代,开发一个EJB还需要配置两个文件,其结果就是配置的工作量比开发的工作量还要大;
EJB是运行在EJB容器中的,而Sun公司定义的JSP和Servlet却是运行在Web容器中的,于是你可以想象得到,你需要使用Web容器去调用EJB容器的服务。这样在实际项目中需要开发两个容器,非常多的配置内容和烦琐的规范导致开发效率十分低下,同时对于Web容器调用EJB容器的服务这种模式,注定了需要通过网络传递,造成性能不佳;对于测试人员还需要了解许多EJB繁琐琐的细节,才能进行配置和测试,这样测试也难以进行。
针对这个问题,一个美国小伙Rod Johnson在同年10月出了一本书《Expert One-on-One J2EE》,指出了 Java EE 和 EJB 组件框架中存在的一些主要缺陷。在这本书中,他提出了一个基于普通 Java 类和依赖注入的更简单的解决方案,并在书中展示了如何在不使用 EJB 的情况下构建高质量、可扩展的在线座位预留系统。为了构建应用程序,他编写了超过 30,000 行的基础结构代码,项目中的根包命名为 com.interface21,所以人们最初称这套开源框架为 interface21,也就是 Spring 的前身。
![3824396b6c66f88b06a43604163db1ac.png](https://i-blog.csdnimg.cn/blog_migrate/292e339e549fb22e0557171536705ef9.png)
在这本书发布后,一对一的 J2EE 设计和开发一炮而红。这本书免费提供的大部分基础架构代码都是高度可重用的。2003 年 Rod Johnson 和同伴在此框架的基础上开发了一个全新的框架命名为 Spring ,据 Rod Johnson 介绍 Spring 是传统 J2EE 新的开始。随后 Spring 发展进入快车道。
2004年03月,1.0 版发布。
2006 年10月,2.0 版发布。
2007年11月更名为SpringSource,同时发布了 Spring 2.5。
2009年12月,Spring3.0 发布。
2013年12月,Pivotal宣布发布 Spring 框架 4.0。
2017年09月,Spring 5.0 发布。
Spring的主要模块如下:
![6b32f93490616541e494f92784d58dce.png](https://i-blog.csdnimg.cn/blog_migrate/046be92a54cf8186d74368cfb4c34c95.png)
其中容器是Spring框架最核心的部分,它管理着Spring应用中bean的创建、配置和管理。在该模块中,包括了Spring bean工厂,它为Spring提供了DI的功能。基于bean工厂,我们还会发现有多种Spring应用上下文的实现,每一种都提供了配置Spring的不同方式。除了bean工厂和应用上下文,该模块也提供了许多企业服务,例如Email、JNDI访问、EJB集成和调度。但是Spring最重要的部分还是IOC(控制反转)与AOP(面向切面编程)这两种思想的体现与应用。
浅析Spring的IOC思想
让我们来举个例子认识一下IOC的概念,比如我们新创建了一个动物园,这个动物园里有猴子,按照我们原先创建类的方式,这个动物园的创建的方式是这样的:
![6c74c23b92e1f189cd6f5ab15a7ff2a2.png](https://i-blog.csdnimg.cn/blog_migrate/9ae7ce202b21d67f18b95c85422db7c9.png)
可以看到创建动物园时,直接创建了猴子对象,这就造成一定的耦合性。在真实的项目中这种类与类之间紧密的耦合会造成代码的难以理解,复用性不高;只要猴子编写出现了错误,这段代码就会出现问题。但是完全解耦的代码又是不现实的,很多功能需要贴合在一起才能产生作用。那么如何来做呢?我们定义一个接口来试试:
![db2e80c080220bcb48ff27a55d4502e2.png](https://i-blog.csdnimg.cn/blog_migrate/03d9ad4c5cc6e80d39594d3de3a71cac.png)
于是当动物园创建时,不用考虑是哪一种具体的动物,Animal接口可以接受任何动物对象,比如也会有猫、狗。事情好像顺了一点,原来复杂的关系解开了一些,但是原来动物园创建时只与猴子类有关,现在多出一个动物接口,似乎变得更加复杂。如何能让动物园创建时与具体的猴子、狮子、老虎脱离关系呢?于是对动物园的创建方式进行修改:
![2c47965c812b8ae302fac94276e15b9a.png](https://i-blog.csdnimg.cn/blog_migrate/e6d552be0d9e3c0f555df791d2832405.png)
可以看到动物园里面已经完全看不到猴子了,当Animal创建时会根据构造函数传入的动物类型进行响应。于是动物园与猴子成功解耦。但是谁来传入猴子或者狮子或者老虎这些具体的对象呢,当然是管理员了。在创建动物园时,管理员做的工作实际上是这样的:
![684889494ef23bd9d52e1ebde96825f8.png](https://i-blog.csdnimg.cn/blog_migrate/e55c7a584327904ea88f0609703bc37d.png)
有了管理员,动物园与具体动物可以完全解耦,这些类可以单独进行复用,实现了最大的自由度,这实际上是一种对象创建权利移交的过程,一开始创建对象的权利属于动物园,后来属于管理员,按照这个逻辑,管理员上头是经理,经理上面是总经理…,最大的那个上级是董事长,那么在Spring框架内这个董事长是谁呢?就是Spring容器。IOC(控制反转)的意思就是将具体对象创建的控制权转交给Spring容器。在Spring框架内,我用SpringBoot的注解方式实现:
![65793f59b6d9562e437055b4887b8c01.png](https://i-blog.csdnimg.cn/blog_migrate/2515763e65d7d1870b027527ce985e9b.png)
测试方法是这样的:
![b0b8008d0a861fa1dc01c2cb86cf00c2.png](https://i-blog.csdnimg.cn/blog_migrate/5d24a98dcdb58ac99cba5a079baa268d.png)
可以看到,全程没有new 出一个对象,全部用注入的方式。这表明在容器启动时,Spring根据配置文件的描述信息,自动实例化Bean(也就是类)并完成依赖关系的装配,从容器中即可返回准备就绪的Bean实例,后续就可直接使用。上面的代码可以看到,Zoo以及Zoo中的Animal都没有进行实例化(也就是创建对象),而是在测试时自动到Spring容器中去取相应的对象,这说明Spring容器控制了创建对象的这个权利,实现了IOC(控制反转)
浅析Spring的AOP思想
AOP意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,是Spring另一个主要内核。AOP与OOP是面向不同领域的两种设计思想:
OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
例如,可以对于“学生”这样一个业务实体进行封装,自然是OOP的任务,我们可以建立一个“Student”类,并将“学生”相关的属性和行为封装其中。而用AOP设计思想对“学生”进行封装则无从谈起。但是AOP可以针对“Student”中的某个方法进行操作:
![c30b1499ee0ce0bbb343285f718f66d0.png](https://i-blog.csdnimg.cn/blog_migrate/34bbfe73c747b5663ea42bf9c8827879.png)
还是用SpringBoot举一个简单的例子来说,例如我们首先封装了一个用户类:
![beadd8f5e33979be6699a75cf713dcac.png](https://i-blog.csdnimg.cn/blog_migrate/1a38360adc236b66a98260f4aab6dd91.png)
然后用户的行为,我们也采用OOP思想进行封装:
![573e8ace277dc383b9377060236cec51.png](https://i-blog.csdnimg.cn/blog_migrate/e0ed4328035e68dcd27b29e41de6f657.png)
当然,“吃”这个动作也可以封装在用户类中,但是一般项目里是将业务逻辑封装到某一个类中的,这里就按照这个约定执行吧。现在我们来测试一下:
![d837c5a608a6dc0789cd59ef474fd4ce.png](https://i-blog.csdnimg.cn/blog_migrate/41362c0b4db0ef097badc8fe37478ed4.png)
系统直接打印出:
![ec53f217f04db722254815ae9e70557b.png](https://i-blog.csdnimg.cn/blog_migrate/98217e363ad1c1081306205125aa74d3.png)
那么假如代码已经写好了,但是我们要做一件事情,那就是要求所有的User对象“吃饭前洗手”,“饭后睡觉”。当然有人可能会说直接修改eat()方法中的源代码,补充进去就好。但是我不想这么做,因为“洗手”,“睡觉”其实与“吃饭”这个动作无关,我们希望做到一种解耦,不要在一类中耦合太多其他的东西。而且“洗手”,“睡觉”的要求是动态的,也许某一天学生们兴致很高,饭后并不想睡觉也说不准。于是采用AOP的思想,我们来做以下操作:
![4171e72f322efb2f1a9fd6dfc3e45eb4.png](https://i-blog.csdnimg.cn/blog_migrate/54cef4d31dcba4c14bcb901a235e007b.png)
再次执行测试方法:
![73e6cd22570242d4eda4034089b008a5.png](https://i-blog.csdnimg.cn/blog_migrate/3b0c843d98e62c9b5a04e5232cbf982c.png)
可以看到我们成功的在吃饭前增加了“洗手”,吃饭后增加了“睡觉”两个业务。
我们还可以定义一些切面方法,比如吃饭前学生的年龄为最初设定的18岁,假如吃饭当天为该学生的生日,吃饭时为凌晨,那么吃完饭学生就变成19岁。也可以采用aop编程来实现,为此只需要修改@before与@after即可:
![153ece21a68a008a38610c69136a1943.png](https://i-blog.csdnimg.cn/blog_migrate/aa9559ab9de6ee592baa690b606531ad.png)
于是再次执行测试方法:
![1b60c9a02d12741658c4a5b5d95de37d.png](https://i-blog.csdnimg.cn/blog_migrate/fbe9b70b463f166b122fbbcf38f7199b.png)
成功的让User对象过了生日。当然Spring还为我们提供了很多切面编辑的注解,例如@Around环绕,@AfterThrowing以及@AfterReturning等等,在此不再赘述,Spring官网可以查询到很多。这里可以看出AOP编程就是可以在不影响方法业务逻辑的情况下指定切面,对切面进行业务操作。切面编程的主要应用场景有:
Authentication(权限认证)
Auto Caching(自动缓存处理)
Error Handling(统一错误处理)
Debugging(调试信息输出)
Logging(日志记录)
Transactions(事务处理)
Spring是如何实现AOP的?
其实就是通过代理来实现,Spring用代理类包裹切面,把他们织入到Spring管理的Bean中。也就是说代理类伪装成目标类,它会截取对目标类中方法的调用,让调用者对目标类的调用都先变成调用伪装类,伪装类中就先执行了切面,再把调用转发给真正的目标bean。
那么怎么搞出来这个伪装类,才不会被调用者发现?(由于JAVA类都需要过JVM的检查,JAVA是强类型检查,哪里都要检查类型)。其实是用了两种思路:
实现和目标类相同的接口,我也实现和你一样的接口,反正上层都是接口级别的调用,这样我就伪装成了和目标类一样的类(实现了同一接口,咱是兄弟了),也就逃过了类型检查,到Java运行期的时候,利用多态的后期绑定(所以Spring采用运行时),伪装类(代理类)就变成了接口的真正实现,而他里面包裹了真实的那个目标类,最后实现具体功能的还是目标类,只不过伪装类在之前干了点事情(写日志,安全检查,事物等)。
生成子类调用,这次用子类来做为伪装类,当然这样也能逃过JVM的强类型检查,因为是继承的,当然查不出来了。子类重写了目标类的所有方法,当然在这些重写的方法中,不仅实现了目标类的功能,还在这些功能之前,实现了一些其他的(写日志,安全检查,事物等)的功能。
Spring的其他思想
Spring除了IOC和AOP这两个核心的思想之外,Spring主要的思想有:
![a3d2de9c37df9d9d72821771f7346518.png](https://i-blog.csdnimg.cn/blog_migrate/08b5a9a26fb4c2d80857ae95c091373d.png)
当然Spring已经发展为一个生态,在AOP与IOC等主要思想的驱动下,主要的项目有:
SpringBoot
SpringFramework
SpringCloud
SpringCloud Data Flow
SpringData
SpringIntegration
SpringBatch
SpringSecurity
...
同时,Spring的源码设计精妙、结构清晰、匠心独运,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。Spring框架源码无疑是Java技术的最佳实践范例。如果想在短时间内迅速提高自己的Java技术水平和应用开发水平,学习和研究Spring源码将会使你收到意想不到的效果。我也在不断的学习与总结当中,希望与众多Spring的爱好者一起提高 。
参考文章:
https://cloud.tencent.com/developer/article/1678024
https://www.jianshu.com/p/50b433669526
https://baijiahao.baidu.com/s?id=1620099105315862154
https://www.cnblogs.com/davidwang456/p/10297476.html
https://www.cnblogs.com/boywwj/p/7502185.html
https://www.jianshu.com/p/94879042db88
https://blog.csdn.net/qukaiwei/article/details/50367761