zoukankan      html  css  js  c++  java
  • Spring框架学习二:通过简单的HelloWorld程序理解IoC

     实现第一个 Spring HelloWorld 程序

      接着上一次笔记,上次我们讲了使用 Spring 框架需要做的一些准备工作。现在,我们就来实现我们的第一个 Spring HelloWorld 程序,先不管为什么,我们先看效果。通过程序的效果,我们再进一步探知 Spring IoC 容器的工作原理。

      首先我们需要新建一个 Spring 配置文件:在src下右键 new —> other —> 找到 Spring文件夹,点击 Spring Bean Configurations File,起名字叫 ApplicationContext.xml,编写如下内容

    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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    5     <!-- class属性值需要写完整的类全名 --!>
    6     <bean id="helloworld" class="com.bupt.springtest.beanXml.HelloWorld"/>
    7 
    8 </beans>
     1 package com.bupt.springtest.beanXml;
     2 
     3 import org.springframework.context.ApplicationContext;
     4 import org.springframework.context.support.ClassPathXmlApplicationContext;
     5 
     6 //新建一个测试类
     7 public class HelloWorld
     8 {
     9     public void greet()
    10     {
    11         System.out.println("Hello World!");
    12     }
    13     
    14     public static void main(String[] args)
    15     {
    16         ApplicationContext ctx = new ClassPathXmlApplicationContext("HelloWorld.xml");
    17         
    18         HelloWorld hw = (HelloWorld) ctx.getBean("helloworld");
    19         
    20         hw.greet();
    21     }
    22 }

    以上程序我们可以得到结果:Hello World!

      最后的结果 Hello World! 是怎么来的,很显然是调用了hw.greet(),而 hw 明显是 HelloWorld这个类的一个实例。但hw这个实例怎么来的?程序并没有为我们 new 出来一个HelloWorld实例。我们再来看上面两行代码,new ClassPathXmlApplicationContext("HelloWorld.xml") 这段代码,学过XML解析的同学应该会眼熟,这很像XML解析时的代码,同时结合到我们在xml文件中定义的一个 HelloWorld 类型的 bean 元素,以及ctx.getBean("helloworld")获取的对象,与 xml 中定义的 bean 的 id 值一样,我们应该可以大概得出结论:

      解析xml文件后,将得到的各种 bean 放置到 ApplicationContext 容器中,容器对象再通过getBean()方法根据 bean id 获取对应的bean,接着就可以操作 bean 对象了。在这整个过程中,作为程序员的我们并没有任何创建对象的动作,创建 bean 实例的整个过程完全交由 Spring 框架来为我们实现,它何时创建,怎么创建我们一概不知。这时,我们再来看看IoC/DI的概念。

    IoC(Inversion of Contorl)一般翻译成 "控制反转",其思想是反转资源获取的方向。传统的资源查找方式要求组件项容器发起请求查找资源。作为回应,容器适时的返回资源。而应用了 IoC 之后,则是容器主动地将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源。

    DI(Dependncy Injection)依赖注入:它是IoC的另一种表述方式,即组件以一些预先定义好的方式(如:setter方法)接受来自如容器的资源注入,相较于IoC而言,这种表述更直接。

      通过概念的解释我们应该更好理解 IoC/DI 了吧,IoC就是一个容器,它为我们获取 bean 资源提供了一种方式。让我们不再手动的去 new 一个对象,而是容器自己会为我们自动生成好对象,只要把配置文件配置好。如此一来,大大降低了模块与模块之间的耦合性。因为当一个类需要持有另一类的对象时,不再需要去手动地实例化这个类,只要通过 Spring 框架为我们注入即可。当一个模块需求改变时,也不会涉及到对其他类的修改。

      现在我们通过分析代码来实现自己的IoC容器,模拟整个 IoC/DI 过程。由前面我们知道,IoC容器会解析 xml 文件生成 bean 实例放置在容器中,xml 文件中配置有值为全类名的 class 属性。我们应该可以想到,这些 bean 实例是通过 反射生成的。所以,我们的 IoC 容器应该是:解析 xml 文件,将里面配置的 bean id 和通过反射生成的 bean 实例作为键值对放入一个Map对象中,这样我们才能通过 id 属性来获取 bean 实例。同时,我们还需要通过反射来为类中的属性赋值。下面来看代码

      1 package com.bupt.test;
      2 
      3 import java.lang.reflect.Field;
      4 import java.lang.reflect.Method;
      5 import java.util.HashMap;
      6 import java.util.List;
      7 import java.util.Map;
      8 import org.dom4j.Document;
      9 import org.dom4j.Element;
     10 import org.dom4j.io.SAXReader;
     11 
     12 /**
     13  * 模拟Spring的ClassPathXmlApplicationContext对象
     14  */
     15 public class ClassPathXmlApplicationContext
     16 {
     17     //创建Map对象用来存放beans.xml中配置的对象名(id)和对象(class.newInstance())的键值对
     18     private Map<String, Object> beans = new HashMap<>();
     19     
     20     public ClassPathXmlApplicationContext(String filePath) throws Exception
     21     {
     22         //获取jom4j解析器
     23         SAXReader sax = new SAXReader();
     24         //获取根节点
     25         Document doc = sax.read(filePath);
     26         //获取根元素节点beans
     27         Element rootElement = doc.getRootElement();
     28         //获取根节点元素下所有的子元素bean
     29         List<Element> elementList = rootElement.elements("bean");
     30         
     31         for(Element e : elementList)
     32         {
     33             //获取bean的id和class属性
     34             String id = e.attributeValue("id");
     35             String className = e.attributeValue("class");
     36             
     37             //反射获取class属性对应类的实例
     38             Object clazz = Class.forName(className).newInstance();
     39             
     40             beans.put(id, clazz);
     41             
     42             //获取类中定义的所有属性
     43             Field[] field = clazz.getClass().getDeclaredFields();
     44             //Map存放的是属性名和属性的class对象的键值对
     45             Map<String, Class<?>> map = new HashMap<>();
     46             //遍历属性,将属性名和属性的Class对象放入map
     47             for(Field f : field)
     48             {
     49                 map.put(f.getName(), f.getType());
     50             }
     51             
     52             //获取所有叫property的子元素
     53             List<Element> propertyElement = e.elements("property");
     54             
     55             for(Element ele : propertyElement)
     56             {
     57                 //获取property的name属性
     58                 String name = ele.attributeValue("name");
     59                 //拼接方法名如setName()
     60                 String methodName = "set" + name.substring(0, 1).toUpperCase()+ name.substring(1);
     61                 Method m = null;
     62                 
     63                 //获取property的bean和value属性
     64                 String beanProperty = ele.attributeValue("bean");
     65                 String valueProperty = ele.attributeValue("value");
     66                 
     67                 //若第二个属性为另一个bean的引用
     68                 if(null != beanProperty)
     69                 {
     70                     //反射获取setXxx方法对象
     71                     m = clazz.getClass().getMethod(methodName, new Class[]{beans.get(beanProperty).getClass().getInterfaces()[0]});
     72                     //执行setXxx方法
     73                     m.invoke(clazz, beans.get(beanProperty));
     74                     
     75                 }
     76                 //若第二个属性为value
     77                 if(null != valueProperty)
     78                 {
     79                     //根据value属性值,从map中获取setXxx方法的参数的Class对象
     80                     m = clazz.getClass().getMethod(methodName, new Class[]{map.get(name)});
     81                     
     82                     //判断参数类型,进行相应的类型变换,再将转换完后的参数传入invoke方法
     83                     if((String.class).equals(map.get(name)))
     84                     {
     85                         m.invoke(clazz, valueProperty);
     86                     }
     87                     else if((int.class).equals(map.get(name)))
     88                     {
     89                         m.invoke(clazz, Integer.parseInt(valueProperty));
     90                     }
     91                     else if((double.class).equals(map.get(name)))
     92                     {
     93                         m.invoke(clazz, Double.parseDouble(valueProperty));
     94                     }
     95                 }
     96             }
     97         }
     98     }
     99     
    100     //通过id值返回自动生成的对象
    101     public Object getBean(String id)
    102     {
    103         return beans.get(id);
    104     }
    105 }

    下面编写测试类

    1 package com.bupt.test;
    2 
    3 public interface UserDAO
    4 {
    5     public void save();
    6 }
     1 package com.bupt.test;
     2 
     3 public class UserDAOImpl implements UserDAO
     4 {
     5     @Override
     6     public void save()
     7     {
     8         System.out.println("a user saved!");
     9     }
    10 }
     1 package com.bupt.test;
     2 
     3 public class UserService
     4 {
     5     //持有对UserDAo的一个引用
     6     private UserDAO userDAO;
     7     private String name;
     8     private int age;
     9     private double height;
    10     
    11     public double getHeight()
    12     {
    13         return height;
    14     }
    15 
    16     public void setHeight(double height)
    17     {
    18         this.height = height;
    19     }
    20 
    21     public String getName()
    22     {
    23         return name;
    24     }
    25 
    26     public void setName(String name)
    27     {
    28         this.name = name;
    29     }
    30 
    31     public int getAge()
    32     {
    33         return age;
    34     }
    35 
    36     public void setAge(int age)
    37     {
    38         this.age = age;
    39     }
    40 
    41     public void add()
    42     {
    43         userDAO.save();
    44     }
    45 
    46     public UserDAO getUserDAO()
    47     {
    48         return userDAO;
    49     }
    50 
    51     public void setUserDAO(UserDAO userDAO)
    52     {
    53         this.userDAO = userDAO;
    54     }
    55 }

    src下创建一个 bean.xml 文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans>
        <!-- 把被引用的bean写在前面 -->
        <bean id="u" class="com.bupt.test.UserDAOImpl">
        </bean>
        <bean id="userService" class="com.bupt.test.UserService">
            <property name="userDAO" bean="u"/>    
            <property name="name" value="tom"/>
            <property name="age" value="19"/>
            <property name="height" value="178.8"/>
        </bean>
    </beans>

    编写执行测试类

    package com.bupt.test;
    
    public class ApplicationContextTest
    {
        public static void main(String[] args) throws Exception
        {
            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("src/beans.xml");
            
            UserService service =(UserService)ctx.getBean("userService");
            
            service.add();
            
            System.out.println(service.getName() + ", " + service.getAge() + ", " + service.getHeight() + ", " + service.getUserDAO());
        }
    }
    /**
     * 结果为
     * a user saved!
     * tom, 19, 178.8, com.bupt.test.UserDAOImpl@128cdfa
     */ 

    这就是模拟 IoC 容器的代码,希望通过这段代码,能加深大家对 IoC 容器的理解

    关于 Spring 面试时常问的关于 IoC/DI 的问题

    spring 的优点?

    1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦 
    2.可以使用容易提供的众多服务,如事务管理,消息服务等 
    3.容器提供单例模式支持 
    4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能 
    5.容器提供了众多的辅助类,能加快应用的开发 
    6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等 
    7.spring属于低侵入式设计,代码的污染极低 
    8.独立于各种应用服务器 
    9.spring的DI机制降低了业务对象替换的复杂性 
    10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部

    什么是DI机制? 

    依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色 
    需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在spring中 
    创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring来完成,然后注入调用者 
    因此也称为依赖注入。 
    spring以动态灵活的方式来管理对象 , 注入的两种方式,设置注入和构造注入。 
    设置注入的优点:直观,自然 
    构造注入的优点:可以在构造器中决定依赖关系的顺序。

    什么是AOP?

    面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面 
    1.面向切面编程提供声明式事务管理 
    2.spring支持用户自定义的切面 

    面向切面编程(aop)是对面向对象编程(oop)的补充, 
    面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。 
    AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop是静态的抽象,aop是动态的抽象, 
    是对应用执行过程中的步骤进行抽象,,从而获得步骤之间的逻辑划分。 

    aop框架具有的两个特征: 

    1.各个步骤之间的良好隔离性 
    2.源代码无关性 

  • 相关阅读:
    SharePoint 2013 APP 开发示例 (六)服务端跨域访问 Web Service (REST API)
    麦咖啡导致电脑不能上网
    SharePoint 2013 Central Admin 不能打开
    SharePoint 2013 APP 开发示例 (五)跨域访问 Web Service (REST API)
    SharePoint 2013 APP 开发示例 系列
    synthesize(合成) keyword in IOS
    Git Cmd
    简单的正则匹配
    Dropbox
    SQL Server Replication
  • 原文地址:https://www.cnblogs.com/2015110615L/p/5566399.html
Copyright © 2011-2022 走看看