浏览 2279 次
该帖已经被评为隐藏帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-03-31
注:希望大家看后,请给我一点评价,无论写的怎么样,希望你们能给我支持。提出你宝贵的意见。我会继续完善。谢谢您。朋友。 第二章 深入探讨控制反转(Ioc)和依赖注入(DI) 在第一章中已经对控制反转(Ioc)和依赖注入(DI)有了一个初步的了解,现在让我们通过具体的程序代码来真正的体验一下,它们真的有那么神奇吗? 在演练之前,我还要先讲一下理论知识,然后在演练程序代码。让你也体会到其中的神奇效果。 Spring 中的控制反转(IoC) (1)IoC = Inversion of Control(由容器控制程序之间的关系) IoC,用白话来讲,就是由容器来控制程序中的各个类之间的关系,而非传统实现中,直接在代码中由程序代码直接操控。比如在一个类(A)中访问另外一个类中(B)的方法时,我们需要先去new 一个B的对象,然后调用所需的方法。他们的关系很显然在程序代码中控制,同时它们之间的耦合度也比较大,不利于代码的重用。而我们现在把这种控制程序之间的关系交给Ioc容器,让它去帮你实例化你所需要的对象,而你直接在程序中调用就可以了。这也就是所谓"控制反转"的概念的由来:控制权由应用程序的代码中转到了外部容器,控制权的转移,是所谓反转的由来。 可能你不知道Ioc容器到底,或确切的指的是什么?其实这里的容器就相当于一个工厂一样。你需要什么,直接来拿,直接用就可以了,而不需要去了解,去关心你所用的东西是如何制成的,在程序中体现为实现的细节,这里就用到了工厂模式,其实Spring容器就是工厂模式和单例模式所实现的。在第三章中我将会详细介绍Spring的Ioc容器。 对于初学者,我想简单的先说明几点,不要把applicationContext.xml,或带有bean的配置文件理解为容器,它们只是描述了要用到的类(bean)之间的依赖关系。Spring中的容器很抽象,不像Tomcat,Weblogic,WebSphere等那样的应用服务器容器是可见的。Spring的Ioc容器给人的感觉好像就是那些配置文件(applicationContext.xml),我刚开始学时,也以为就是那些带bean的配置文件,虽然它对你学习Spring没什么影响,但如果想更深沉的了解就会迷茫的,我们在这里要正确理解Spring的Ioc容器,以后对我们学习会有很大的帮助的。其实它的容器是有一些类和接口来充当的,你可能又会很迷茫。这就是它与别的框架的不同之处,这一点也正在体现了它的无侵入性的一点,不像EJB需要专门的容器来运行,侵入性很大的重量级的框架。Spring只是一种轻量级的无侵入性的框架。说白了Spring的Ioc容器就是可以实例化BeanFactory或ApplicationContext(扩展了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] - <恭喜你,登录成功!> 你可能会想登录用户的信息在那? 所有的配置信息和实例化的工作都交给了Spring的Ioc容器,看看配置文件的配置。 <?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中不用担心实例化,以及管理它的生命周期了。全部让Spring的Ioc容器管理记行了。这样做减少了它们之间的依赖性,也就是降低它们的耦合度。 整个程序的工作流程是这样的。 在main()方法中通过ApplicationContext来加载配置文件,然后把它转换为BeanFactory。这就是我们要讲的Spring中控制反转容器。它把所有的类都初始化,并放在这个容器中。在我们需要的时候只需像 UserService userService = (UserService) beanFactory.getBean("userService");通过配置文件中bean的id或name调用就可以了。不必在像以前的编程那样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 在这里我就不具体的讲了。在以后的章节中会讲到的。 今天先写到这里.
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |