• IOC介绍及其简单实现


    预备知识: Java反射原理,XML及其解析
     
    IOC:Inversion of Control,控制反转,它最主要反映的是与传统面向对象(OO)编程的不同。通常我们编程实现某种功能都需要几个对象相互作用,从编程的角度出发,也就是一个主对象要保存其他类型对象的引用,通过调用这些引用的方法来完成任务。如何获得其他类型的对象引用呢?一种方式是主对象内部主动获得所需引用;另一种方式是在主对象中设置setter 方法,通过调用setter方法或构造方法传入所需引用。后一种方式就叫IOC,也是我们常常所说的依赖注入。以下我们用一个简单的例子来说明传统OO编程与IOC编程的差别。
       这个例子的目的是根据时间不同返回不同的问候字符串, 比如Good Morning, world或Good afternoon, World。
    服务接口:
        package com.kettas.springdev.ioc;
    public interface HelloIF {
         String sayHello();
    }
     
    传统实现:
       package com.kettas.springdev.ioc;
       import java.util.Calendar;
    public class HelloIFImpl implements HelloIF{
          private Calendar cal;    //我们需要的引用
     
            public HelloIFImpl(){
                         cal = Calendar.getInstance();   //主动获取
    }
     
    public String sayHello(){
        if(cal.get(Calendar.AM_PM) == Calendar.AM) return “Good morning, World”;
        else return “Good afternoon, World”;
    }
    }
     
    采用IOC方式:
    package com.kettas.springdev.ioc;
       import java.util.Calendar;
    public class HelloIFImpl implements HelloIF{
          private Calendar cal;   //我们需要的引用
     
    public void setCal(Calendar cal){ this.cal = cal;} //依赖注入
      
    public String sayHello(){
        if(cal.get(Calendar.AM_PM) == Calendar.AM) return “Good morning, World”;
        else return “Good afternoon, World”;
    }
    }
     
    在这里你也许会问:我看不出有太大差别,并且依赖注入还需要我先创建外部的Calendar对象,然后再传到HelloIFImpl对象中。
     
    是的,如果我们直接创建HelloIFImpl对象没有任何的优势。如果我们让一个Bean工厂来帮我们创建HelloIF类型的引用就有优势了,当然要在这样的前提下: 1. Bean工厂可以随时改变HelloIF实现的类型;2. Bean工厂在创建好对象后主动调用依赖注入的方法。所以离开Bean工厂谈IOC是没有什么意义的, 开源框架Spring就提供了灵活多样的Bean工厂。以上例子可以通过如下XML片段来告诉Bean工厂如何创建对象并注入依赖:
    <beans>
       <bean id=”hello” class=”com.kettas.springdev.ioc.HelloIFImpl”> <!—调用构造方法产生对象à
          <property name=”cal” > <!—注入下面定义的Calendar引用-->
              <ref local=”calendar”/>
    </property>
       </bean>
     
       <bean id=”calendar” class=”java.util.GregorianCalendar” /> <!—产生calendar对象à 
    </beans>
     
    Bean工厂通过解析以上的配置就知道如何创建对象,如何注入依赖。
     
    那Bean工厂到底如何实现所说的功能呢?从以上的XML配置片段我们可以看出
     
    有两种数据类型:一个是Bean的定义(BeanDefinition);一个是对Bean的属性(Property)的定义(PropertyDefinition),嵌套在Bean定义中。Bean的定义包括:id, 类名(clazz)和一到多个PropertyDefinition; Property的定义包括: name, refName(如果是引用,指向另一个Bean), value(如果是基本数据类型), ifRef(是否是引用)。Java通过反射机制可以调用构造方法创建对象,也可以调用该实例上的方法。Bean工厂通过递归调用创建配置文件里定义的Bean对象,并调用这些对象的setter方法来实现依赖注入。而依赖注入都使用的是Bean的id, 所以我们在Bean工厂里用一个Map来保存Bean的定义,该Map的key是Bean的id。创建的Bean对象都是单例的,所以我们要保存Bean对象;而客户是通过id来获取Bean对象,所以我们也用Map来缓存。以下的代码提供了上述问题的解决方案(当然这是最简单的一种:只调用没有参数的构造方法,只实现基于setter的依赖注入,注入的依赖只能是基本数据类型或引用, xml解析没有做合法性检测等)
     
    Bean的定义:BeanDefinition,java:
     
    package com.kettas.springdev.ioc.bf;
     
    import java.util.HashSet;
    import java.util.Set;
     
    public class BeanDefinition { //对应<bean>元素
           private String clazz;     //Bean类名全路径, 对应<bean>的attribute: class
           private String id;        //唯一属性, 对应<bean>的attribute: id
           private Set<PropertyDefinition> propertyDefinitions     //依赖描述, 对应<property>元素
            = new HashSet<PropertyDefinition>();
          
           public String getId() {
                  return id;
           }
           public void setId(String id) {
                  this.id = id;
           }
           public String getClazz() {
                  return clazz;
           }
           public void setClazz(String clazz) {
                  this.clazz = clazz;
           }
           public Set<PropertyDefinition> getPropertyDefinitions() {
                  return propertyDefinitions;
           }
           public void setPropertyDefinitions(Set<PropertyDefinition> propertyDefinitions) {
                  this.propertyDefinitions = propertyDefinitions;
           }
          
           public void addPropertyDefinition(PropertyDefinition pd){
                  this.propertyDefinitions.add(pd);
           }
    }
     
    Bean的属性的定义:PropertyDefinition.java:
     
    package com.kettas.springdev.ioc.bf;
     
    public class PropertyDefinition { //对应<property>元素
           private String name;         // <property> attribute: name
           private String refName;      // <property>子元素<ref>的attribute(如local, bean等)的值
           private String value;   //<property>子元素<value>的字符串子元素
           private boolean isRef;        //是否为引用
          
           public boolean isRef() {
                  return isRef;
           }
           public void setRef(boolean isRef) {
                  this.isRef = isRef;
           }
           public String getName() {
                  return name;
           }
           public void setName(String name) {
                  this.name = name;
           }
           public String getRefName() {
                  return refName;
           }
           public void setRefName(String refName) {
                  this.refName = refName;
           }
           public String getValue() {
                  return value;
           }
           public void setValue(String value) {
                  this.value = value;
           }
          
          
    }
     
    Bean工厂接口: BeanFactory.java:
    package com.kettas.springdev.ioc.bf;
     
    public interface BeanFactory {
           Object getBean(String id);
    }
     
    BeanFactory的一个实现:XmlBeanFactory.java:
     
    package com.kettas.springdev.ioc.bf;
     
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map;
    import java.util.Set;
     
    public class XmlBeanFactory implements BeanFactory{
           private Map<String, Object> beans
            = new HashMap<String, Object>(); //Bean对象缓存
           private Set<BeanDefinition> beanDefinitions
             = new HashSet<BeanDefinition>(); //Bean的定义
          
           public XmlBeanFactory(String xmlFileClassPath){
                  new BeanFactoryConfiguration(beanDefinitions, xmlFileClassPath);
           }
          
           public Object getBean(String id){
                  if(beans.containsKey(id)) return beans.get(id);
                  Object bean = null;
                  BeanDefinition bd = getBeanDefintion(id);
                  try{
                         Class beanClass = Class.forName(bd.getClazz());
                         bean = beanClass.newInstance();
                         for(PropertyDefinition pd : bd.getPropertyDefinitions()){
                                String property = pd.getName();
                                Method m = getSetter(beanClass,property);
                                //System.out.println(property + " : " + pd.isRef());
                                if(pd.isRef()){
                                       m.invoke(bean, getBean(pd.getRefName()));
                                }else{
                                       setValue(m, bean, pd.getValue());
                                }
                         }
                         beans.put(id, bean);
                  }catch(Exception e){
                         throw new RuntimeException("Can't create bean " + id, e);
                  }
                 
                  return bean;
           }
          
           private void setValue(Method m, Object bean, String value) throws Exception {
                  // TODO Auto-generated method stub
                  Class paramType = m.getParameterTypes()[0];
                  //System.out.println(paramType.getName());
                  if(paramType == byte.class || paramType == Byte.class){
                         Byte b = new Byte(value);
                         m.invoke(bean, b);
                  }else if(paramType == short.class || paramType == Short.class){
                         Short s = new Short(value);
                         m.invoke(bean, s);
                  }else if(paramType == char.class || paramType == Character.class){
                         Character c = new Character(value.charAt(0));
                         m.invoke(bean, c);
                  }else if(paramType == int.class || paramType == Integer.class){
                         Integer i = new Integer(value);
                         m.invoke(bean, i);
                  }else if(paramType == float.class || paramType == Float.class){
                         Float f = new Float(value);
                         m.invoke(bean, f);
                  }else if(paramType == double.class || paramType == Double.class){
                         Double d = new Double(value);
                         m.invoke(bean, d);
                  }else{
                         m.invoke(bean, value);
                  }
           }
     
           private Method getSetter(Class beanClass, String property) throws Exception {
                  // TODO Auto-generated method stub
                  StringBuilder sb = new StringBuilder("set");
                  char c = property.charAt(0);
                  if(c >= 'a' && c <= 'z') c -= 32;
                  sb.append(c).append(property.substring(1));
                  Method[] methods = beanClass.getMethods();
                  for(Method m : methods){
                         if(m.getName().equals(sb.toString()))
                                return m;
                  }
                 
                  throw new RuntimeException("No such property: " + property);
           }
     
           private BeanDefinition getBeanDefintion(String id){
                  for(BeanDefinition bd : beanDefinitions){
                         if(bd.getId().equals(id))
                                return bd;
                  }
                 
                  throw new RuntimeException("There is not [id="
                                + id + "] in the xml configuration file");
           }
    }
     
     
    解析Xml文件,生成BeanDefintion和PropertyDefinition: BeanFactoryConfiguration.java
    package com.kettas.springdev.ioc.bf;
     
    import java.util.Set;
     
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.parsers.ParserConfigurationException;
     
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    import org.w3c.dom.NamedNodeMap;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
     
    public class BeanFactoryConfiguration {
     
           public BeanFactoryConfiguration(Set<BeanDefinition> beanDefinitions, String xmlFileClassPath) {
                  // TODO Auto-generated constructor stub
                  DocumentBuilderFactory dbf =
                         DocumentBuilderFactory.newInstance();
                  try {
                         DocumentBuilder db = dbf.newDocumentBuilder();
                         Document doc = db.parse(
                                this.getClass().getResourceAsStream(xmlFileClassPath)    
                         );
                         parse(doc, beanDefinitions);
                  } catch (Exception e) {
                         // TODO Auto-generated catch block
                         throw new RuntimeException("can't parse the configuration file : " + xmlFileClassPath, e);
                  }
           }
     
           private void parse(Document doc, Set<BeanDefinition> beanDefinitions) {
                  // TODO Auto-generated method stub
                  NodeList beans = doc.getElementsByTagName("bean");
                  int len = beans.getLength();
                  for(int i = 0; i < len; i++){
                         BeanDefinition bd = new BeanDefinition();
                         Element n = (Element)beans.item(i);
                         NamedNodeMap nnm = n.getAttributes();
                         for(int j = 0; j < nnm.getLength(); j++){
                                Node attr = nnm.item(j);
                                //System.out.println(attr.getNodeName() + " " + attr.getNodeValue());
                                if(attr.getNodeName().equals("id"))
                                       bd.setId(attr.getNodeValue());
                                else
                                       bd.setClazz(attr.getNodeValue());
                         }
                        
                         NodeList pros = n.getElementsByTagName("property");
                         for(int j = 0; j < pros.getLength(); j++){
                                bd.addPropertyDefinition(createPropertyDefinition(pros.item(j)));
                         }
                        
                         beanDefinitions.add(bd);
                  }
           }
     
           private PropertyDefinition createPropertyDefinition(Node node) {
                  // TODO Auto-generated method stub
                  PropertyDefinition pd = new PropertyDefinition();
                  NamedNodeMap nnm = node.getAttributes();
                  pd.setName(nnm.item(0).getNodeValue());
                  NodeList nl = ((Element)node).getElementsByTagName("value");
                  if(nl != null && nl.getLength() > 0){
                         pd.setValue(nl.item(0).getFirstChild().getNodeValue());
                  }else{
                         pd.setRef(true);
                         nl = ((Element)node).getElementsByTagName("ref");
                         Node ref = nl.item(0);
                         pd.setRefName(ref.getAttributes().item(0).getNodeValue());
                  }
          
                  return pd;
           }
     
    }
     
     
    测试程序:
     
    package com.kettas.springdev.ioc.bf;
     
    import springdev.ioc.day1.HelloIF;
     
    public class TestMyBeanFactory {
     
           /**
            * @param args
            */
           public static void main(String[] args) {
                  // TODO Auto-generated method stub
                  BeanFactory bf = new XmlBeanFactory(
                                "/com/kettas/springdev/ioc/beans.xml"
                  );
                 
                  HelloIF hello = (HelloIF)bf.getBean("hello");
                  System.out.println(hello.sayHello());
           }
     
    }
  • 相关阅读:
    分式函数的变换源
    分式之殇
    两条直线的位置关系
    数列专题思维导图
    数列通项公式思维导图
    函数与导数思维导图
    三角函数思维导图
    函数与初等函数思维导图
    集合思维导图
    npm包发布正式和测试版
  • 原文地址:https://www.cnblogs.com/DreamRecorder/p/9100080.html
Copyright © 2020-2023  润新知