Spring的Ioc与DI

Posted 賣贾笔的小男孩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring的Ioc与DI相关的知识,希望对你有一定的参考价值。

一、前言

  Spring框架的核心基于控制反转的原理。 IoC是一种将组件依赖关系的创建和管理外部化的技术。 考虑一个示例,其中Foo类依赖于Bar类的实例来执行某种处理。 传统上,Foo使用new运算符创建Bar的实例,或者从某种工厂类中获取一个实例。 使用IoC方法,运行时某些外部进程会将Bar的实例(或子类)提供给Foo。 这种行为,即在运行时注入依赖项,导致IoC被Martin Fowler重命名为更具描述性的依赖项注入(DI)。依赖注入是IoC的一种特殊形式,尽管您经常会发现这两个术语可以互换使用。

  Spring的DI实现基于两个Java核心概念:JavaBean和接口(interface)。

  当使用Spring的DI为程序提供依赖时,您将获得以不同方式(例如XML文件,Java配置类,代码中的注释或新的Groovy bean定义方法)在应用程序中定义依赖项配置的灵活性。 JavaBeans(POJO)提供了一种标准的机制来创建可通过多种方式配置的Java资源,例如构造函数和setter方法。实际上,任何Spring管理的资源都称为Bean。

  接口和DI是互惠互利的技术。显然,将应用程序设计和编码到接口可以构成一个灵活的应用程序,但是将使用接口设计的应用程序连接在一起的复杂性很高,给开发人员带来了额外的编码负担。通过使用DI,可以将在应用程序中使用基于接口的设计所需的代码量减少到几乎为零。同样,通过使用接口,您可以最大程度地利用DI,因为bean可以利用任何接口实现来满足它们的依赖性。接口的使用还允许Spring利用JDK动态代理(代理模式)来提供强大的概念,例如AOP来解决横切关注点。

  在DI的上下文中,Spring的作用比框架更像容器(为应用程序类的实例提供所需的所有依赖关系),但是这样做的方式要少得多。使用Spring for DI仅依赖于在类中遵循JavaBeans命名约定-没有要继承的特殊类或要遵循的专有命名方案。如果有的话,您在使用DI的应用程序中所做的唯一更改就是在JavaBeans上公开了更多属性,从而允许在运行时注入更多的依赖项。

二、依赖注入的演变

  在过去的几年中,由于Spring和其他DI框架的流行,DI在Java开发人员社区中得到了广泛的接受。同时,开发人员坚信使用DI是应用程序开发中的最佳实践,并且也充分了解了使用DI的好处。当Java社区流程(JCP)在2009年采用JSR-330(Java依赖注入)时,就认可了DI的流行。JSR-330已经成为一个正式的Java规范要求,正如您所期望的,规范的领先者之一是Rod Johnson-Spring Framework的创始人。在JEE 6中,JSR-330成为整个技术堆栈中包含的规范之一。同时,EJB架构(从3.0版开始)也进行了重大改进。它采用了DI模型,以简化各种Enterprise JavaBeans应用程序的开发。

  相对于非传统方法,使用DI的好处有:

  • 减少粘合代码:DI的最大优点之一是它可以显着减少将应用程序的组件粘合在一起所需编写的代码量。

  • 简化的应用程序配置:通过采用DI,可以大大简化配置应用程序的过程。可以使用多种选项进行配置那些可以注入其他类的类,还可以使用相同的技术向“注入者”表达依赖性要求以注入适当的bean实例或属性。

  • 能够在单个存储库中管理公共依赖项的能力:使用传统方法对公共服务(例如,数据源连接,事务和远程服务)进行依赖项管理,你可以在其中创建依赖项的实例(或从某些工厂类中查找) 是必需的(在依赖类中),这将导致依赖关系散布在应用程序的各个类中,并且更改它们可能会带来问题。 当使用DI时,有关这些公共依存关系的所有信息都包含在单个存储库中,这使得对依存关系的管理变得更加简单且不易出错。

  • 优化可测试性:在为DI设计类时,可以轻松替换依赖项。

  • 培养良好的应用程序设计:DI设计通常意味着针对接口进行设计。设计了一个典型的面向注入的应用程序,以便将所有主要组件定义为接口,然后使用DI容器创建这些接口的具体实现并将其挂钩。

  实际上,依赖注入是IoC的一种特殊形式,你可以发现经常会发现这两个术语可以互换使用。

  IoC以及DI旨在提供一种更简单的机制来配置组件依赖关系(通常称为对象的 协作者)并在其整个生命周期中管理这些依赖关系。 需要某些依赖关系的组件通常称为依赖对象,在IoC的情况下,也称为目标。 通常,IoC可以分解为两个子类型:依赖项注入和依赖项查找

  这些子类型进一步分解为IoC服务的具体实现。 从这个定义中,您可以清楚地看到,当我们谈论DI时,我们总是在谈论IoC,但是当我们谈论IoC时,我们并不总是在谈论DI(例如,依赖关系查找也是IoC的一种形式)。

  为什么有两种类型的IoC,以及为什么将这些类型进一步拆分为不同的实现。这个问题似乎没有明确的答案,IoC似乎更多是新旧思想的结合。 IoC的两种类型代表了这一点。依赖查找是一种更为传统的方法,乍一看,Java程序员似乎更加熟悉。依赖注入虽然起初看起来是违反直觉的,但实际上比依赖查找要灵活得多且可用。使用依赖项查找样式的IoC,组件必须获取对依赖项的引用,而使用依赖项注入,则依赖项将由IoC容器注入到组件中。

  依赖关系查找有两种类型:依赖关系拉取和上下文相关性依赖关系查找(CDL)。依赖注入还具有两种常见的风格:构造函数和setter依赖注入。

三、依赖项查找

① 依赖关系拉取

   对于Java开发人员而言,依存关系拉动是最常见的IoC类型。 在依赖项拉取中,需要时从注册表中提取依赖项。 曾经编写过代码以访问EJB(2.1或更早版本)的任何人都使用过依赖项拉取(即通过JNDI API查找EJB组件)。下图显示了通过查找机制进行依赖项拉取的场景。

  

  Spring还提供依赖项拉取作为检索框架管理的组件的机制。下面的代码示例显示了在基于Spring的应用程序中的典型依赖项拉取查找:

 1 package com.apress.prospring5.ch3;
 2 
 3 import com.apress.prospring5.ch2.decoupled.MessageRenderer;
 4 import org.springframework.context.ApplicationContext;
 5 import org.springframework.context.support.ClassPathXmlApplicationContext;
 6 
 7 public class DependencyPull {
 8     public static void main(String... args) {
 9         ApplicationContext ctx = new ClassPathXmlApplicationContext
10            ("spring/app-context.xml");
11 
12         MessageRenderer mr = ctx.getBean("renderer", MessageRenderer.class);
13         mr.render();
14     }
15 }

  这种IoC不仅在基于JEE的应用程序中流行(使用EJB 2.1或更低版本),该应用程序广泛使用JNDI查找来从注册表中获取依赖关系,而且对于在使用Spring的许多环境中至关重要。

② 上下文相关性依赖关系查找

  在某些方面,上下文相关的依赖关系查找(CDL)与依赖关系拉取类似,但是在CDL中,查找是针对正在管理资源的容器执行的,而不是从某些中央注册表中进行的,并且通常在某个设置点执行。 CDL机制如图所示。

  CDL通过使组件实现与以下代码段相似的接口来工作:

1 package com.apress.prospring5.ch3;
2 public interface ManagedComponent {
3  void performLookup(Container container);
4 }

  通过实现此接口,组件可以向容器发出要获得依赖关系的信号。 容器通常由基础应用程序服务器或框架(例如Tomcat或JBoss)或框架(例如Spring)提供。 下面的代码片段显示了一个简单的Container接口,该接口提供了依赖项查找服务:

1 package com.apress.prospring5.ch3;
2 public interface Container {
3  Object getDependency(String key);
4 }

  当容器准备好将依赖项传递给组件时,它将依次在每个组件上调用performLookup()。 然后,该组件可以使用Container接口查找其依赖项,如以下代码片段所示:

 1 package com.apress.prospring5.ch3;
 2 public class ContextualizedDependencyLookup
 3  implements ManagedComponent {
 4    private Dependency dependency;
 5    @Override
 6    public void performLookup(Container container) {
 7        this.dependency = (Dependency) container.getDependency("myDependency");
 8    }
 9    @Override
10    public String toString() {
11        return dependency.toString();
12    }
13 }

四、依赖项注入

① 构造函数依赖注入

  当在组件的构造函数中向组件提供组件的依赖项时,就会发生构造函数依赖项注入。 组件声明一个构造函数或一组构造函数,并将其依赖项作为参数,并且在实例化发生时,IoC容器将依赖项传递给组件,如以下代码片段所示:

 1 package com.apress.prospring5.ch3;
 2 public class ConstructorInjection {
 3    private Dependency dependency;
 4    public ConstructorInjection(Dependency dependency) {
 5      this.dependency = dependency;
 6    }
 7    @Override
 8    public String toString() {
 9      return dependency.toString();
10    }
11 }

  使用构造函数注入的一个明显结果是,没有依赖关系的对象就无法创建对象。 因此,它们是强制性的。

② Setter依赖注入

  在setter依赖注入中,IoC容器通过JavaBean样式的setter方法注入组件的依赖。 组件的设置程序公开了IoC容器可以管理的依赖项。 以下代码示例显示了一个典型的基于setter依赖注入的组件:

 1 package com.apress.prospring5.ch3;
 2 public class SetterInjection {
 3      private Dependency dependency;
 4      public void setDependency(Dependency dependency) {
 5          this.dependency = dependency;
 6      }
 7      @Override
 8      public String toString() {
 9          return dependency.toString();
10      }
11 }

  使用setter注入的明显结果是可以创建一个没有对象依赖关系的对象,并且可以在以后通过调用setter来提供它们。 在容器内,由setDependency()方法公开的依赖项要求由JavaBeans样式名称dependency引用。 实际上,setter注入是使用最广泛的注入机制,它是最简单的IoC机制之一。

③ 接口注入

 

1 public class ClassA {  
2     private InterfaceB clzB;   
3     public init() {
4         Ojbect obj =Class.forName(Config.BImplementation).newInstance();   
5         clzB = (InterfaceB)obj;  
6     } ……   
7 }  

 

  上面的代码中,ClassA依赖于InterfaceB的实现,如何获得InterfaceB实现类的实例?传统的方法是在代码中创建InterfaceB实现类的实例,并将起赋予clzB.

 

④ 字段注入

  Spring提供了另一种类型的注入,称为字段注入,是使用@Autowire注释进行自动装配的。

  在Spring的IoC设计中,前两种注入是主要的注入方式。

五、依赖查找与依赖注入的选择

  选择使用哪种样式的IoC(注入或查找)通常并不困难。 在许多情况下,您使用的IoC类型由您使用的容器规定。 例如,如果您使用的是EJB 2.1或更早版本,则必须使用查找样式的IoC(通过JNDI)从JEE容器中获取EJB。 在Spring中,除了最初的bean查找之外,您的组件及其依赖项始终使用注入样式的IoC连接在一起。

  一般选用依赖注入,一方面使用依赖注入对组件的代码的影响为零,另一方面,依赖项提取的代码必须主动获取对注册表的引用,并与之交互以获取依赖项,并且使用CDL要求你的类实现特定的接口并手动查找所有依赖项。当你使用依赖注入时,你对类要做的大多数事情是允许使用构造函数或设置器注入依赖项。使用注入,可以自由使用完全与IoC容器分离的类(实现解耦),该IoC容器手动为其依赖对象提供依赖对象,而通过查找,你的类始终依赖于容器定义的类和接口。依赖查找的另一个缺点是,很难与容器隔离地测试你的类。使用依赖注入,测试组件很简单,因为你可以使用适当的构造函数或设置器简单地自己提供依赖项。

  基于查找的解决方案必然比基于注入的解决方案更为复杂,尽管不用担心复杂性,但我们应该反思一下有没有必要将不必要的复杂性添加到项目至关重要的依赖管理中。除开这些原因,选择依赖注入而不是依赖查找的最大原因是它使你更轻松。使用注入时,你编写的代码要少得多,并且编写的代码很简单,并且通常可以由良好的IDE自动执行。你会注意到注入示例中的所有代码都是被动的,因为它不会主动尝试完成任务。在注入代码中看到的最令人兴奋的事情是对象仅存储在字段中。从任何注册表或容器中提取依赖项都不会涉及其他代码。因此,该代码更加简单,并且不易出错。被动代码比主动代码维护起来容易得多,因为几乎不会出错。考虑以下来自CDL示例的代码:

1 public void performLookup(Container container) {
2      this.dependency = (Dependency) container.getDependency("myDependency");
3 }

  在此代码中,可能会出错:依赖项键可能会更改,容器实例可能为null或返回的依赖项可能是错误的类型。 我们将此代码称为具有很多活动部分的代码,因为很多事情可能会中断。 使用依赖关系查找可能会使应用程序的组件分离,但是这增加了将这些组件重新耦合在一起以执行任何有用任务所需的附加代码的复杂性。

六、Setter注入与构造函数注入

  现在我们已经确定了依赖注入方法是更可取的,但仍然需要选择使用setter注入还是构造函数注入。 当在使用组件之前绝对必须具有依赖项类的实例时,构造函数注入特别有用。 许多容器(包括Spring)提供了一种机制,用于确保在使用setter注入时定义了所有依赖关系,但是通过使用构造函数注入,你可以通过与容器无关的方式来声明对依赖关系的要求。 构造函数注入还有助于实现不可变对象的使用。 setter注入在各种情况下都是有用的如果组件将其依赖项暴露给容器,相反还很乐意提供自己的默认值,则setter注入通常是实现此目的的最佳方法。 setter注入的另一个好处是,它允许在接口上声明依赖项,尽管它没有你首先想到的那样有用。 考虑具有一种业务方法defineMeaningOfLife() 的典型业务接口。 如果除了此方法之外,还为注入定义了setter(例如setEncylopedia()),则要求所有实现都必须使用或至少了解Encyclopedia的依赖性。 但是,您无需在业务界面中定义setEncylopedia()。 相反,您可以在实现业务接口的类中定义方法。 以这种方式进行编程时,所有最近的IoC容器(包括Spring)都可以在业务接口方面与组件一起使用,但仍提供实现类的依赖关系。 这样的一个例子可能会稍微澄清这个问题。 在以下代码片段中考虑业务接口:

1 package com.apress.prospring5.ch3;
2 public interface Oracle {
3      String defineMeaningOfLife();
4 }

  注意,业务接口没有为依赖项注入定义任何setter。 可以如以下代码片段所示实现此接口:

 1 package com.apress.prospring5.ch3;
 2 public class BookwormOracle implements Oracle {
 3      private Encyclopedia encyclopedia;
 4      public void setEncyclopedia(Encyclopedia encyclopedia) {
 5          this.encyclopedia = encyclopedia;
 6      }
 7      @Override
 8      public String defineMeaningOfLife() {
 9          return "Encyclopedias are a waste of money - go see the world instead";
10      }
11 }    

  如你所见,BookwormOracle类不仅实现Oracle接口,还定义了用于依赖项注入的setter。 Spring非常适合处理这样的结构。绝对没有必要在业务接口上定义依赖项。使用接口定义依赖项的能力是setter注入的一个经常被吹捧的好处,但是实际上,应该努力将仅用于注入的setter保留在接口之外。除非你完全确定特定业务接口的所有实现都需要特定的依赖关系,否则请让每个实现类定义自己的依赖关系并为业务方法保留业务接口。虽说你不应该总是在业务界面中放置依赖项的设置器,但是在业务界面中放置配置参数的设置器和获取器是一个好主意,并且可以使设置器注入成为有价值的工具。可以认为配置参数是依赖项的特例。当然,你的组件依赖于配置数据,但是配置数据与到目前为止所看到的依赖关系类型明显不同。现在,思考一下以下代码片段中所示的业务接口:

1 package com.apress.prospring5.ch3;
2 public interface NewsletterSender {
3      void setSmtpServer(String smtpServer);
4      String getSmtpServer();
5      void setFromAddress(String fromAddress);
6      String getFromAddress();
7      void send();
8 }

  通过电子邮件发送一组新闻通讯的类实现了NewsletterSender接口。 send()方法是唯一的业务方法,但是请注意,我们在接口上定义了两个JavaBean属性。 刚刚说不应该在业务接口中定义依赖项时,为什么要这样做? 原因是这些值,SMTP服务器地址和电子邮件发送的地址在实际意义上不是依赖性。 相反,它们是配置详细信息,它们影响NewsletterSender接口的所有实现的功能。 那么这里的问题是:配置参数和任何其他类型的依赖关系之间有什么区别? 在大多数情况下,你可以清楚地看到是否应将依赖项分类为配置参数,但是如果不确定,请查找指向配置参数的以下三个特征:

  • 配置参数是被动的。在前面的代码片段中描述的NewsletterSender示例中,SMTP服务器参数是被动依赖项的示例。被动依赖项不直接用于执行动作;相反,它们在内部或由其他依赖项使用以执行其操作。
  • 配置参数通常是信息,而不是其他组件。意思是,配置参数通常是组件完成其工作所需的一些信息。显然,SMTP服务器是NewsletterSender所需的一条信息,但MessageProvider实际上是MessageRenderer需要正确运行的另一个组件。
  • 配置参数通常是简单值或简单值的集合。这实际上是前两点的副产品,但是配置参数通常是简单值。在Java中,这意味着它们是原语(或相应的包装器类)或String或这些值的集合。简单值通常是被动的。这意味着除了操作字符串表示的数据外,您不能做太多事情。并且你几乎总是将这些值用主要用于存信息,例如,一个int值表示一个网络套接字应侦听的端口号,或者一个String表示一个SMTP服务器,电子邮件程序应通过该服务器发送消息。

  在考虑是否在业务接口中定义配置选项时,还要考虑配置参数是适用于业务接口的所有实现还是仅适用于一个。 例如,对于NewsletterSender的实现,很明显,所有实现都需要知道在发送电子邮件时使用哪个SMTP服务器。 但是,我们可能会选择保留配置选项,该选项用于标记是否从业务接口发送安全电子邮件,因为并非所有电子邮件API都能够做到这一点,因此可以正确地假设许多实现不会将安全性纳入考虑范围。

  Setter注入还允许你即时为其他实现交换依赖关系,而无需创建父组件的新实例。Spring的JMX支持也使这成为可能。 setter注入的最大好处也许是它对注入机制的干扰最小。 

  通常,应该根据用例选择注入类型。 基于Setter的注入允许在不创建新对象的情况下换出依赖项,还可以让你的类选择适当的默认值而无需显式注入对象。当你要确保将依赖项传递给组件以及在设计不可变对象时,构造函数注入是一个不错的选择

七、Spring中的控制反转

  如前所述,控制反转Ioc是Spring所做的很大一部分。尽管实现了依赖查找功能,但Spring实现的核心是基于依赖注入。当Spring自动将协作者提供给依赖对象时,它会使用依赖注入。在基于Spring的应用程序中,总是最好使用依赖注入将协作者传递给依赖对象,而不是让依赖对象通过查找来获得协作者。下图显示了Spring的依赖项注入机制。尽管依赖关系注入是将协作者和依赖对象连接在一起的首选机制,但是你需要进行依赖关系查找才能访问依赖对象。在许多环境中,Spring无法使用依赖注入自动连接所有应用程序组件,并且必须使用依赖查找来访问组件的初始集合。例如,在独立的Java应用程序中,您需要在main()方法中引导Spring的容器,并获取依赖项(通过ApplicationContext接口)以进行编程处理。但是,当你使用Spring的MVC支持构建Web应用程序时,Spring可以通过自动将整个应用程序粘合在一起来避免这种情况。只要有可能在Spring中使用依赖注入,就应该这样做。否则,你可以依赖于依赖项查找功能。

 

   Spring的IoC容器的一个有趣的功能是,它可以充当其自己的依赖项注入容器和外部依赖项查找容器之间的适配器。Spring同时支持构造函数和setter注入,并通过大量有用的附加功能来支持标准IoC功能集,是程序员编程变得更轻松。

八、Spring中的依赖注入

① Beans和BeanFactory

  Spring的依赖项注入容器的核心是BeanFactory接口。 BeanFactory负责管理组件,包括组件的依赖关系及其生命周期。 在Spring中,术语Bean用于指代容器管理的任何组件。 通常,你的bean在某种程度上遵守JavaBeans规范,但这不是必需的,特别是如果你打算使用构造函数注入将您的bean连接在一起。 

  如果你的应用程序仅需要DI支持,则可以通过BeanFactory接口与Spring DI容器进行交互。 在这种情况下,你的应用程序必须创建一个实现BeanFactory接口的类的实例,并使用Bean和依赖项信息对其进行配置。 完成之后,你的应用程序可以通过BeanFactory访问bean并继续进行处理。

  在某些情况下,所有这些设置都是自动处理的(例如,在Web应用程序中,Spring的ApplicationContext将在应用程序启动期间通过web.xml描述符文件中声明的Springprovided ContextLoaderListener类由Web容器引导)。 但是在许多情况下,需要自己编写设置代码。

  尽管可以通过编程方式配置BeanFactory,但更常见的是使用某种配置文件在外部对其进行配置在内部,bean配置由实现BeanDefinition接口的类的实例表示。 bean配置不仅存储有关bean本身的信息,而且还存储有关其依赖的bean的信息。 对于还实现BeanDefinitionReader接口的任何BeanFactory实现类,你都可以使用PropertiesBeanDefinitionReader或XmlBeanDefinitionReader从配置文件中读取BeanDefinition数据。 PropertiesBeanDefinitionReader从属性文件读取bean定义,而XmlBeanDefinitionReader从XML文件读取。

  因此,你可以在BeanFactory中标识你的bean。 可以为每个bean分配一个ID,一个名称,或同时分配两者。 一个bean也可以实例化而没有任何ID或名称(称为匿名bean)或另一个bean中的内部bean。 每个bean至少具有一个名称,但可以具有任意数量的名称(其他名称用逗号分隔)。 名字之后的任何名称均被视为同一bean的别名。 您可以使用bean的ID或名称从BeanFactory检索bean并建立依赖关系(即bean X取决于bean Y)。

② BeanFactory实现

  BeanFactory接口的描述可能看起来过于复杂,但实际上并非如此。 看一个简单的例子,假设您有一个模仿上面Oracle接口的实现:

 1 //interface
 2 package com.apress.prospring5.ch3;
 3 public interface Oracle {
 4      String defineMeaningOfLife();
 5 }
 6 
 7 //implementation
 8 package com.apress.prospring5.ch3;
 9 public class BookwormOracle implements Oracle {
10      private Encyclopedia encyclopedia;
11      public void setEncyclopedia(Encyclopedia encyclopedia) {
12          this.encyclopedia = encyclopedia;
13      }
14      @Override
15      public String defineMeaningOfLife() {
16          return "Encyclopedias are a waste of money - go see the world instead";
17      }
18 }

  现在,让我们看看在独立的Java程序中如何初始化Spring的BeanFactory并获取用于处理的oracle bean。 这是代码:

 1 package com.apress.prospring5.ch3;
 2 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
 3 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
 4 import org.springframework.core.io.ClassPathResource;
 5 public class XmlConfigWithBeanFactory {
 6      public static void main(String... args) {
 7          DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
 8          XmlBeanDefinitionReader rdr = new XmlBeanDefinitionReader(factory);
 9          rdr.loadBeanDefinitions(new
10              ClassPathResource("spring/xml-bean-factory-config.xml"));
11          Oracle oracle = (Oracle) factory.getBean("oracle");
12          System.out.println(oracle.defineMeaningOfLife());
13      }
14 }        

  在上面的代码示例中,可以看到使用了DefaultListableBeanFactory,它是Spring随附的两个主要BeanFactory实现之一,并且我们正在使用XmlBeanDefinitionReader从XML文件中读取BeanDefinition信息。 创建并配置BeanFactory实现后,将使用在XML配置文件中配置的名称oracle来检索oracle bean。

1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4  xsi:schemaLocation="http://www.springframework.org/schema/beans
5  http://www.springframework.org/schema/beans/spring-beans.xsd">
6 <bean id="oracle"
7      name="wiseworm"
8      class="com.apress.prospring5.ch3.BookwormOracle"/>
9 </beans>

  上面这个xml文件声明了一个Spring bean,为其提供了oracle的ID和wiseworm的名称,并告诉Spring底层实现类是com.apress.prospring4.ch3.BookwormOracle。定义好配置后,运行前面代码示例中显示的程序; 您将在控制台输出中看到由defineMeaningOfLife() 方法返回的短语。

  除了XmlBeanDefinitionReader,Spring还提供了PropertiesBeanDefinitionReader,它使您可以通过使用属性而不是XML来管理bean的配置。 尽管properties是比较小,简单应用程序的理想选择,但是当你处理大量bean时,它们很快就会变得很麻烦。 因此,最好将XML配置格式用于除最琐碎的应用程序之外的所有应用程序

  当然,你可以自由定义自己的BeanFactory实现,尽管要知道这样做很麻烦。为了获得与提供的BeanFactory实现相同级别的功能,你需要实现的不仅是BeanFactory,还需要实现更多的接口。 如果您要做的只是定义新的配置机制,请通过开发扩展DefaultListableBeanFactory类的类来创建定义读取器,该类已实现BeanFactory接口。

③ ApplicationContext

  在Spring中,ApplicationContext接口是BeanFactory的扩展。除DI服务外,ApplicationContext还提供其他服务,例如事务和AOP服务,国际化消息源(i18n)和应用程序事件处理等。 在开发基于Spring的应用程序时,建议通过ApplicationContext接口与Spring进行交互。 Spring通过手动编码(手动实例化并加载适当的配置)或在Web容器环境中通过ContextLoaderListener来支持ApplicationContext的引导。 

 

以上是关于Spring的Ioc与DI的主要内容,如果未能解决你的问题,请参考以下文章

Spring中IOC与DI的的区别

Spring基础-IOC与DI(控制反转与依赖注入)

spring 依赖注入(DI)与控制反转(IOC)

Spring整合web项目原理以及IOC与DI关系

spring IOC(控制反转)及DI(依赖注入)

四Spring 中的 DI 与 IOC