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.源代码无关性 

  • 相关阅读:
    Django 请求生命周期
    Django views.py中的 FBV(函数) CBV(类)
    cookie session
    Django admin
    Django ORM QuerySet集合对象的特性
    Django ORM 多对多操作 使用聚合函数和分组 F查询与Q查询
    Django ORM 一对多操作
    Django ORM 单表操作
    Django 模板 template
    css之background-clip: text
  • 原文地址:https://www.cnblogs.com/2015110615L/p/5566399.html
Copyright © 2011-2022 走看看