学习Spring框架系列(一):通过Demo阐述IoC和DI的优势所在

Spring框架最核心东西便是大名鼎鼎的IoC容器,主要通过DI技术实现。下面我通过Demo的演变过程,对比学习耦合性代码,以及解耦和的过程,并深入理解面向接口编程的真正内涵。

 

这个例子包括如下几个类:

  • 实体类:Book,有名称、作者等属性
  • BookFinder接口,定义了findAll()方法
  • BookLister接口,定义了findBooks(String name)方法,以书名作为参数,并返回Book[]数组作为查找的结果
  • 以及一个测试客户端BookClient,调用BookLister,来获取所需要的数据

 

Book.java

 

[java] view plain copy
 
  1. public class Book {  
  2. private String name;  
  3. private String author;  
  4. /** 
  5.  * @return Returns the author. 
  6.  */  
  7. public String getAuthor() {  
  8. return author;  
  9. }  
  10. /** 
  11.  * @param author The author to set. 
  12.  */  
  13. public void setAuthor(String author) {  
  14. this.author = author;  
  15. }  
  16. //other getters/setters…  
  17. }  



 

 

BookFinder

  • [java] view plain copy
     
    1. BookFinder接口  
    2. public interface BookFinder {  
    3. public List findAll();  
    4. }  
    5.    
    6. SimpleBookFinderImpl  
    7. public class SimpleBookFinderImpl implements BookFinder{  
    8. /** 
    9.  * @see com.bjpowernode.spring.BookFinder#findAll() 
    10.  */  
    11. public List findAll() {  
    12. List books = new ArrayList();  
    13. for(int i=0; i<20; i++){  
    14. Book book = new Book();  
    15. book.setName("book"+i);  
    16. book.setAuthor("author"+i);  
    17. books.add(book);  
    18. }  
    19. return books;  
    20. }  
    21. }  


 

BookLister

  • [java] view plain copy
     
    1. BookList接口  
    2. public interface BookLister{  
    3. public Book[] findBooks(String name);  
    4. }  
    5. BookListerImpl实现代码  
    6. public class BookListerImpl implements BookLister {  
    7. BookFinder finder;  
    8. public BookListerImpl() {  
    9. finder = new SimpleBookFinderImpl();  
    10. }  
    11. public Book[] findBooks(String name){  
    12. List books = finder.findAll();  
    13. for (Iterator iter = books.iterator(); iter.hasNext();) {  
    14. Book book = (Book) iter.next();  
    15. if(!book.getName().equals(name)){  
    16. iter.remove();  
    17. }  
    18. }  
    19. return (Book[])books.toArray(new Book[books.size()]);  
    20. }  


 

Spring配置ApplicationContext.xml及客户调用代码

  • Spring配置:

<bean id="bookLister" class="com.bjpowernode.spring.BookListerImpl“/>

 

  • 客户调用代码

 

[java] view plain copy
 
  1. public static void main(String[] args) {  
  2. BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");  
  3. BookLister bl = (BookLister)beanFactory.getBean("bookLister");  
  4. Book[] books = bl.findBooks("book3");  
  5. if(books != null){  
  6. for(int i=0; i<books.length; i++){  
  7. System.out.println("书《"+books[i].getName()+"》的作者是:"+books[i].getAuthor());  
  8. }  
  9. }  
  10. }  



 

 

同样的问题:需求变更?

  • 现在,我们需要的不再是一个简单的BookFinder,我们需要的是一个能从本地文件系统中读取文件,并分析其中所包含的Book的内容,将其结果返回
  • 因为我们遵守了面向接口编程,所以,我们只需要提供第二个实现即可

 

FileBookFinderImpl –从文件系统读取Book的信息

 

[java] view plain copy
 
  1. public class FileBookFinderImpl implements BookFinder {  
  2. public List findAll() {  
  3. String file = “d:/test.txt”;  
  4. List books = new ArrayList();  
  5. try {  
  6. BufferedReader in  
  7.        = new BufferedReader(new FileReader(file));  
  8. String line = null;  
  9. while( (line = in.readLine()) != null)  
  10. {  
  11. String[] sp = line.split(";");  
  12. if(sp.length == 2){  
  13. …            
  14. Book book = new Book();  
  15. book.setName(sp[0]);  
  16. book.setAuthor(sp[1]);  
  17. books.add(book);  
  18. }  
  19. }  
  20. catch (FileNotFoundException e) {  
  21. e.printStackTrace();  
  22. catch (IOException e) {  
  23. e.printStackTrace();  
  24. }  
  25. return books;  
  26. }  
  27. }  



 

 

现在需要做什么?

  • 我们的客户代码调用BookLister
  • BookLister如何调用BookFinder?下面是以前初始化BookFinder的代码:

public BookListerImpl() {

finder = newSimpleBookFinderImpl();

}

  • 现在,我们必须改动这段代码:

public BookListerImpl() {

finder = newFileBookFinderImpl();

}

  • 看出问题来了吗?

 

严重违反面向对象的设计原则

  • BookLister接口的实现类严重依赖于BookFinder接口的实现类,这就是问题所在!
  • 我们必须在BookLister的实现类和BookFinder的实现类之间进行解偶,即解除它们之间的实现类耦合关系(依赖!)
  • 我们需实现以下目标:
    • BookLister的实现类BookListerImpl不应该依赖于特定的BookFinder接口的实现类(比如SimpleBookFinderImplFileBookFinderImpl
    • BookLister的实现类,应该仅仅依赖于BookFinder接口,它不应该跟具体的功能需求实现相
    • 总之,我们不应该让BookListerBookFinder的实现类之间互相耦合!

 

Spring让一切变得轻而易举

创建setter方法,为注入做好准备

  • BookListerImpl中定义一个setter方法

public void setFinder(BookFinder finder) {

this.finder = finder;

}

  • BookListerImpl构造器中的new语句注释掉

public BookListerImpl() {

//finder = new FileBookFinderImpl();

}

  • Spring去关心那些烦人的依赖关系吧!

 

Spring配置文件:ApplicationContext.xml

  • 添加BookFinder定义

<bean id="fileBookFinder" class="com.bjpowernode.spring.impl.FileBookFinderImpl“/>

  • 修改BookLister定义的配置

<bean id="bookLister" class="com.bjpowernode.spring.BookListerImpl">

<property name="finder" ref=“fileBookFinder" />

</bean>

  • 以上配置,即指定了BookListerImplFileBookFinderImpl之间的依赖关系,这种依赖关系,被放到了配置文件中,这样,只要需求有变更,只需要修改这种依赖关系即可,java代码本身不用任何变更

 

简单配置后面隐藏的内涵

  • 控制反转(Inversion of ControlIoC)与依赖注入(Dependency Injection)
  • 本来是由应用程序管理的对象之间的依赖关系,现在交给了容器管理,就叫“控制反转”或“依赖注入”。即我们需要做的事情交给了IoC容器,SpringIoC容器主要使用DI方式实现的。不需要主动查找,对象的查找、定位和创建全部由容器管理
  • 注入,其实就是个赋值的过程,只是当我们使用时,容器已经为我们赋值好了。Spring默认在创建BeanFactory时,将配置文件中所有的对象实例化并进行注入(注入时间,可以通过default-lazy-init属性进行设置)。
  • 分析IoC容器的操作流程

实例化类。实例化fileBookFinderBookListerImpl

根据property标签赋值。把FileBookFinderImpl对象赋值BookListerImpl对象中的finder属性。

posted @ 2018-06-05 18:11  皇问天  阅读(286)  评论(0编辑  收藏  举报