一 、Spring的功能底层一般都依赖于它的两个核心特性:依赖注入(DI)、面向切面编程(AOP)。
(1)依赖注入(DI)
背景:在一些有实际应用的类中都会有两个或者更多的类组成,这些类相互之间进行协作来完成特定的业务逻辑。按照传统的做法,每个对象负责管理与自己相互协作的对象(即它所依赖的对象,一般是使用new来创建对象)的引用,这样的代码高度耦合和难以测试。而且耦合具有两面性。一方面,紧密耦合的代码难以测试、难以复用、难以理解,并且典型的表现出“打地鼠”式的bug特性(修复一个bug,将会出现一个或者更多的bug)。另一方面,一定程度的耦合又是必须的--完全没有耦合的代码什么也做不了。为了完成有实际意义的功能,不同的类必须以适当的方式进行交互。总而言之,耦合是必须的,但应当被小心谨慎地管理。
应对:对象的依赖关系由系统中负责协调各个对象的第三方组件在创建对象的时候进行设定。对象无需自行创建或管理它们的依赖关系,依赖关系将被自动注入到需要它们的对象当中去。这就是DI。
好处:松耦合,只需要在对象使用的时候将它所依赖的实现类注入进去即可。如果一个对象只通过接口(而不是具体的实现或初始化过程)来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行替换。
如何工作:Spring通过应用上下文(Application Context)装载bean的定义并把它们组装起来,Spring应用上下文全权负责对象的创建和组装。Spring自带了多种应用上下文的实现,它们之间的主要区别仅仅在于如何加载配置。
(2)面向切面编程(AOP)
面向切面编程允许你把遍布应用各处的功能分离出来形成可重用的组件。
面向切面编程往往被定义为促使软件系统实现关注点的分离的一项技术,因为系统由许多不同的组件组成,每一个组件负责一块特定功能。除了实现自身核心的功能之外,这些组件还经常承担着额外的职责,诸如日志、事务管理和安全这样的系统服务经常融入到自身具有核心业务逻辑的组件中去,这些系统服务通常被称为横切关注点,因为它们会跨越系统的多个组件。
组件分散给代码带来的复杂性:
1、代码复用性低,当要修改这些关注点的逻辑,你必须要修改各个模块中的相关实现。即使你把这些关注点抽象为一个独立的模块,其他模块只是调用它的方法,但方法的调用还是会重复出现在各个模块中。
2、混乱,组件会因为那些与自身核心业务无关的代码而变的混乱,一个模块或方法关心的事情应该只有一件。
使用AOP所带来的好处:
1、被相切的组件会具有更高的内聚性并且会更加关注自身业务,这些组件无钱不需要了解涉及系统服务所带来的复杂性。
2、AOP能确保POJO的简单性,即被相切的类不需要实现任何接口或继承任何类。
二、Spring的最根本使命是简化JAVA开发,为了简化Java开发的复杂性,Spring采取了以下4种关键策略:
1、基于POJO的轻量级和最小侵入性编程;
2、通过依赖注入和面向接口实现松耦合;
3、基于切面和惯例进行声明式编程;
4、通过切面和模板减少样板式代码。
(1)POJO的最小侵入性编程
很多框架会强迫应用继承它们的类或实现它们的接口从而导致应用与框架绑死(例如Struts2的Action,你可以去继承它的ActionSupport的类来增强你的Action)。但Spring会竭力避免因自身的API而弄乱你的应用代码,相反在基于Spring构建的应用中,它的类通常没有任何痕迹表明你使用了Spring,最坏的场景是一个类或许会使用Spring注解,但它依旧是POJO。但是一些必要的注解依旧存在,如Spring Boot中的启动注解。
(2)通过依赖注入和面向接口实现松耦合
在类中通过面向接口编程,使接口来表现对象之间的依赖关系,而具体的实现通过依赖注入进行注入,这样就实现了松耦合。即类A依赖一个接口B,而该接口B有多种实现类,则类A可以使用该接口B的所有实现类,而且这样对类A来说是看不见的。
(3)样板代码:即一些重复但不得不写的代码。比如相同的异常处理,流的开闭。
三、Spring容器
Spring自带了多个容器实现,可以归为两种不同的类型。bean工厂(由org.springframework.beans.factory.BeanFactory接口定义)是最简单的容器,提供基本的DI支持。应用上下文(由org.Springframework.context.ApplicationContext接口定义)基于BeanFactory构建,并提供应用框架级别的服务,例如从属性文件解析文本信息以及发布应用事件给感兴趣的事件监听者。bean工厂与应用上下文的区别在于,bean工厂是最简单的容器,只提供基本的DI,而应用上下文是基于bean工厂进行构建,提供应用框架级别的服务。可以看看源码,应用上下文的接口是继承了bean工厂的接口的。
常用的应用上下文都有: