Spring介绍
Spring 是一个致力于简化Java开发开源框架。早期,是为了解决企业级应用开发的复杂性而创建的。使用Spring可以让JavaBean很简单的实现只有EJB才能完成的事情,并且不单单只有服务器端开发,任何Java应用都可以从简单性、松耦合和可测试性等方面在Spring中获益。
Spring怎么简化Java开发的呢?
为了降低Java开发的复杂性,Spring采取了一下4个策略。
- 基于POJO的轻量级和最小侵入性编程。
- 通过依赖注入和面向接口实现松耦合。
- 基于切面和惯例进行声明式编程。
- 通过切面和模板减少样板是开发。
Spring所做的任何事情几乎都可以追溯到以上的一条或多条策略。
依赖注入(DI)
任何一个应用都会由一个或者多个类组成,理所当然这些类之间彼此依赖、相互协作来完成特定的业务逻辑。但按照传统的做法,每个对象负责管理与自己协作的对象(即所依赖的对象)的引用,这将会导致高耦合以及难以测试的代码。
例:如一下程序所展现的Kniight 类
package springdemo;
public class DamselRescuingKniight {
private RescueDamselQuest quest;
public DamselRescuingKniight(){
this.quest = new RescueDamselQuest(); // --->紧耦合
}
public void embarkonQuest(){
quest.embark();
}
}
可以看到,DamselRescuingKniight类在构造函数种自行创建了RescueDamselQuest类。这使得DamselRescuingKniight类和RescueDamselQuest类紧密的耦合到了一起,这极大的限制了这个骑士执行探险的能力。如果一个少女需要救援,这个骑士能够召之即来。但如果一头恶龙需要杀掉,这个骑士就爱莫能助了。
同时,为这个DamselRescuingKniight编写单元测试将出奇的困难。在这样一个测试种,必须保证当骑士的embarkonQuest()方法被调用的时候,探险的embark()方法也要被调用。但是没有一个简单明了的方式能够实现这一点。所以,DamselRescuingKniight类无法测试。
耦合的重要性
耦合具有两面性(two-headed beast)。一方面,紧密耦合的代码难以测试,难以复用也难以理解,并且典型的展现出“打地鼠”的bug特性(修复一个bug,将会出现一个或者更多新的bug)。另一方面,一定程度的耦合又是必须的------完全没有耦合的代码什么也做不了。为了完成实际意义的功能,不同的类必须以适当的方式进行交互。总而言之,耦合是必须的,但应当小心谨慎的管理。
通过Spring的DI,对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对象的时候进行设定。对象无需自行创建或管理他们的依赖关系。DI将根据依赖自动注入到需要他们的对象当中去。
例:
package springdemo;
public class BraveKnight {
private Quest quest;
private BraveKnight(Quest quest){
this.quest = quest; // --->Quest被注入进来
}
public void embarkonQuest(){
quest.embark();
}
}
不同于上个例子DamselRescuingKniight类,BraveKnight 类没有自行创建探险任务,而是通过构造器参数注入传入。这是依赖注入方式之一的构造器注入
并且,BraveKnight 没有和任何特定的Quest实现发生耦合。结果来说,只要探险任务实现了Quest机接口,那么是什么探险任务就无关紧要了。这就是DI带来的巨大收益---------松耦合。
让我们看看Quest的实现以及怎么将Quest传入到构造器参数中。
Quest实现类
package springdemo;
import java.io.PrintStream;
public class SlayDragonQuest implements Quest {
private PrintStream stream;
public SlayDragonQuest(PrintStream stream){
this.stream = stream;
}
@Override
public void embark() {
stream.println("Embarking on quest to slay the dragon");
}
}
通过Spring Xml文件配置将Quest注入到Knight中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.atguigu.springboot01helloworldquick.springdemo.BraveKnight">
<constructor-arg ref="quest"/> <!--构造器参数注入-->
</bean>
<bean id="quest" class="com.atguigu.springboot01helloworldquick.springdemo.SlayDragonQuest">
<constructor-arg ref="#{T(System).out}"/> <!--构造器参数注入-->
</bean>
</beans>
同时,也可以选择使用Java的方式配置:
@Configuration
public class KnightConfig {
public Knight knight(){
return new BraveKnight(quest());
}
@Bean
public Quest quest(){
return new SlayDragonQuest(System.out);
}
}
这里我们不需要过多的关注细节,我们只要知道不管是基于XML还是基于Java配置。DI所带来的的收益都是相同的。