引言
控制反转(Inversion of Control,英文缩写为IoC)是框架的重要特征,并非面向对象编程的专用术语。它包括依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。
依赖注入
依赖注入听起来比较吓人,貌似一种非常复杂的编程技术或者设计模式。实际上依赖注入并不复杂,通过在工程中应用依赖注入技术,可以得到更简单、更容易理解和测试的代码。
How DI works
除了Hello-world级别的程序,稍微复杂一点的 Java 应用都需要多个类配合实现功能。一般而言,每个类自己负责获取它要合作的类对象的引用,这会导致代码高度耦合且难以测试。
首先看如下代码:
package com. Spring .sample.knights;
public class DamselRescuingKnight implements Knight {
private RescueDamsel quest quest;
public DamselRescuingKnight() {
this.quest = new RescueDamsel Quest (); //与RescueDamselQuest紧耦合
}
public void embarkOnQuest() {
quest.emark();
}
}
可以看出,DamselRescuingKnight在它的 构造函数 中创建了自己的Quest实例——RescueDamselQuest实例,这使得DamselRescuingKnight与RescueDamselQuest紧密耦合,如果需要刺杀Damsel,则这个刀可以使用,但是如果需要刺杀恐龙,则这个刀就派不上用场了。
更糟的是,给DamselRescuingKnight写单元测试很不方便,在这个测试中,你必须确认:当调用knight的emarkOnQuest函数时,quest的embark函数也正确调用,但这并不容易。
耦合是一头双头怪:一方面,紧耦合的代码难以测试、难以复用并且难以理解,并且经常陷入“修复一个bug但引入一个新的bug”的开发怪圈中;另一方面,应用程序必须存在适当的耦合,否则该应用无法完成任何功能。总之,耦合是必要的,但是应该控制组件之间的耦合程度。
通过使用依赖注入(DI)技术,对象之间的依赖关系由Spring框架提供的容器进行管理,而不需要某个对象主动创建自己需要的引用,如下图所示:
再看一个BraveKnight类的例子:
package com.spring.sample.knights;
public class BraveKnight implements Knight {
private Quest quest;
public BraveKnight(Quest quest) { // Quest实例被注入
this.quest = quest;
}
public void embarkOnQuest() {
quest.emark();
}
}
该对象不再局限于一种quest实例,在构造过程中利用构造函数的参数传入quest实例,这种类型的依赖注入称为构造注入。
还有一点需要注意,使用接口定义quest实例,这就是面向接口编程,使得BraveKnight不再局限于某种特定的Quest实现,这就是DI带来的最大的好处——松耦合。
实现依赖注入
在上述例子代码可以看出,Spring相当于将依赖注入的位置从BraveKnight类中剥离出来,那么具体的依赖注入代码如何写呢?开发人员如何规定给BraveKnight注入哪个Quest实现,例如SlayDragonQuest?
package com.spring.sample.knights;
import java.io.PrintStream;
public class SlayDragonQuest implements Quest {
private PrintStream stream;
public SlayDragonQuest(PrintStream stream) {
this.stream = stream;
}
public void emark() {
stream.println(“Embarking on quest to slay the dragon!”);
}
}
在Spirng框架中,最通用的方法是通过写XML配置文件来定义组件之间的依赖关系,如下所示:
<?xml version=”1.0″ encoding=”UTF-8″?>
<beans xmlns=””
xmlns:xsi=””
xsi:schemaLocation=” “>
<bean id=”knight” class=”com.spring.sample.knights.BraveKnight”>
<constructor-arg ref=”quest” />
</bean>
<bean id=”quest” class=”com.spring.sample.knights.SlayDragonQuest”>
<constructor-arg value=”#{T(System).out}” />
</bean>
</beans>
在这个xml配置文件中分别定义了BraveKnight和SlayDragonQuest两个bean:在BraveKnightbean的定义中,通过构造器函数传入一个SlayDragonQuest的引用;在SlayDragonQuest的定义中,通过SpEL语言将System.out传入它的构造函数。
Spring 3.0引入了JavaConfig,这种写法比xml文件的好处是具备类型安全检查,例如,上面XML配置文件可以这么写:
package com.spring.sample.knights.config;
import com.spring.sample.knights.BraveKnight;
import com.spring.sample.knights.Knight;
import com.spring.sample.knights.Quest;
import com.spring.sample.knights.SlayDragonQuest;
import org.springframework.context. annotation .Bean;
import org.springframework.context.annotation. Configuration ;
@Configuration
public class KnightConfig {
@Bean
public Knight knight() {
return new BraveKnight(quest());
}
@Bean
public Quest quest() {
return new SlayDragonQuest(System.out);
}
}
不论是基于XML的配置还是基于Java文件的配置,都由Spring框架负责管理beans之间的依赖关系。
启动依赖注入
在Spring应用中,由application context负责加载beans,并将这些beans根据配置文件编织在一起。Spring框架提供了几种application context的实现,如果使用XML格式的配置文件,则使用ClassPathXmlApplicationContext;如果使用Java文件形式的配置文件,则使用AnnotationConfigApplicationContext。
package com.spring.sample.knights;
import com.spring.sample.knights.config.KnightConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class KnightMain {
public static void main(String[] args) {
// ClassPathXmlApplicationContext context =
// new ClassPathXmlApplicationContext(“classpath:/knight.xml”);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(KnightConfig.class);
Knight knight = context.getBean(Knight.class);
knight.embarkOnQuest();
context.close();
}
}
上述代码中,根据KnightConfig.java文件创建Spring应用上下文,可以把该应用上下文看成对象工厂,来获取idknight的bean。
总结
以 上就是我对Java开发大型互联网架构- spring架构依赖注入那些不知道的事问题及其优化总结,分享给大家,觉得收获的话可以点个关注收藏转发一波喔,谢谢大佬们支持!
最后,每一位读到这里的网友,感谢你们能耐心地看完。希望在成为一名更优秀的Java程序员的道路上,我们可以一起学习、一起进步!都能赢取白富美,走向架构师的人生巅峰!
想了解学习Java方面的技术内容以及Java技术视频的内容可加群:722040762 验证码:头条(06 必过)欢迎大家的加入哟!