关于依赖注入ioc/di的感想

麦子TMAC 麦子TMAC     2022-08-15     591

关键词:

  之前一直不明白依赖注入有什么好处,甚至觉得它是鸡肋,现在想想,当时真是可笑。

  这个想法正如同说接口是没有用处一样。

  当整个项目非常庞大,各个方法之间的调用非常复杂,那么,可以想象一下,假设说没有任何的分离模块的想法,各个关系非常的复杂,不便于维护以及查找bug等等。这个时候,就需要一个东西,去将这么多复杂的玩意分离开来,很喜欢的一句话:高内聚,低耦合。

  举个栗子,现在一个很简单的功能,可能只需要通过一些方法互相调用就可以实现,OK,那么随着需求的增多,现在添加了几个功能,那么,我们把相同的东西划分为一个类吧,类由此而生,同样,类似的,一层一层如下所示:

方法-->类-->接口-->模块-->甚至把各个模块之间的关系抽象出来(比如IOC/DI)

 

 

  总之,一个核心的思想就是,对于一个大项目来说,把所有的模块尽可能的多分几个,一个里面的东西太多的话,多分开几个写,一句话表达:高内聚,低耦合

 

浅谈依赖注入

 

最近几天在看一本名为Dependency Injection in .NET 的书,主要讲了什么是依赖注入,使用依赖注入的优点,以及.NET平台上依赖注入的各种框架和用法。在这本书的开头,讲述了软件工程中的一个重要的理念就是关注分离(Separation of concern, SoC)。依赖注入不是目的,它是一系列工具和手段,最终的目的是帮助我们开发出松散耦合(loose coupled)、可维护、可测试的代码和程序。这条原则的做法是大家熟知的面向接口,或者说是面向抽象编程。

关于什么是依赖注入,在Stack Overflow上面有一个问题,如何向一个5岁的小孩解释依赖注入,其中得分最高的一个答案是:

“When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn’t want you to have. You might even be looking for something we don’t even have or which has expired.

What you should be doing is stating a need, “I need something to drink with lunch,” and then we will make sure you have something when you sit down to eat.”

映射到面向对象程序开发中就是:高层类(5岁小孩)应该依赖底层基础设施(家长)来提供必要的服务。

编写松耦合的代码说起来很简单,但是实际上写着写着就变成了紧耦合。

使用例子来说明可能更简洁明了,首先来看看什么样的代码是紧耦合。

1 不好的实现

编写松耦合代码的第一步,可能大家都熟悉,那就是对系统分层。比如下面的经典的三层架构。

Classic 3-tier architecture

分完层和实现好是两件事情,并不是说分好层之后就能够松耦合了。

1.1 紧耦合的代码

有很多种方式来设计一个灵活的,可维护的复杂应用,但是n层架构是一种大家比较熟悉的方式,这里面的挑战在于如何正确的实现n层架构。

假设要实现一个很简单的电子商务网站,要列出商品列表,如下:

product list page

下面就具体来演示通常的做法,是如何一步一步把代码写出紧耦合的。

1.1.1 数据访问层

要实现商品列表这一功能,首先要编写数据访问层,需要设计数据库及表,在SQLServer中设计的数据库表Product结构如下:

Product Table

表设计好之后,就可以开始写代码了。在Visual Studio 中,新建一个名为DataAccessLayer的工程,添加一个ADO.NET Entity Data Model,此时Visual Studio的向导会自动帮我们生成Product实体和ObjectContext DB操作上下文。这样我们的 Data Access Layer就写好了。

Product Entity Model

1.1.2 业务逻辑层

表现层实际上可以直接访问数据访问层,通过ObjectContext 获取Product 列表。但是大多数情况下,我们不是直接把DB里面的数据展现出来,而是需要对数据进行处理,比如对会员,需要对某些商品的价格打折。这样我们就需要业务逻辑层,来处理这些与具体业务逻辑相关的事情。

新建一个类库,命名为DomainLogic,然后添加一个名为ProductService的类:

public class ProductService {
    private readonly CommerceObjectContext objectContext;

    public ProductService()
    {
        this.objectContext = new CommerceObjectContext();
    }

    public IEnumerable<Product> GetFeaturedProducts(
        bool isCustomerPreferred)
    {
        var discount = isCustomerPreferred ? .95m : 1;
        var products = (from p in this.objectContext
                            .Products
                        where p.IsFeatured
                        select p).AsEnumerable();
        return from p in products
                select new Product
                {
                    ProductId = p.ProductId,
                    Name = p.Name,
                    Description = p.Description,
                    IsFeatured = p.IsFeatured,
                    UnitPrice = p.UnitPrice * discount
                };
    }
}

现在我们的业务逻辑层已经实现了。

1.1.3 表现层

现在实现表现层逻辑,这里使用ASP.NET MVC,在Index 页面的Controller中,获取商品列表然后将数据返回给View。

public ViewResult Index()
{
    bool isPreferredCustomer = 
        this.User.IsInRole("PreferredCustomer");

    var service = new ProductService();
    var products = 
        service.GetFeaturedProducts(isPreferredCustomer);
    this.ViewData["Products"] = products;

    return this.View();
}

然后在View中将Controller中返回的数据展现出来:

<h2>Featured Products</h2>
<div>
<% var products =
        (IEnumerable<Product>)this.ViewData["Products"];
    foreach (var product in products)
    { %>
    <div>
    <%= this.Html.Encode(product.Name) %>
    (<%= this.Html.Encode(product.UnitPrice.ToString("C")) %>)
    </div>
<% } %>
</div>

1.2 分析

现在,按照三层“架构”我们的代码写好了,并且也达到了要求。整个项目的结构如下图:

 Solution layout

这应该是我们通常经常写的所谓的三层架构。在Visual Studio中,三层之间的依赖可以通过项目引用表现出来。

1.2.1 依赖关系图

现在我们来分析一下,这三层之间的依赖关系,很明显,上面的实现中,DomianLogic需要依赖SqlDataAccess,因为DomainLogic中用到了Product这一实体,而这个实体是定义在DataAccess这一层的。WebUI这一层需要依赖DomainLogic,因为ProductService在这一层,同时,还需要依赖DataAccess,因为在UI中也用到了Product实体,现在整个系统的依赖关系是这样的:

Dependency graph in three-tier architecture

1.2.2 耦合性分析

使用三层结构的主要目的是分离关注点,当然还有一个原因是可测试性。我们应该将领域模型从数据访问层和表现层中分离出来,这样这两个层的变化才不会污染领域模型。在大的系统中,这点很重要,这样才能将系统中的不同部分隔离开来。

现在来看之前的实现中,有没有模块性,有没有那个模块可以隔离出来呢。现在添加几个新的case来看,系统是否能够响应这些需求:

添加新的用户界面

除了WebForm用户之外,可能还需要一个WinForm的界面,现在我们能否复用领域层和数据访问层呢?从依赖图中可以看到,没有任何一个模块会依赖表现层,因此很容易实现这一点变化。我们只需要创建一个WPF的富客户端就可以。现在整个系统的依赖图如下:

WPF client

更换新的数据源

可能过了一段时间,需要把整个系统部署到云上,要使用其他的数据存储技术,比如Azure Table Storage Service。现在,整个访问数据的协议发生了变化,访问Azure Table Storage Service的方式是Http协议,而之前的大多数.NET 访问数据的方式都是基于ADO.NET 的方式。并且数据源的保存方式也发生了改变,之前是关系型数据库,现在变成了key-value型数据库。

Azure datatable 

由上面的依赖关系图可以看出,所有的层都依赖了数据访问层,如果修改数据访问层,则领域逻辑层,和表现层都需要进行相应的修改。

1.2.3 问题

除了上面的各层之间耦合下过强之外,代码中还有其他问题。

  • 领域模型似乎都写到了数据访问层中。所以领域模型看起来依赖了数据访问层。在数据访问层中定义了名为Product的类,这种类应该是属于领域模型层的。
  • 表现层中掺入了决定某个用户是否是会员的逻辑。这种业务逻辑应该是 业务逻辑层中应该处理的,所以也应该放到领域模型层
  • ProductService因为依赖了数据访问层,所以也会依赖在web.config 中配置的数据库连接字符串等信息。这使得,整个业务逻辑层也需要依赖这些配置才能正常运行。
  • 在View中,包含了太多了函数性功能。他执行了强制类型转换,字符串格式化等操作,这些功能应该是在界面显示得模型中完成。

上面可能是我们大多数写代码时候的实现, UI界面层去依赖了数据访问层,有时候偷懒就直接引用了这一层,因为实体定义在里面了。业务逻辑层也是依赖数据访问层,直接在业务逻辑里面使用了数据访问层里面的实体。这样使得整个系统紧耦合,并且可测试性差。那现在我们看看,如何修改这样一个系统,使之达到松散耦合,从而提高可测试性呢?

2 较好的实现

依赖注入能够较好的解决上面出现的问题,现在可以使用这一思想来重新实现前面的系统。之所以重新实现是因为,前面的实现在一开始的似乎就没有考虑到扩展性和松耦合,使用重构的方式很难达到理想的效果。对于小的系统来说可能还可以,但是对于一个大型的系统,应该是比较困难的。

在写代码的时候,要管理好依赖性,在前面的实现这种,代码直接控制了依赖性:当ProductService需要一个ObjectContext类的似乎,直接new了一个,当HomeController需要一个ProductService的时候,直接new了一个,这样看起来很酷很方便,实际上使得整个系统具有很大的局限性,变得紧耦合。new 操作实际上就引入了依赖, 控制反转这种思想就是要使的我们比较好的管理依赖。

2.1 松耦合的代码

2.1.1 表现层

首先从表现层来分析,表现层主要是用来对数据进行展现,不应该包含过多的逻辑。在Index的View页面中,代码希望可以写成这样

<h2>
    Featured Products</h2>
<div>
    <% foreach (var product in this.Model.Products)
        { %>
    <div>
        <%= this.Html.Encode(product.SummaryText) %></div>
    <% } %>
</div>

可以看出,跟之前的表现层代码相比,要整洁很多。很明显是不需要进行类型转换,要实现这样的目的,只需要让Index.aspx这个视图继承自 System.Web.Mvc.ViewPage<FeaturedProductsViewModel> 即可,当我们在从Controller创建View的时候,可以进行选择,然后会自动生成。整个用于展示的信息放在了SummaryText字段中。

这里就引入了一个视图模型(View-Specific Models),他封装了视图的行为,这些模型只是简单的POCOs对象(Plain Old CLR Objects)。FeatureProductsViewModel中包含了一个List列表,每个元素是一个ProductViewModel类,其中定义了一些简单的用于数据展示的字段。

FeatureProductsViewModel

现在在Controller中,我们只需要给View返回FeatureProductsViewModel对象即可。比如:

public ViewResult Index()
{
    var vm = new FeaturedProductsViewModel();
    return View(vm);
}

现在返回的是空列表,具体的填充方式在领域模型中,我们接着看领域模型层。

2.1.2 领域逻辑层

新建一个类库,这里面包含POCOs和一些抽象类型。POCOs用来对领域建模,抽象类型提供抽象作为到达领域模型的入口。依赖注入的原则是面向接口而不是具体的类编程,使得我们可以替换具体实现。

现在我们需要为表现层提供数据。因此用户界面层需要引用领域模型层。对数据访问层的简单抽象可以采用Patterns of Enterprise Application Architecture一书中讲到的Repository模式。因此定义一个ProductRepository抽象类,注意是抽象类,在领域模型库中。它定义了一个获取所有特价商品的抽象方法:

public abstract class ProductRepository
{
    public abstract IEnumerable<Product> GetFeaturedProducts();
}

这个方法的Product类中只定义了商品的基本信息比如名称和单价。整个关系图如下:

Domain model

现在来看表现层,HomeController中的Index方法应该要使用ProductService实例类来获取商品列表,执行价格打折,并且把Product类似转化为ProductViewModel实例,并将该实例加入到FeaturesProductsViewModel中。因为ProductService有一个带有类型为ProductReposity抽象类的构造函数,所以这里可以通过构造函数注入实现了ProductReposity抽象类的实例。这里和之前的最大区别是,我们没有使用new关键字来立即new一个对象,而是通过构造函数的方式传入具体的实现。

现在来看表现层代码:

public partial class HomeController : Controller
{
    private readonly ProductRepository repository;

    public HomeController(ProductRepository repository)
    {
        if (repository == null)
        {
            throw new ArgumentNullException("repository");
        }

        this.repository = repository;
    }

    public ViewResult Index()
    {
        var productService = new ProductService(this.repository);

        var vm = new FeaturedProductsViewModel();

        var products = productService.GetFeaturedProducts(this.User);
        foreach (var product in products)
        {
            var productVM = new ProductViewModel(product);
            vm.Products.Add(productVM);
        }

        return View(vm);
    }

}

在HomeController的构造函数中,传入了实现了ProductRepository抽象类的一个实例,然后将该实例保存在定义的私有的只读的ProductRepository类型的repository对象中,这就是典型的通过构造函数注入。在Index方法中,获取数据的ProductService类中的主要功能,实际上是通过传入的repository类来代理完成的。

ProductService类是一个纯粹的领域对象,实现如下:

public class ProductService
{
    private readonly ProductRepository repository;

    public ProductService(ProductRepository repository)
    {
        if (repository == null)
        {
            throw new ArgumentNullException("repository");
        }

        this.repository = repository;
    }

    public IEnumerable<DiscountedProduct> GetFeaturedProducts(IPrincipal user)
    {
        if (user == null)
        {
            throw new ArgumentNullException("user");
        }

        return from p in
                        this.repository.GetFeaturedProducts()
                select p.ApplyDiscountFor(user);
    }
}

可以看到ProductService也是通过构造函数注入的方式,保存了实现了ProductReposity抽象类的实例,然后借助该实例中的GetFeatureProducts方法,获取原始列表数据,然后进行打折处理,进而实现了自己的GetFeaturedProducts方法。在该GetFeaturedProducts方法中,跟之前不同的地方在于,现在的参数是IPrincipal,而不是之前的bool型,因为判断用户的状况,这是一个业务逻辑,不应该在表现层处理。IPrincipal是BCL中的类型,所以不存在额外的依赖。我们应该基于接口编程IPrincipal是应用程序用户的一种标准方式。

这里将IPrincipal作为参数传递给某个方法,然后再里面调用实现的方式是依赖注入中的方法注入的手段。和构造函数注入一样,同样是将内部实现代理给了传入的依赖对象。

现在我们只剩下两块地方没有处理了:

  • 没有ProductRepository的具体实现,这个很容易实现,后面放到数据访问层里面去处理,我们只需要创建一个具体的实现了ProductRepository的数据访问类即可。
  • 默认上,ASP.NET MVC 希望Controller对象有自己的默认构造函数,因为我们在HomeController中添加了新的构造函数来注入依赖,所以MVC框架不知道如何解决创建实例,因为有依赖。这个问题可以通过开发一个IControllerFactory来解决,该对象可以创建一个具体的ProductRepositry实例,然后传给HomeController这里不多讲。

现在我们的领域逻辑层已经写好了。在该层,我们只操作领域模型对象,以及.NET BCL 中的基本对象。模型使用POCOs来表示,命名为Product。领域模型层必须能够和外界进行交流(database),所以需要一个抽象类(Repository)来时完成这一功能,并且在必要的时候,可以替换具体实现。

2.1.3 数据访问层

现在我们可以使用LINQ to Entity来实现具体的数据访问层逻辑了。因为要实现领域模型的ProductRepository抽象类,所以需要引入领域模型层。注意,这里的依赖变成了数据访问层依赖领域模型层。跟之前的恰好相反,代码实现如下:

public class SqlProductRepository : Domain.ProductRepository
{
    private readonly CommerceObjectContext context;

    public SqlProductRepository(string connString)
    {
        this.context =
            new CommerceObjectContext(connString);
    }

    public override IEnumerable<Domain.Product> GetFeaturedProducts()
    {
        var products = (from p in this.context.Products
                        where p.IsFeatured
                        select p).AsEnumerable();
        return from p in products
                select p.ToDomainProduct();
    }
}

在这里需要注意的是,在领域模型层中,我们定义了一个名为Product的领域模型,然后再数据访问层中Entity Framework帮我们也生成了一个名为Product的数据访问层实体,他是和db中的Product表一一对应的。所以我们在方法返回的时候,需要把类型从db中的Product转换为领域模型中的POCOs Product对象。

two product class in the system 

Domain Model中的Product是一个POCOs类型的对象,他仅仅包含领域模型中需要用到的一些基本字段,DataAccess中的Product对象是映射到DB中的实体,它包含数据库中Product表定义的所有字段,在数据表现层中我们 定义了一个ProductViewModel数据展现的Model。

这两个对象之间的转换很简单:

public class Product
{
    public Domain.Product ToDomainProduct()
    {
        Domain.Product p = new Domain.Product();
        p.Name = this.Name;
        p.UnitPrice = this.UnitPrice;
        return p;
    }
}

2.2 分析

2.2.1 依赖关系图

现在,整个系统的依赖关系图如下:

Dependency graph in DDD

表现层和数据访问层都依赖领域模型层,这样,在前面的case中,如果我们新添加一个UI界面;更换一种数据源的存储和获取方式,只需要修改对应层的代码即可,领域模型层保持了稳定。

2.2.2 时序图

整个系统的时序图如下:

Sequence Diagram

系统启动的时候,在Global.asax中创建了一个自定义了Controller工厂类,应用程序将其保存在本地便两种,当页面请求进来的时候,程序出发该工厂类的CreateController方法,并查找web.config中的数据库连接字符串,将其传递给新的SqlProductRepository实例,然后将SqlProductRepository实例注入到HomeControll中,并返回。

然后应用调用HomeController的实例方法Index来创建新的ProductService类,并通过构造函数传入SqlProductRepository。ProductService的GetFeaturedProducts 方法代理给SqlProductRepository实例去实现。

最后,返回填充好了FeaturedProductViewModel的ViewResult对象给页面,然后MVC进行合适的展现。

2.2.3 新的结构

在1.1的实现中,采用了三层架构,在改进后的实现中,在UI层和领域模型层中加入了一个表现模型(presentation model)层。如下图:

presentation model layer

 

将Controllers和ViewModel从表现层移到了表现模型层,仅仅将视图(.aspx和.ascx文件)和聚合根对象(Composition Root)保留在了表现层中。之所以这样处理,是可以使得尽可能的使得表现层能够可配置而其他部分尽可能的可以保持不变。

3. 结语

一不小心我们就编写出了紧耦合的代码,有时候以为分层了就可以解决这一问题,但是大多数的时候,都没有正确的实现分层。之所以容易写出紧耦合的代码有一个原因是因为编程语言或者开发环境允许我们只要需要一个新的实例对象,就可以使用new关键字来实例化一个。如果我们需要添加依赖,Visual Studio有些时候可以自动帮我们添加引用。这使得我们很容易就犯错,使用new关键字,就可能会引入以来;添加引用就会产生依赖。

减少new引入的依赖及紧耦合最好的方式是使用构造函数注入依赖这种设计模式:即如果我们需要一个依赖的实例,通过构造函数注入。在第二个部分的实现演示了如何针对抽象而不是具体编程。

构造函数注入是反转控制的一个例子,因为我们反转了对依赖的控制。不是使用new关键字创建一个实例,而是将这种行为委托给了第三方实现。

希望本文能够给大家了解如何真正实现三层架构,编写松散耦合,可维护,可测试性的代码提供一些帮助。

 

http://stackoverflow.com/questions/1362962/when-not-to-use-ioc-and-di

参考http://www.cnblogs.com/yangecnu/p/Introduce-Dependency-Injection.html

容器unity实现ioc+di

...反转:正常情况下,程序开发过程中,是上端调用下端,依赖下端///依赖倒置原则->上端不要依赖下端,要依赖下端的抽象///上端只依赖抽象,细节交给第三方工厂来决定,这就是IOC,就是控制反转->系统架构可以更稳定,支持... 查看详情

springioc(di)

...在你的对象内部直接控制。 DI—DependencyInjection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。 依赖注入(DI)和控制反转( 查看详情

spring学习2-ioc原理控制反转/依赖注入

控制反转/依赖注入 最近,买了本spring入门书:springInAction。大致浏览了下感觉还不错。就是入门了点。Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专注于Manning,但怀着崇敬的心情和激情通览了一遍。又一次... 查看详情

为啥在python中是ioc/di不常见

...oc/di不常见IOC是控制反转,所谓反转是指应用本身并不负责依赖对象的创建和维护,而把这个任务交给第三方即Spring去处理,这是将创建对象的权利交给第三方,控制反转就是控制权的转移DI是依赖注入.依赖注入,是指运行期间,Spring动... 查看详情

解释spring中ioc,di,aop

...如果,你对数据库的操作有很多类,那你每一类中都要写关于日志的方法。但是如果你用aop,那么你可以写一个方法,在这个方法中有关于数据库操作的方法,每一次调用这个方法的时候,就加上生成日志的操作。说的不是很清... 查看详情

spring之ioc/di(反转控制/依赖注入)_入门demo

软件152刘安民在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时至少需要两个或以上的对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像newobject()这样的... 查看详情

ioc/di的理解

...ired的自动注入,只是从IOC容器中获得实例的过程叫做依赖注入IOC/DI的讲解http://jinnianshilongnian.i 查看详情

深入理解dipiocdi以及ioc容器(代码片段)

...展以及易复用的程序。其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC、DI以及Ioc容器等概念。通过本文我们将一起学习这些概念,并理清他们之间微妙的关系。 目录前言依赖倒置原则(DIP)控制反转... 查看详情

依赖注入和控制反转的理解,写的太好了。

...习过spring框架的人一定都会听过Spring的IoC(控制反转)、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及... 查看详情

ioc和di的理解

...直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC容器控制了对象;控制什么?那就是主要控制了外部资源获取(不... 查看详情

依赖注入和控制反转的理解,写的太好了。

...习过Spring框架的人一定都会听过Spring的IoC(控制反转)、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及... 查看详情

对ioc和di的理解

...习过Spring框架的人一定都会听过Spring的IoC(控制反转)、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC、DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及... 查看详情

理解spring的aop和ioc/di就这么简单

...DIIoc:InversionofControl——控制反转DI:DependencyInjection——依赖注入其实这两个概念本质上是没有区别的,那我们先来看看什么叫做Ioc?假设这么一个场景:在A类中调用B类的方法,那么我们就称A依赖B,B为被依赖(对象),相信这... 查看详情

设计模式-ioc/di

 转(http://www.cnblogs.com/sjms/archive/2010/06/19/1760692.html) IoC——InversionofControl 控制反转 DI——DependencyInjection  依赖注入1:如何理解IoC/DI       查看详情

记录学习spring(ioc/di)

...自己new构造方法来调用,变成了交由Spring创建对象;  DI依赖注入Depen 查看详情

菜鸟认知--dip,ioc,di,ioc容器

DIP:依赖倒置原则  依赖倒置原则是一种设计原则,它提出可以通过反转依赖关系来实现高内聚和低耦合。  那么当DIP提出这种设计原则之后谁来具体实现这种原则呢,这时候引进IoC。IoC:控制反转  Ioc是基于DIP提出的反... 查看详情

学习ioc设计模式前必读:依赖注入的三种实现(代码片段)

...展以及易复用的程序。其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC、DI以及Ioc容器等概念。通过本文我们将一起学习这些概念,并理清他们之间微妙的关系。 查看详情

spring依赖注入怎么回事,还有面向方面编程是怎么回事

依赖注入,也称反转控制,IoC,DI,这些都是一回事,简单说起来就是Spring提供了一个JavaBean的处理器,通过读取xml配置文件或注解(Annotation)来自动为你需要的的接口类型,实例化(也就是新建)对象,这样你的代码就不需要针... 查看详情