本课程讲一下spring的DI,也就是依赖注入,或者说是控制反转IOC,这概念听起来有点高大上,其实则不然,就是一种设计思想,强调解耦你的application中的各个Component,当然没有耦合的应用是没有的,凡事都有度,技术到了一定高度,对这个度的把控就是见仁见智的事情了。
好了,废话没少说,进入正题,我们以spring实战中的例子为基础:在圣斗士星矢中有个骑士,名字叫星矢(本故事纯属虚构,如有雷同,纯属活该),这厮的任务是保护一个小网红,名字叫麦当娜,好吧,这个美好的故事就这样开始了,座位故事的设计者,我这样实现这个故事。
package com.gaoxueping.knights; public class DamselRescuingKnight implements Knight { private RescueDamselQuest quest; public DamselRescuingKnight() { this.quest = new RescueDamselQuest(); } public void embarkOnQuest() { quest.embark(); } }
以上我们的例子中,在构造函数中,我们为了让星矢实现营救任务,让他自己去实现RescueDamselQuest的实例,因此有了轻微的耦合,
以上我们费尽心机的说了那么多耦合的不好处(在传统EJB中,这个弊端很明显,当然在spring的压迫下,EJB目前也支持DI),就是为了引出dependency injection,也就是依赖注入,还是以星矢营救麦当娜为例子,试想一下,我们如果让星矢去营救麦当娜,至于在营救的过程中需要什么辅助,我们采用按需供给的方式,他需要什么技能,我们就是提供什么,这样的话,星矢只要安心去营救麦当娜好了,其他的概不关心。好的,我们给出我们全新的星矢
package com.gaoxueping.controller; public class BraveKnight implements Knight { private Quest quest; public BraveKnight(Quest quest) { this.quest = quest; } public void embarkOnQuest(){ quest.embark(); } }
从这里可以看出,星矢没有自己创建营救任务的实例,营救任务的实例是在构造器中作为参数传入,这是注入的一种方式,叫构造器注入,重要的是,传入的营救任务类型是Quest,比如在营救道路上杀一头拦路驴,在牢房斩掉一扇门的任务都会实现Quest这个接口,因此星矢可以在胜任各种营救任务。
从以上的类中看出,BraveKnight这个类没有与任何Quest耦合,对星矢来说,他的营救任务只是实现了Quest接口,至于这个探险任务是杀一头驴还是炸一扇门,他不需要关心,这就是DI的好处,松耦合。
例如在星矢在营救任务重要杀一头驴,这个任务可以这样来写
package com.gaoxueping.controller; public class SlayDragonQuest implements Quest { public void embark(){ System.out.println("Embarking on quest to slay the dragon!"); } }
我们接下来将SlayDragonQuest注入到BraveKnight,也就是装配,可以通过java配置,也可以通过xml,通过java配置如下
package com.gaoxueping.controller; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.gaoxueping.controller.*; @Configuration public class KnightConfig { @Bean public Knight knight(){ return new BraveKnight(quest()); } @Bean public Quest quest(){ return new SlayDragonQuest(); } }
通过xml配置如下
<?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 id="knight_guy" class="com.gaoxueping.controller.BraveKnight"> <constructor-arg ref="quest" /> </bean> <bean id="quest" class="com.gaoxueping.controller.SlayDragonQuest"> </bean> </beans>
然后我们调用一下试试
package com.gaoxueping.controller; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.expression.spel.SpelMessage; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class BraveKnightDo { @RequestMapping(value = "/knightDo", method = RequestMethod.GET) public void knightDo(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/knight.xml"); Knight knight = context.getBean(Knight.class); knight.embarkOnQuest(); } }
okay,成功,就这样子