什么是ioc控制反转转(IoC)促进了低耦合,耦合是什么意思

Quartz与spring结合的动态作业调度经验
 作者: 杨鑫 编辑:
  【IT168 技术】作业调度,在项目的开发中,是一个使用非常普遍的功能,用来执行一个周期性的任务。Quartz是一个开源的用于实现作业计划的框架。在和spring集成后,Quartz使用起来变得非常的简单,因此Quartz也被广泛的使用。一般的作业调度执行的周期都是事先定义好的,能满足大部分的需求。但是也有不能满足的场景,如:1、在系统刚刚上线时,时间周期是一个大概数值。更加符合真实情况的执行周期需要通过不断的调试才可以得出。但是每次调整执行周期,都要重新启动项目,这显然是不能接受的。2、当遇到某些特殊情形,需要在某个时期停止计划的执行。当遇到以上2种场景时,固定的执行周期是不能满足我们的需求。这就要求我们实现可以动态调整执行周期的作业调度。此篇文章就是自己在项目中实现动态的作业计划的一些技术分享。  一、背景介绍  1、Spring简介  Spring是一个开源框架,用来解决企业应用开发的复杂性。Spring使用基本的JavaBean来完成以前只可能由企业级JavaBean(EJB)完成的事情。对于开发者来说,简单性和程序的低耦合性是共同追求的开发方式。而spring使Java的开发变得简单,并且Spring的使用降低了程序间的耦合性,使程序更加容易维护。  Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。1、轻量级。Spring的大小是轻量级的,整个spring的核心jar包加在一起也就1MB左右。Spring的性能开销也是轻量级的,Spring的对象模式默认使用的是单例模式,可以有效的减小系统的开销。2、控制反转。Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的对象会通过被动的方式传递进来,而不是这个对象自己创建。当容器在对象初始化时容器会将其所依赖的其他对象主动的传递给所需的对象。这个方式也可以叫做依赖注入。调用对象本身不用去关心被调用对象的初始化,使开发变得相对简单。3、面向切面。Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发。应用对象只实现它们所应该实现的业务逻辑。其他附件的功能,如日志的记录,可以在切面中进行实现。  Spring提供了使用xml配置文件和注解两种方式,对Spring容器内的类进行引用关系等的配置,在使用时可以根据各自的特点选择不同的方式。  2、Quartz简介  Quartz是一个实现作业调度的框架。Quartz的核心主要由Scheduler、Job、JobDetail和Trigger几部分组成。1、Scheduler是一个接口,它提供了对作业计划的启动、停止、恢复、删除、和对作业计划的重新制定的方法。它通过JobDetail和trigger创建一个作业计划。2、Job是一个接口,只有一个需要实现的方法void execute(JobExecutionContext context)。程序中需要被执行的作业就需要在exeute中实现。3、JobDetail是一个类。通过JobDetail可以设置具体执行的Job,并且给执行的Job设置名称、组、描述等信息。该类包括一个JobDetail(java.lang.String name, java.lang.String group, java.lang.Class jobClass)构造器,它的参数分别是Job名称、组名和实现了Job接口的实现类。4、Trigger是一个类。主要用于设置触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。SimpleTrigger擅长执行单次执行或者固定周期计划任务执行。而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案。可以说Quartz支持cron表达式,是Quartz被广泛使用的一个重要的原因。  3、Cron表达式简介  Cron表达式由6或7个由空格分隔的时间字段组成,如“0 0 4 * * ?”。具体的含义如下表:  Cron表达式还包括一些特殊字符。  1、“*”:表示匹配该域的任意值,如0 * * * * ?,表示每分钟执行。  2、“?” : 只能用在“天(月)” 和 “天(星期)” 两个域。它也匹配域的任意值。由于此两个域是由冲突的只能有一个生效,所以如果不使用哪个域的话,就可以在此域使用&?&。  3、“-”:表示范围,例如在“分钟”域使用5-20,表示从5分到20分钟每分钟触发一次。  4、“/”:表示起始时间开始触发,然后每隔固定时间触发一次,例如在“分钟”域使用0/20,则意味着从整分开始,每隔20分钟执行一次。  5、“,”:表示列出枚举值值。例如:在“分钟”域使用5,20,则意味着在5和20分每分钟触发一次。  6、“L”:表示最后,只能出现在“天(月)” 和 “天(星期)” 两个域,如果在“天(星期)”域使用5L,意味着在最后的一个星期四触发。  7、“W”: 表示有效工作日(周一到周五),只能出现在“天(月)”域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 “天(月)”使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日触发;如果5日在星期一到星期五 中的一天,则就在5日触发。  8、“#”:只能用于“天(星期)”。确定每个月第几个星期几。例如在4#2,表示某月的第二个星期三。  二、 项目中作业调度的使用  1、为什么使用Quartz  在Java中有多种作业调度的框架,如java.util.TimerTask。但是在项目中大多不采用TimerTask,而是使用quartz。为什么不使用java.util.Timer结合java.util.TimerTask?主要有以下几个原因:1、主要的原因是使用不方便,特别是制定具体的年月日时分的时间,而quartz使用cron表达式时,很方便的配置每隔时间执行触发。2、其次性能的原因,使用jdk自带的Timer不具备多线程,而quartz采用线程池,性能上比timer高出很多。3、quartz具有TimerTask的所有的功能,而Timer则不是。  2、标注方式的Quartz使用  我们的项目采用的quartz版本为1.8.4,可以和 spring3.x版本进行很好的融合。在开发中,使用了简单直观的标注方式实现周期性的作业调度。只需要通过以下几步就可以实现在spring中使用Quartz。1、在spring的xml配置文件中加入& task:annotation-driven /& ,用来开启任务调度功能。2、实现一个JAVA类,在类的名称前加入@Component,将类可以被spring容器创建。3、在类中添加一个方法,并且在方法名前加入类似于@Scheduled(cron = &0 0 4 * * ?&)的标注。此处使用的是cron表达式的方式执行作业调度。  从上面的步骤中可以看到,在spring中使用quartz是多么的简单。虽然使用在spring中使用quartz来进行作业调度是那么的简单,但是在对于需要实现动态的作业调度,还需要自己通过编码实现。  三、 作业调度实现  1、实现思路及流程  在项目中使用注解的方式,可以快速的实现一个作业调度程序。但是它也有其缺点,就是执行的周期都是在代码中固定的,如果要修改执行周期,那么就需要修改Java源代码,并且重新编译和部署。对于一个上线的系统需要尽量的减少项目的重新部署。  既然采用注解的方式实现动态的任务调度,那么就只有使用非注解的方式。如果要实现动态的作业调度的话,很容易想到,我们可以将要动态改变的内容作为参数来传入到作业调度方法中。实现时可以有以下几个步骤:1、简单的数据库设计。有了数据库表结构后,当手工修改表中的关于执行周期的字段后,程序就可以读取到最新的执行周期,用于重新加载。  表可以有如下信息:ID、jobName、clazz、scheduleType、startDate、intervalInMillis、cront。2、实现Quartz框架中的Job接口。在Quartz中被调度的类,都是要实现org.quartz.Job接口的。  在实现程序后我们在调度方法中,我们在其中加入打印Job实现类的名称,作为测试使用。3、实现一个支持了增加调度任务和修改调度任务的方法。此处采用的是CronTrigger,支持cron表达式。  此处新增和更新采用了一个方法。当任务没有被启动时会创建任务,当任务已经存在时会重新制定计划。4、编写定时任务,读取数据库。  当程序都准备好后,那么就必须要及时的知道数据库中的记录是否发生变化。我们可以使用现有的标注方式,实现一个定期读取数据库记录的计划调度。如果发现记录中执行周期有变化,那么通过调用addOrUpdateCrontTask方法重新进行计划调度,使新的计划调度生效。  2、测试结果及问题  在完成了以上4个步骤后,需要对实现的程序进行测试。  测试结果程序运行正常。当手工修改数据库表的周期信息后,程序会自动发现数据库的记录中的信息发生了改变,从而调用addOrUpdateCrontTask,使计划周期进行了更改。这个测试结果说明作业调度可以在修改执行周期后,可以被重新的制定。  不过以上测试,是最简单的使用场景。我们知道在真实地使用中,计划调度的方法中肯定会调用现有的已经被实现的Service类的。因此我们对程序进行一下改造。在JobClass中注入一个JobService,然后调用JobService中的方法,看是否成功。  1、首先创建JobService类,并添加标注,以便可以被其他类进行依赖注入。并且在方法中添加sayHelloWorld方法。  2、修改JobClass类,添加JobService类,并且使用@Autowired根据类型注入JobService。并且在执行任务时调用JobService的sayHelloWorld方法。  程序准备好后,再次运行程序,程序发生了错误,空指针异常,“hello world”并没有被打印出来。定位后,这个问题出现在jobService上,jobService为NULL。这说明jobService没有Spring容器自动的传递进来(也就是依赖注入没有生效)。这样就比较麻烦了,虽然可以在excute方法中不使用注入的方式同样可以实现业务逻辑,但是这样就造成之前实现的Service方法不能够被复用,降低了工作效率。所以将类注入到Job的实现类中是非常必要的。  那么该如何解决呢?  四、问题的解决  现在的问题是,想办法将不能被注入的类,注入到Job的接口实现类中。查找资料后,了解到要想使不受Spring管理的类,进行依赖注入,可以通过AutowireCapableBeanFactory类实现。AutowireCapableBeanFactory提供了一个autowireBean(Object existBean)方法,可以给传入的参数existBean对象进行依赖注入。另一方面Spring是通过SpringBeanJobFactory类的createJobInstance方法创建Job实例。那么很容易想到我们可以实现一个SpringBeanJobFactory类的子类,并且重写createJobInstance方法,给创建的Job实例手工进行依赖注入即可解决无法进行依赖注入的问题。下面按照以上构想再对程序做出修改。1、首先自己定义一个类JobSpringFactory继承SpringBeanJobFactory。2、将AutowireCapableBeanFactory类注入到SpringBeanJobFactory中的beanFactory对象中,以方便使用其注入功能。3、重写createJobInstance方法。使用AutowireCapableBeanFactory类的实例beanFactory对创建的实例jobInstance进行手工注入。4、事情还没有完,要想让程序启用,还必须要告诉spring,使用JobSpringFactory来创建Job的实例。所以我们需要在spring的配置文件中加入如下配置。通过此配置将自己实现的jobSpringFactory类注入到了SchedulerFactoryBean中,承担起了创建job的工作。5、最后修改一下之前实现的程序。  在之前的程序中scheduler是通过new关键字产生的工厂的方法产生。现在则需要使用spring中已经配置的SchedulerFactoryBean类,并将其实例注入到scheduler上。  到此程序修改完毕,我们需要再尝试一下。  在测试后,我们发现在JobService中的方法sayHelloworld已经被执行,helloworld已经可以成功的打印出来。这就说明JobService已经被成功的注入到了,Job接口实现类JobClass中。这样就成功的实现了在与spring结合时使用quartz实现动态的作业调度。避免了不能复用spring已经实现过的程序的问题。  五、总结  通过此篇文章,我们简单了解Spring和quartz的背景。在此基础通过一个简单的例子,实现了使用Quartz实现动态的计划任务调度。但是在spring环境下,发现Job接口的实现类中,本来应该通过spring的依赖注入的对象,没有被注入。这个造成已经实现的类和方法不能够被复用,因此必须想办法通过手工的方式对类进行注入。最终我们通过复写了spring的相关类完成了对象的注入。以上就是关于spring下的quartz使用的总结,可以为以后在项目开发过程中遇到类似问题,提供参考。作者简介:杨鑫:主要从事JAVA方面的研发工作。
大学生分期购物销量榜
IT168企业级golang 依赖控制反转(IoC)
| Go语言中文网 | Golang中文社区 | Golang中国
<meta name="author" content="polaris ">
golang 依赖控制反转(IoC)
golang 依赖控制反转(IoC)
  主流开发语言,为了达到项目间的低耦合,都会借助IoC框架来实现。即抽象和实现分离,使用抽象层,不用关心这些抽象层的具体实现;抽象层的实现,可以独立实现。现在比较流行的领域驱动设计(ddd),为了达到将领域层作为最核心,也需要依赖于IOC。
  回过头来,我们看看golang实现的ioc框架,有golang风格的框架,也有从其他主流语言搬过来的比较重的框架。我觉得目前实现最轻量级的,当属martini框架的ioc依赖库 /codegangsta/inject
。代码行数很少,提供类型注册、接口注册、类型解析、函数注入、struct注入的方法,可以说基本的已经比较全了。从文章开头应该可以猜到,我现在一直在学习ddd,目前在.NET实际项目中边运用边学习。在实际使用中发现,ioc除了要有单例模式(Singleton)支持外,应该还有临时实例(Transient)的支持。因此萌生了我写golang下的ioc框架的原因。
  我的目的很简单,希望ioc不仅支持Singleton,还要支持Transient。最初想法是,编写一个抽象层,里面支持这两种模式的注入。其中transition部分自己独立实现,而singleton,则采用现成的 /codegangsta/inject 框架,并加一层适配。Transient的实现,其特点就是,每次解析类型(Resolve)时,都需要创建一个新的对象,这个对象和先前创建的是独立的。此处我采用反射机制,根据类型创建新对象。golang中没有构造函数,为了在创建对象后并在使用前,对其初始化,我引入了构造函数的概念。这个构造函数的接口其实很简单
// Initializer is to init a struct.
type Initializer interface {
InitFunc() interface{}
  在这里我吐槽下博客园,怎么插入代码,还不支持golang啊?
  这个接口很简单,就一个返回interface{}的函数。其实返回的应该是另一个函数,即为构造函数。例如:
func (container *iocContainer) InitFunc() interface{} {
return func() {
if !container.isInitialized {
container.locker = &sync.RWMutex{}
container.singleton = &singletonContainer{valuemapper: make(map[reflect.Type]reflect.Value)}
container.transient = &transientContainer{typemapper: make(map[reflect.Type]reflect.Type)}
container.isInitialized = true
  当初始化时,调用一次构造函数,即完成了一次初始化的操作。其实针对singleton也是一样,也需要一次初始化,只是这个初始化要求仅在第一次时进行,在这里不会因此只调用一次(因为ioc框架不知道你什么时候会被第一次调用,这里需要由构造函数的实现自己进行判断,此处可以用一个字段isInitialized进行检查是否已经初始化了)。
  都说golang的反射,性能很差,我觉得部分反射的部分功能会性能很差,但有些应该还算凑合吧。既然ioc框架实现完了,那就测试下性能。由于在调整前,性能数据没有保存,就不展示了。总之,在改版前,发现inject包,在Resolve的性能很差。经过仔细排查,发现有一处的实现很智能,即当Resolve的接口类型在已注入的类型中不存在时,会尝试将已存在的类型转为接口,如果可以转换则返回。由于golang的理念里,没有类型树。认为接口的方法都实现了,就认为实现了接口,那么判断本身就会变得耗时。也因为这个原因,我重写了singleton部分,在Resolve的时候,仅仅根据传入的类型来判断。如果这个类型在注册时为singleton,那就是singleton,且原先是接口还是类型,都原样拿出,不进行任何转换。果然发现性能有所提升。
  这是ioc容器的接口,也是最核心的:
// ReadonlyContainer is a readonly container
type ReadonlyContainer interface {
// Resolve is to get instance of type.
Resolve(typ reflect.Type) reflect.Value
// Invoke is to inject to function&#39;s params, such as construction.
Invoke(f interface{}) ([]reflect.Value, error)
// Container is a container for ioc.
type Container interface {
Initializer
ReadonlyContainer
// Register is to register a type as singleton or transient.
Register(val interface{}, lifecycle Lifecycle)
// RegisterTo is to register a interface as singleton or transient.
RegisterTo(val interface{}, ifacePtr interface{}, lifecycle Lifecycle)
// SetParent is to resolve parent&#39;s container if current hasn&#39;t registered a type.
SetParent(parent ReadonlyContainer)
  这是调用的代码:
func main() {
var requestContext = ioc.NewContainer()
requestContext.SetParent(iocContainer)
requestContext.RegisterTo(&productcategoryApp.ProductCategoryApplicationServiceImpl{}, (*application.ProductCategoryApplicationService)(nil), ioc.Transient)
commandMQAdapter := new(provider.MyCommandMQProvider)
processor := cqrs.NewCommandProcessor(commandMQAdapter)
processor.RegisterMiddleware((*middleware.AuthCommandMiddleware)(nil))
// execute count
var exeCount = 1000000
// concurrent routine
var concurrentCount = 1
for true {
var wg *sync.WaitGroup = &sync.WaitGroup{}
time.Sleep(300 * time.Millisecond)
startTime := time.Now().UnixNano()
for i := 0; i & concurrentC i++ {
go func(wg1 *sync.WaitGroup) {
for j := 0; j & exeCount/concurrentC j++ {
requestContext.Invoke(func(productCategoryAppSvc application.ProductCategoryApplicationService, roContainer ioc.ReadonlyContainer) {
//processor.RegisterHandler(productCategoryAppSvc)
wg1.Done()
endTime := time.Now().UnixNano()
consoleLog.Printf(&#34;[info] requestContext.Invoke for %d times with %d routines execute in %vms.\n&#34;, exeCount, concurrentCount, float64(endTime-startTime)/float64(time.Millisecond))
  这是性能数据:
1 routine, 3 times resolve singleton and 1 times resolve transient per code invoke, invoke 1,000,000 times.
[commandprocessor]
11:31:29 [info] requestContext.Invoke for 1000000 times with 1 routines execute in ms.
[commandprocessor]
11:31:34 [info] requestContext.Invoke for 1000000 times with 1 routines execute in ms.
[commandprocessor]
11:31:39 [info] requestContext.Invoke for 1000000 times with 1 routines execute in ms.
2 routine, 3 times resolve singleton and 1 times resolve transient per code invoke, invoke 1,000,000 times.
[commandprocessor]
11:23:50 [info] requestContext.Invoke for 1000000 times with 2 routines execute in ms.
[commandprocessor]
11:23:53 [info] requestContext.Invoke for 1000000 times with 2 routines execute in ms.
[commandprocessor]
11:23:56 [info] requestContext.Invoke for 1000000 times with 2 routines execute in ms.
  预估下来,差不多是 2 routine, 4 resolve action, 350,000 / sec 的性能数据。我是在笔记本上进行的测试(i5双核),启用2个并发routine来测试Resolve,每次测试代码的一次执行,包含Resolve4次调用。测试下来,每秒35w次测试代码执行。这个性能,我觉得在业务系统开发中,不需要考虑性能损耗的问题了。
---------------------------------分割线-------------------------------------------------------------
我的,已经挂在github上,有兴趣的可以去了解下。
通过go来安装ioc包:
go /berkaroad/ioc
使用中有何问题,欢迎在github上给我提issue,谢谢!
支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
支持 @ 本站用户;支持表情(输入 : 提示),见
记住登录状态
还不是会员控制反转_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
上传于||暂无简介
阅读已结束,如果下载本文需要使用2下载券
想免费下载本文?
定制HR最喜欢的简历
你可能喜欢下次自动登录
现在的位置:
& 综合 & 正文
IoC(控制反转)与依赖注入是什么个概念
IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”,还有些书籍翻译成为“控制反向”或者“控制倒置”。
1996年,Michael Mattson在一篇有关探讨面向对象框架的中,首先提出了IOC 这个概念。对于面向对象设计及编程的基本思想,前面我们已经讲了很多了,不再赘述,简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。IOC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦,如下图:
图1:IOC解耦过程
大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。
我们再来做个试验:把上图中间的IOC容器拿掉,然后再来看看这套系统:
图2:拿掉IoC容器后的系统
我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发而言,这将是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!
我们再来看看,控制反转(IOC)到底为什么要起这么个名字?我们来对比一下:
软件系统在没有引入IOC容器之前,如图1所示,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。
软件系统在引入IOC容器之后,这种情形就完全改变了,如图3所示,由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。
通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。
依赖注入()
2004年,Martin Fowler探讨了同一个问题,既然IOC是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现IOC的方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。
所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。
我们举一个生活中的例子,来帮助理解依赖注入的过程。大家对USB接口和USB设备应该都很熟悉吧,USB为我们使用电脑提供了很大的方便,现在有很多的外部设备都支持USB接口。
图3:USB接口和USB设备
现在,我们利用电脑主机和USB接口来实现一个任务:从外部USB设备读取一个文件。
电脑主机读取文件的时候,它一点也不会关心USB接口上连接的是什么外部设备,而且它确实也无须知道。它的任务就是读取USB接口,挂接的外部设备只要符合USB接口标准即可。所以,如果我给电脑主机连接上一个U盘,那么主机就从U盘上读取文件;如果我给电脑主机连接上一个外置硬盘,那么电脑主机就从外置硬盘上读取文件。挂接外部设备的权力由我作主,即控制权归我,至于USB接口挂接的是什么设备,电脑主机是决定不了,它只能被动的接受。电脑主机需要外部设备的时候,根本不用它告诉我,我就会主动帮它挂上它想要的外部设备,你看我的服务是多么的到位。这就是我们生活中常见的一个依赖注入的例子。在这个过程中,我就起到了IOC容器的作用。
通过这个例子,依赖注入的思路已经非常清楚:当电脑主机读取文件的时候,我就把它所要依赖的外部设备,帮他挂接上。整个外部设备注入的过程和一个被依赖的对象在系统运行时被注入另外一个对象内部的过程完全一样。
我们把依赖注入应用到软件系统中,再来描述一下这个过程:
对象A依赖于对象B,当对象 A需要用到对象B的时候,IOC容器就会立即创建一个对象B送给对象A。IOC容器就是一个对象制造工厂,你需要什么,它会给你送去,你直接使用就行了,而再也不用去关心你所用的东西是如何制成的,也不用关心最后是怎么被销毁的,这一切全部由IOC容器包办。
在传统的实现中,由内部来控制组件之间的关系。我们经常使用new关键字来实现两个组件之间关系的组合,这种实现方式会造成组件之间耦合。IOC很好地解决了该问题,它将实现组件间关系从程序内部提到外部容器,也就是说由容器在运行期将组件间的某种依赖关系动态注入组件中。
IOC为我们带来了什么好处
我们还是从USB的例子说起,使用USB外部设备比使用内置硬盘,到底带来什么好处?
第一、USB设备作为电脑主机的外部设备,在插入主机之前,与电脑主机没有任何的关系,只有被我们连接在一起之后,两者才发生联系,具有相关性。所以,无论两者中的任何一方出现什么的问题,都不会影响另一方的运行。这种特性体现在软件工程中,就是可维护性比较好,非常便于进行单元测试,便于调试程序和诊断故障。代码中的每一个Class都可以单独测试,彼此之间互不影响,只要保证自身的功能无误即可,这就是组件之间低耦合或者无耦合带来的好处。
第二、USB设备和电脑主机的之间无关性,还带来了另外一个好处,生产USB设备的厂商和生产电脑主机的厂商完全可以是互不相干的人,各干各事,他们之间唯一需要遵守的就是USB接口标准。这种特性体现在软件开发过程中,好处可是太大了。每个开发团队的成员都只需要关心实现自身的业务逻辑,完全不用去关心其它的人工作进展,因为你的任务跟别人没有任何关系,你的任务可以单独测试,你的任务也不用依赖于别人的组件,再也不用扯不清责任了。所以,在一个大中型项目中,团队成员分工明确、责任明晰,很容易将一个大的任务划分为细小的任务,开发效率和产品质量必将得到大幅度的提高。
第三、同一个USB外部设备可以插接到任何支持USB的设备,可以插接到电脑主机,也可以插接到DV机,USB外部设备可以被反复利用。在软件工程中,这种特性就是可复用性好,我们可以把具有普遍性的常用组件独立出来,反复利用到项目中的其它部分,或者是其它项目,当然这也是面向对象的基本特征。显然,IOC不仅更好地贯彻了这个原则,提高了模块的可复用性。符合接口标准的实现,都可以插接到支持此标准的模块中。
第四、同USB外部设备一样,模块具有热插拔特性。IOC生成对象的方式转为外置方式,也就是把对象生成放在配置文件里进行定义,这样,当我们更换一个实现子类将会变得很简单,只要修改配置文件就可以了,完全具有热插拨的特性。
以上几点好处,难道还不足以打动我们,让我们在项目开发过程中使用IOC框架吗?
IOC容器的技术剖析
IOC中最基本的技术就是“反射(Reflection)”编程,目前.Net C#、Java和PHP5等语言均支持,其中PHP5的技术书籍中,有时候也被翻译成“映射”。有关反射的概念和用法,大家应该都很清楚,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象。这种编程方式可以让对象在生成时才决定到底是哪一种对象。反射的应用是很广泛的,很多的成熟的框架,比如象Java中的Hibernate、Spring框架,.Net中 NHibernate、Spring.Net框架都是把“反射”做为最基本的技术手段。
反射技术其实很早就出现了,但一直被忽略,没有被进一步的利用。当时的反射编程方式相对于正常的对象生成方式要慢至少得10倍。现在的反射技术经过改良优化,已经非常成熟,反射方式生成对象和通常对象生成方式,速度已经相差不大了,大约为1-2倍的差距。
我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
下面我们来看一个使用轻量级的IoC架构---StructureMap 的例子
using System.Collections.G
using System.L
using System.T
using StructureM
namespace ConsoleApplication1
class Program
private static void Main(string[] args)
ConfigureDependencies();
IAppEngine appEngine = ObjectFactory.GetInstance&IAppEngine&();
appEngine.Run();
private static void ConfigureDependencies()
StructureMapConfiguration.ForRequestedType&IAppEngine&().TheDefaultIsConcreteType&AppEngine&();
StructureMapConfiguration.ForRequestedType&IGreeter&().TheDefaultIsConcreteType&EnglishGreeter&();
StructureMapConfiguration.ForRequestedType&IOutputDisplay&().TheDefaultIsConcreteType&ConsoleOutputDisplay&();
public class AppEngine : IAppEngine
private readonly IG
private readonly IOutputDisplay outputD
public AppEngine(IGreeter greeter, IOutputDisplay outputDisplay)
this.greeter =
this.outputDisplay = outputD
public void Run()
outputDisplay.Show(greeter.GetGreeting());
public interface IAppEngine
void Run();
public interface IGreeter
string GetGreeting();
public class EnglishGreeter : IGreeter
public string GetGreeting()
return "Hello";
public class FrenchGreeter : IGreeter
public string GetGreeting()
return "Bonjour";
public interface IOutputDisplay
void Show(string message);
public class ConsoleOutputDisplay : IOutputDisplay
public void Show(string message)
Console.WriteLine(message);
&&&&推荐文章:
【上篇】【下篇】

我要回帖

更多关于 快穿之前任女友大反转 的文章

 

随机推荐