Spring学习笔记之自定义ClassPathXmlApplicationContext类

匿名 (未验证) 提交于 2019-12-02 20:37:20

package com.hl.action;  import org.junit.Test; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;  import com.hl.service.GoodsService;  public class GoodsAction {     @Test     public void save(){         AbstractApplicationContext aac = new ClassPathXmlApplicationContext("applicationContext.xml");         GoodsService gs = aac.getBean("goodsService", GoodsService.class);         gs.save();     }  }

Spring的配置文件如下:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xmlns="http://www.springframework.org/schema/beans"      xmlns:aop="http://www.springframework.org/schema/aop"     xmlns:context="http://www.springframework.org/schema/context"      xmlns:tx="http://www.springframework.org/schema/tx"     xmlns:cache="http://www.springframework.org/schema/cache"      xmlns:p="http://www.springframework.org/schema/p"     xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd      http://www.springframework.org/schema/aop      http://www.springframework.org/schema/aop/spring-aop-4.0.xsd      http://www.springframework.org/schema/context      http://www.springframework.org/schema/context/spring-context-4.0.xsd      http://www.springframework.org/schema/tx      http://www.springframework.org/schema/tx/spring-tx-4.0.xsd      http://www.springframework.org/schema/cache       http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">      <bean id="goodsDao" class="com.hl.dao.GoodsDao"></bean>                  <bean id="goodsService" class="com.hl.service.GoodsService">        <!-- set方式的注入 -->        <property name="goodsDao" ref="goodsDao"></property>        <!-- set注入的第二种写法 -->        <!--         <property name="goodsDao"><bean class="com.hl.dao.GoodsDao"></bean></property>        -->      </bean>            <!-- 使用构造器注入 -->      <!--                   一个参数:      <bean id="goodsService" class="com.hl.service.GoodsService">        <constructor-arg ref="goodsDao"></constructor-arg>      </bean>                 多个参数:      <bean id="goodsService" class="com.hl.service.GoodsService">        <constructor-arg index="0" ref="goodsDao"></constructor-arg>        <constructor-arg index="1" value="小明"></constructor-arg>      </bean>            -->       </beans>

有关xml配置文件中定义的bean的类这里就不给出了。那么Spring是怎么进行初始化的呢?

通过分析xml配置文件我们可以看到beans跟节点下有多个bean子节点,而bean子节点有两个属性:id和class,class属性值对应的是这个bean定义的类的全路径,id为唯一标识符。这时我们不难想到ClassPathXmlApplicationContext可能是通过解析xml中的配置得到bean节点中中的id和class属性值,然后通过反射根据class值创建类的实例对象并将对应的id值和class实例对象存到一个Map集合中,ClassPathXmlApplicationContext类只需要维护Map集合就行了。到此我们的第一步已经完成了,我们创建了bean的实例,但是我们观察到xml文件中bean节点下还有property子节点,我们还要解析到这个节点并且把该节点中的ref值中的bean对象实例注入到这个bean中,这里同样用到反射的原理。

首先我们定义两个类用来封装bean节点和property节点的信息。代码如下:

BeanDefinition类:

package com.hl.myspring;  import java.util.ArrayList; import java.util.List;  /**  * 本类是封装了Sring配置文件中的bean对象  *   * @author admin  *  */ public class BeanDefinition {     private String id; //配置文件中bean节点中的id属性值     private String myclass;//配置文件中bean节点中的class属性值     private List<PropertyDefinition> list = new ArrayList<PropertyDefinition>(0);//bean节点中的property子节点,应为可能有多个,所以使用list集合存储          public List<PropertyDefinition> getList() {         return list;     }     public void setList(List<PropertyDefinition> list) {         this.list = list;     }     public String getId() {         return id;     }     public void setId(String id) {         this.id = id;     }     public String getMyclass() {         return myclass;     }     public void setMyclass(String myclass) {         this.myclass = myclass;     }            }

PropertyDefinition类:

package com.hl.myspring; /**  * 封装了spring配置文件中bean节点中的property子节点信息  * @author admin  *  */ public class PropertyDefinition {     private String name;     private String ref;     private String value;               public PropertyDefinition() {         super();     }     public PropertyDefinition(String name, String ref, String value) {         super();         this.name = name;         this.ref = ref;         this.value = value;     }     public String getName() {         return name;     }     public void setName(String name) {         this.name = name;     }     public String getRef() {         return ref;     }     public void setRef(String ref) {         this.ref = ref;     }     public String getValue() {         return value;     }     public void setValue(String value) {         this.value = value;     }       }

自定义的ClassPathXmlApplicationContext类MyClassPathXmlApplicationContext:

package com.hl.myspring;  import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;  import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.XPath; import org.dom4j.io.SAXReader;  /**  * 实现一个自己的Spring ClassPathXmlApplicationContext类来通过配置文件来配置bean  * 比上个实现增添了一个功能,能够解析配置文件,进行bean的set注入。  * @author admin  *  */ public class MyClassPathXmlApplicationContext {          private List<BeanDefinition> list = new ArrayList<BeanDefinition>(0);  //存储所有的bean对象     private Map<String ,Object > map = new HashMap<String ,Object >(0);               /**      * 根据Spring自带的ClassPathXmlApplicationContext来构建自己的构造方法      * @param path 配置文件名      */     public MyClassPathXmlApplicationContext(String xml){         //解析xml文件,将xml中的bean节点存储到list结合中          parser(xml);                  //通过list结合中的BeanDefinition元素创建bean对象并根据id-class的键值对存到map集合中          instanceBean();         //把bean节点中property配置注入进去          setProperty();                       }             /**      * 解析xml文件      * @param xml      */     private void parser(String xml) {         //创建解析器         SAXReader sax = new SAXReader();         //创建解析路劲         URL url = this.getClass().getClassLoader().getResource(xml);         //System.out.println(url);         //获取domcument元素         try {             Document document = sax.read(url);             //解析有命名空间的xml需要使用XPath             XPath  xpath = document.createXPath("//ns:bean");             //创建一个map接和用来设置命名空间             Map<String,String> m = new HashMap<String,String>(0);             m.put("ns", "http://www.springframework.org/schema/beans");             //设置命名空间             xpath.setNamespaceURIs(m);             //获取所有的bean节点             List<Element> l = xpath.selectNodes(document);             for (Element element : l) {                 //得到bean节点中的id属性值                 String id = element.attributeValue("id");                 String myclass = element.attributeValue("class");                 BeanDefinition b = new BeanDefinition();                 b.setId(id);                 b.setMyclass(myclass);                 //解析每个bean节点下的多个子节点,可能有多个property配置                 List<Element> pl = element.elements();                 //循环遍历pl ,把property设置到b的properdefinition集合中                 for (Element property : pl ) {                     String name = property.attributeValue("name");                     String ref = property.attributeValue("ref");                     String value = property.attributeValue("value");                     PropertyDefinition p = new PropertyDefinition(name, ref, value);                     b.getList().add(p);                         }                 list.add(b);                 }         } catch (DocumentException e) {                          e.printStackTrace();         }              }     /**      * 便利list集合 通过放射来创建对象      */     private void instanceBean() {         for (BeanDefinition beanDefinition : list) {             try {                 //System.out.println(beanDefinition.getMyclass());                 Class clazz = Class.forName(beanDefinition.getMyclass());                 map.put(beanDefinition.getId(), clazz.newInstance());                     } catch (Exception e) {                                  e.printStackTrace();             }                      }                       }          /**      *       * 进行set注入      */     private void setProperty() {              //先遍历BeanDefinition集合         for (BeanDefinition beanDefinition : list) {             String beanId = beanDefinition.getId();//得到bean节点的id值             String beanClassName = beanDefinition.getMyclass();//的到bean节点的class属性值             Object bean = map.get(beanId);//得到bean的实例对象             List<PropertyDefinition> pl = beanDefinition.getList();//得到bean节点中的property子节点信息             //遍历pl集合进行set设置             for (PropertyDefinition propertyDefinition : pl) {                 String propertyName = propertyDefinition.getName();//得到property节点name属性的值                 String propertyRef = propertyDefinition.getRef();//得到property节点ref属性的值                 String methodName = "set"+propertyName.substring(0,1).toUpperCase()+propertyName.substring(1);//拼写set方法的名称                 //创建method方法,并执行                 try {                     Method m = bean.getClass().getMethod(methodName, map.get(propertyRef).getClass());                     m.invoke(bean, map.get(propertyRef));                 } catch (Exception e) {                                          e.printStackTrace();                 }                               }                                                }              }        public Object getBean(String key ){         return map.get(key);              } }

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!