Spring学习笔记之自定义ClassPathXmlApplication…

2018-08-05 07:46:41来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

  在学习Spring的时候,配置完xml文件后,我们在测试类中一般是创建一个ClassPathXmlApplicationContext类的实例对象通过加载xml配置文件的路劲进行Spring的初始化。然后我们可以通过这个实例对象中getBean的方法来得到xml配置文件中定义好的bean的实例化对象。具体操作如下。

  

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中,这里同样用到反射的原理。

  原理大概就是这个样子,现在我们来实现这个自定义的ClassPathXmlApplicationContext类吧。

首先我们定义两个类用来封装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);
        
    }
}

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:1.14(java学习笔记)数组

下一篇:Java-集合