早期的 Java EE 使用 EJB 为核心的开发方式,但是这种开发方式在实际开发环境中存在诸多问题: 使用复杂, 代码臃肿, 移植性差等.
于是"Spring 之父" Rod Johnson 在其畅销书《Expert One-on-One J2EE Design and Development》中使用一个3万行代码的附件,展示在不使用EJB的情况下创建一个拓展性强, 高质量的 Java 应用程序, 随着这本著作的流行, Rod Johnson 把这份源码开源, 并把这个新框架命名为: “Spring”, 含义为: Spring 像一缕春风一样,扫平传统 J2EE 的寒冬.于是在2004年3月24日发布了 1.0 正式版本.
广义上的Spring指的是以Spring Framework 为核心的 Spring 技术栈.
衍生的相关技术有: Spring Framework、Spring MVC、SpringBoot、Spring Cloud、Spring Data、Spring Security 等
狭义的Spring: Spring Framework , 通常被称为Spring 框架
Spring 有两个核心部分: AOP 和 Ioc
AOP (Aspect Oriented Programming) 译为"面向切面编程" , 利用AOP 把为业务服务的但是不属于业务部分的逻辑封装,增加代码的复用性, 降低模块之间的耦合度,除此之外, AOP 还可以解决日志, 事物, 权限等系统层面的问题.
IOC (Inverse of Control ) 译为"控制反转", 指把创建对象过程交给 Spring 进行管理
用一句话概括Spring: Spring是包含众多工具方法的IoC容器.
我们回忆之前学习的知识, 在Java中的List/Map是数据存储容器, 在Java Web中 的Tomcat 是 Web容器. 那么对应的Spring 就是 存储 IoC的容器.
下面利用两种不同的方式构建Car对象来认识其区别.
假如你现在收到一个项目: 客户需要定制化的选择的新车的组件及其配件. 那么对于一个Car对象是由许多组件构成, 这些组件又由更多小组件组成,所以下面的就简化的提供一个依赖关系.
传统的程序开发:
传统开发采当需要下层对象时在当前类中 引入该对象 即可, 这样就会衍生出许多问题,先上代码
package Old;
public class CarExample {
public static void main(String[] args) {
Car car = new Car(20);
car.init();
}
/**
* 汽⻋对象
*/
static class Car {
private Framework framework;
public Car(int size) {
this.framework = new Framework(size);
}
public void init() {
// 依赖⻋身
framework.init();
}
}
/**
* ⻋身类
*/
static class Framework {
private Bottom bottom;
public Framework(int size) {
this.bottom = new Bottom(size);
}
public void init() {
// 依赖底盘
bottom.init();
}
}
/**
* 底盘类
*/
static class Bottom {
private Tire tire;
public Bottom(int size) {
this.tire = new Tire(size);
}
public void init() {
// 依赖轮胎
tire.init();
}
}
/**
* 轮胎类
*/
static class Tire {
// 尺⼨
private int size = 30;
public Tire(int size) {
this.size = size;
}
public void init() {
System.out.println("轮胎尺⼨:" + size);
}
}
}
当前需求只是客制化轮胎尺寸,但是后期有了更多的需求,那么代码的改动就会很大,这就是代码的耦合度很高,那么如何降低耦合度呢? 那么就可以把控制权交给上级类,由上级类实现下级类的初始化.
控制反转式程序开发:
通过控制反转开发的手段,就可以实现模块之间的解耦,假如需求增加底盘的高度,车身的颜色,车身的材质,实现代码如下:
package New;
class CarExample {
public static void main(String[] args) {
// 自定义轮胎尺寸
Tire tire = new Tire();
tire.setSize(32);
// 自定义底盘高度
Bottom bottom = new Bottom(tire);
bottom.setHeight(20);
// 自定义车身的颜色和材质
Framework framework = new Framework(bottom);
framework.setColor("Green");
framework.setMaterial("钛合金");
// 最后将这些组件组装好
Car car = new Car(framework);
}
/**
* 汽⻋对象
*/
static class Car {
private Framework framework;
public Car(Framework framework) {
this.framework = framework;
}
public void init() {
// 依赖⻋身
framework.init();
}
}
/**
* ⻋身类
*/
static class Framework {
private Bottom bottom;
private String color = "Red";
private String material = "镀锌薄钢板";
public void setColor(String color) {
this.color = color;
}
public void setMaterial(String material) {
this.material = material;
}
public Framework(Bottom bottom) {
this.bottom = bottom;
}
public void init() {
// 依赖底盘
bottom.init();
}
}
/**
* 底盘类
*/
static class Bottom {
private Tire tire;
private int height = 10;
public Bottom(Tire tire) {
this.tire = tire;
}
public void setHeight(int height) {
this.height = height;
}
public void init() {
// 依赖轮胎
tire.init();
System.out.println("底盘高度: " + height);
}
}
/**
* 轮胎类
*/
static class Tire {
// 尺⼨
private int size = 30;
public Tire() {
}
public void setSize(int size) {
this.size = size;
}
public void init() {
System.out.println("轮胎尺⼨:" + size);
}
}
}
因为通过传递的方式(也就是注入的方式), 我们不需要在当前类中创建下级类,即使下级类改变或者增加属性都不会影响当前类,当前类需要什么就通过set()方法设置好然后交付给上级类,从而降低类之间的耦合度.
对比总结:
在传统开发中采用的,Car对象控制并创建了Framework, Framework对象控制并创建了Bottom,依次向下,而修改后的代码的控制权发生了反转, 不再由是上级对象创建和控制了,而是把下级对象注入到当前对象中,于是下级对象内部改变不会影响到当前类,这就是典型的控制反转,也就是Ioc的实现思想.
通过上述开发方式的转化,实现控制权的翻转,这是Ioc的一个特点,那么如何理解"Spring 是一个Ioc容器",那么对于容器所具备的特性;
那么对应到Ioc容器中就是关于对象的存取操作: 把对象存储到Spring中,把对象从Spring中取出.
Spring是包含众多工具方法的容器,那么把对象存储到Ioc容器中就可以对容器中的工具"随用随取, 用完放回".不必在需要某个工具(对象)时才去创建, 和传统开发需要时创建有着很大区别.
对于传统开发中需要某个对象时就使用 new 去创建(二者之间的关系称为依赖, 比如底盘依赖于轮胎),不妨称 new 对象的类为"调用者", 被 new 的对象称为"被调用者" ,那么调用者掌握着被调用者的控制器,而在Spring中,由于把对象都’存储’到IoC容器中,调用方变成被动的去请求IoC容器创建对象,此时就发生明显的控制反转.
DI (Dependency Injection 依赖注入),依赖注入不是一个设计思想,而是一个具体的实现技术,指的是在IoC容器运行期间动态的把依赖对象注入到当前对象中的技术就是DI.
例如 上面的Bottom(底盘类) 依赖 Tire(轮胎类) , 把Tire对象注入到Botom对象的操作就是DI(依赖注入)
/**
* 底盘类
*/
static class Bottom {
private Tire tire;
private int height = 10;
public Bottom(Tire tire) {
this.tire = tire;
}
public void setHeight(int height) {
this.height = height;
}
public void init() {
// 依赖轮胎
tire.init();
System.out.println("底盘高度: " + height);
}
}
/**
* 轮胎类
*/
static class Tire {
// 尺⼨
private int size = 30;
public Tire() {
}
public void setSize(int size) {
this.size = size;
}
public void init() {
System.out.println("轮胎尺⼨:" + size);
}
}
public static void main(String[] args) {
Tire tire = new Tire();
tire.setSize(32);
// 把构造好的tire对象注入bottom对象的技术就是依赖注入.
Bottom bottom = new Bottom(tire);
bottom.setHeight(20);
}
共同点: 二者都是从不同的维度, 描述同一件事(如何实现程序的解耦, 达到控制权的反转)
不同点: IoC是实现的思想, DI是实现IoC思想的一个具体手段
比如今天的完满的完成了今天的任务,我决定要"奖励自己", 那么奖励自己是一个思想并没有具体的实现; 对应的DI就是如何具体的实现,如奖励自己玩游戏,吃KFC等.
Spring是一个大容器,可以把对象的创建,依赖关系的维护等交付给Spring管理
Spring可以支持对各种框架(Hibernate,MyBaits等)的直接支持.
Spring对Java EE 中一些难用的API(JDBC,远程调用等)都提供了封装,上手难度大大降低
Spring 支持 JUnit4, 可以通过注解方便地测试 Spring 程序
Spring 提供面向切面编程, 可以方便地实现对程序进行权限拦截和运行监控等功能
只需要通过配置就可以完成对事务的管理,而无须手动编程