第二章 深入探讨控制反转(Ioc)和依赖注入(DI)

简介:
 注:希望大家看后,请给我一点评价,无论写的怎么样,希望你们能给我支持。提出你宝贵的意见。我会继续完善。谢谢您。朋友。
第二章   深入探讨控制反转( Ioc )和依赖注入( DI
在第一章中已经对控制反转(Ioc)和依赖注入(DI)有了一个初步的了解,现在让我们通过具体的程序代码来真正的体验一下,它们真的有那么神奇吗?
在演练之前,我还要先讲一下理论知识,然后在演练程序代码。让你也体会到其中的神奇效果。
Spring 中的控制反转(IoC
1IoC = Inversion of Control(由容器控制程序之间的关系)
IoC,用白话来讲,就是由容器来控制程序中的各个类之间的关系,而非传统实现中,直接在代码中由程序代码直接操控。比如在一个类(A)中访问另外一个类中(B)的方法时,我们需要先去new 一个B的对象,然后调用所需的方法。他们的关系很显然在程序代码中控制,同时它们之间的耦合度也比较大,不利于代码的重用。而我们现在把这种控制程序之间的关系交给Ioc容器,让它去帮你实例化你所需要的对象,而你直接在程序中调用就可以了。这也就是所谓"控制反转"的概念的由来:控制权由应用程序的代码中转到了外部容器,控制权的转移,是所谓反转的由来
可能你不知道Ioc容器到底,或确切的指的是什么?其实这里的容器就相当于一个工厂一样。你需要什么,直接来拿,直接用就可以了,而不需要去了解,去关心你所用的东西是如何制成的,在程序中体现为实现的细节,这里就用到了工厂模式,其实Spring容器就是工厂模式和单例模式所实现的。在第三章中我将会详细介绍SpringIoc容器。
对于初学者,我想简单的先说明几点,不要把applicationContext.xml,或带有bean的配置文件理解为容器,它们只是描述了要用到的类(bean)之间的依赖关系。Spring中的容器很抽象,不像Tomcat,Weblogic,WebSphere等那样的应用服务器容器是可见的。SpringIoc容器给人的感觉好像就是那些配置文件(applicationContext.xml,我刚开始学时,也以为就是那些带bean的配置文件,虽然它对你学习Spring没什么影响,但如果想更深沉的了解就会迷茫的,我们在这里要正确理解SpringIoc容器,以后对我们学习会有很大的帮助的。其实它的容器是有一些类和接口来充当的,你可能又会很迷茫。这就是它与别的框架的不同之处,这一点也正在体现了它的无侵入性的一点,不像EJB需要专门的容器来运行,侵入性很大的重量级的框架。Spring只是一种轻量级的无侵入性的框架。说白了SpringIoc容器就是可以实例化BeanFactoryApplicationContext(扩展了BeanFactory)的类.
可能你对Ioc容器还是不太理解,慢慢来,刚刚接触的人都会很迷茫。我现在通过讲解一个例子来说明它的工作原理,你可能会恍然大悟,原来如此简单。
首先打开你的IDE编译器,我用的是Eclipse3.2+MyEclipse5.5.1
我举了一个大家比较熟悉的例子,用户登录验证。如果用户名为:admin 密码为:1234.
就会在控制台输出恭喜你,登录成功!反之输出“对不起,登录失败!”并在日志文件中记录登录信息。这里我用到的是log4j.(日志记录器)。我严格按分层思想和Spring中提倡的按接口编程的思想来演练这个简单的例子。可能你会想这么简单的为什么要那么麻烦呢?怎么不用一个类就解决了,做为程序员,我们时刻要记住,我们的代码要易维护,可重用,易扩展,低耦合,高内聚。如果你了解这些原则,你自然会明白这样麻烦的好处了。
我先整体的讲解一下工程中的目录结果:如下图
 
首先我们看一下UserLogin接口和它的实现部分UserLoginImpl
package  com.chap2;
public   interface  UserLogin  {
   
     public   boolean  login(String userName,String passWord);
}
package  com.chap2;
@SuppressWarnings ( "unused" )
public   class  UserLoginImpl  implements  UserLogin {
     public   boolean  login(String userName,String passWord) {
        if  (userName.equals( "admin" ) && passWord.equals( "1234" ))
            return   true ;
        else
            return   false ;
    }
}
上面的接口只是简单的定义了一个方法,用于验证用户身份是否合法。在实现中只是简单的数据比较,实际当中应该从数据库中去取数据进行验证的,我们只要能说明问题就可以了。
然后在看一下业务逻辑层的UserService这里应该再对该类进行提取接口的,我只是写了一个类,如果你有兴趣的话,你可以在加上接口像上面的UserLogin一样,这样做是为了降低代码的耦合度,提高封装性。
package  com.chap2.service;
import  com.chap2.UserLogin;
public   class  UserService {
     private  String  userName ;
     private  String  passWord ;
     private  UserLogin  userLogin ;
     public   void  setUserName(String userName) {
        this . userName  = userName;
    }
     public   void  setPassWord(String passWord) {
        this . passWord  = passWord;
    }
     public   void  setUserLogin(UserLogin userLogin) {
        this . userLogin  = userLogin;
    }
     public  String validateUser() {
        if  ( userLogin .login( userName passWord ))
            return   " 恭喜你,登录成功!" ;
        else
            return   " 对不起,登录失败!" ;
     }
}
在这个业务方法里其实是对userLogin中的方法的再次封装,这里面可能隐藏的用到了正面封装的模式(Facade)或叫做门面模式。它的好处是为了不让客户直接访问UserLogin中的方法,在实际的开发中UserLogin的方法应该放在DAO层中,这里的DAO是数据访问层的意思,其实就是对数据库执行的CRUD(增,删,改,查)操作。大家不要因为我多讲了一些就迷。我们应该养成好的编程习惯。也就是分工明细。层与层之间只能通过接口访问。至于上面提到的模式。你可以不先管了,只要你知道它里面用到了就可以了,以后我会把常用到的模式给你讲讲的。
这里的前台客户端的调用,我没用到html,jsp之类的页面,只是用了一个main()方法简单的模拟一下。同样可以起到上面讲的效果的。
public   class  Client {
     public   static   void  main(String[] args) {
        //  TODO   自动生成方法存根
       Log log = LogFactory.getLog(Client. class );
        // 初始化,并加载配置文件。
       ApplicationContext context =  new  ClassPathXmlApplicationContext(
               "applicationContext.xml" );
       BeanFactory beanFactory = (BeanFactory) context;
        // 得到实例化的UserService
       UserService userService = (UserService) beanFactory
              .getBean( "userService" );
        // 输出并记录登录信息
       log.info(userService.validateUser());
    }
}
先简单的看一下运行的结果是什么。
2007-11-21 11:04:44,765 INFO [org.springframework.context.support.ClassPathXmlApplicationContext] - <Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@c51355: display name [org.springframework.context.support.ClassPathXmlApplicationContext@c51355]; startup date [Wed Nov 21 11:04:44 CST 2007]; root of context hierarchy>
2007-11-21 11:04:44,843 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - <Loading XML bean definitions from class path resource [applicationContext.xml]>
2007-11-21 11:04:45,031 INFO [org.springframework.context.support.ClassPathXmlApplicationContext] - <Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext@c51355]: org.springframework.beans.factory.support.DefaultListableBeanFactory@1ce2dd4>
2007-11-21 11:04:45,046 INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - <Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1ce2dd4: defining beans [userLogin,userService]; root of factory hierarchy>
2007-11-21 11:04:45,078 INFO [com.chap2.util.Client] - < 恭喜你,登录成功!>
你可能会想登录用户的信息在那?
所有的配置信息和实例化的工作都交给了SpringIoc容器,看看配置文件的配置。
<? 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 [url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]" >
     < bean  id = "userLogin"  class = "com.chap2.UserLoginImpl"  />
     < bean  id = "userService"  class = "com.chap2.service.UserService" >
        < property  name = "userName" >
            < value > admin </ value >
        </ property >
        < property  name = "passWord" >
            < value > 1234 </ value >
        </ property >
        < property  name = "userLogin" >
            < ref  bean = "userLogin"  />
        </ property >
     </ bean >
</ beans >
上面的配置文件只是描述了我们所用到类的调用关系,和数据的赋值<初始值>。我们在UserSerivce中要用到UserLogin中的方法,我们只需在这里简单的设置它的一个属性(property)就可以了,以前的编程要在代码中实现了,通过先new 一个UserLogin的实现对象,然后在调用其中的方法。我们这里只需在配置文件中简单的配置一下就可以了,在程序用到这个方法时,容器会自动先实例化UserLoginImpl,然后把它交给UserService使用。在UserService中不用担心实例化,以及管理它的生命周期了。全部让SpringIoc容器管理记行了。这样做减少了它们之间的依赖性,也就是降低它们的耦合度。
整个程序的工作流程是这样的。
main()方法中通过ApplicationContext来加载配置文件,然后把它转换为BeanFactory。这就是我们要讲的Spring中控制反转容器。它把所有的类都初始化,并放在这个容器中。在我们需要的时候只需像 UserService userService = (UserService) beanFactory.getBean("userService");通过配置文件中beanidname调用就可以了。不必在像以前的编程那样UserService userSerivce=new UserService();这样做还有一个好处就是让容器来管理它的生命周期,我们只需用就可以了,用完了在交给容器管理。而不用担心什么时候销毁它。
附加一些log4j的文件信息。就是工程目录下的log4j.properties.一定要放到src目录下,要不然程序运行时,它会找不到的。或直接把它放到class目录下。Log4j就是简单的记录一些日志信息,为以后使用的。下面是它的配置。其实还有多中形式的配置,可以是java属性文件形式的。像userLogin.log, userLogin.log.1就是一些备份文件。里面记录了程序运行时的一些信息。
log4j.rootLogger= INFO,   stdout,logfile
 
log4j.appender.stdout= org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout= org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= %d   %p   [%c]   -   <%m>%n
 
log4j.appender.logfile= org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File= userLogin.log
log4j.appender.logfile.MaxFileSize= 2KB
# Keep three backup files.
log4j.appender.logfile.MaxBackupIndex= 3
# Pattern to output: date priority [category] - message
log4j.appender.logfile.layout= org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern= %d   %p   [%c]   -   %m%n
在这里我就不具体的讲了。在以后的章节中会讲到的。
  今天先写到这里.









本文转自 weijie@java 51CTO博客,原文链接:http://blog.51cto.com/weijie/66456,如需转载请自行联系原作者
目录
相关文章
|
2月前
|
XML Java 数据格式
从六个方面读懂IoC(控制反转)和DI(依赖注入)
在一开始学习 Spring 的时候,我们就接触 IoC 了,作为 Spring 第一个最核心的概念,我们在解读它源码之前一定需要对其有深入的认识,对于初学Spring的人来说,总觉得IOC是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及谈谈我对Spring IOC的理解。
23 2
|
4月前
|
XML Java 数据格式
深入理解 Spring IoC 和 DI:掌握控制反转和依赖注入的精髓
在本文中,我们将介绍 IoC(控制反转)和 DI(依赖注入)的概念,以及如何在 Spring 框架中实现它们。
64 0
|
5月前
|
Java 容器 Spring
[javaweb]——spring框架之控制反转(IOC)与依赖注入(DI)
[javaweb]——spring框架之控制反转(IOC)与依赖注入(DI)
|
9月前
|
XML 图形学 数据格式
IOC控制反转——基础概念与实例
IOC控制反转——基础概念与实例
|
设计模式 中间件 容器
小满nestjs(第二章 IOC控制反转 DI依赖注入)
Inversion of Control字面意思是控制反转,具体定义是高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
104 0
|
XML Java 程序员
Spring的艺术(二):控制反转(IOC)和依赖注入(DI)的完美实现
IOC叫做控制反转,从本质上讲,IOC就是把原本由程序员创建的对象的这个动作交给Spring去实现,程序员无需再去管理对象的创建,这种方式可以大大减少系统的偶尔性。 没有IOC之前,对象的创建和对象间的依赖关系都完全编码在程序中,使用IOC之后,对象的创建由程序自己控制,使得程序解耦合。 IOC并不是一种技术,他是一种思想,即控制权反转的思想,DI(依赖注入)则是Spring实现IOC的方法。 Spring容器在初始化时根据配置文件或元数据创建和组织对象存入容器中,需要使用时再从IOC容器中获取。
|
XML JavaScript Java
阅读Spring源码:IOC控制反转前的处理
阅读Spring源码:IOC控制反转前的处理
105 0
阅读Spring源码:IOC控制反转前的处理
|
C# 容器
控制反转/依赖注入简明教程
控制反转/依赖注入简明教程
90 0
|
C# 容器
控制反转_依赖注入简明教程
控制反转_依赖注入简明教程
101 0
|
前端开发 JavaScript 容器