spring小结2:spring管理bean原理(转帖)

接上回《spring概述及环境搭建
      上回讲到只需要在xml中配置一个 <bean><property name="msg" value="喜欢你,没道理! "></property></bean>配置一个子标签,便可以对Helloworld中msg行赋值,是否觉蛋疼?刚学习spring时,小飞我也觉得挺纳闷的,单单配置个属性就能进行赋值,这不是坑吗!其实,当熟悉原理之后,你会觉得,靠,原来也就那么一会事!现在小飞通过代码模拟一下spring实现Bean的原理!

上回的xml配置文件

<!-- 部分1 -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<!-- 部分2 -->
    <!-- 配置组件,上面编写的helloWold类 -->
    <bean id="sayBean" class="com.langfei.helloworld.HelloWorld">
        <!-- 依赖注入 -->
        <property name="msg" value="喜欢你,没道理! "></property>
    </bean>
</beans>

实体类
public class HelloWorld {
    private String msg;
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public void say()
    {
        System.out.println(msg);
    }
}
测试类
public class SayMsg {
   
public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
        HelloWorld hw = (HelloWorld) app.getBean("sayBean");
        hw.say();
       
    }
}
#########################【山寨版spring 依赖注入及bean实例化】#######################################

第一步:导包
dom4j-1.6.1.jar   xml解析使用包
jaxen-1.1-beta-6.jar  dom4j依赖包

第二步:模拟BeanDefinition类
/**
 * 模拟spring中的BeanDefinition
 * 此处该类是一个简单的bean类,用于模拟id和classname对应关系
 * @author 狼飞
 */
public class MyBeanDefinition {
    private String id;    //对应xml<bean>标签id属性值
    private String classname; //对应class 属性值
    public MyBeanDefinition(String id, String classname){
        this.id = id;
        this.classname = classname;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getClassname() {
        return classname;
    }
    public void setClassname(String classname) {
        this.classname = classname;
    }
}

第三步:模拟ClassPathXmlApplicationContext
package com.langfei.helloworld;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
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类,对xml文件的进行解析,获取其中包含的实体类配置信息,
 * 通过反射,实现山寨版的spring依赖注入及bean实例化
 * @author 狼飞
 *
 */
public class MyClassPathXmlApplicationContext {
    private List<MyBeanDefinition> beanDefinitions = new ArrayList<MyBeanDefinition>();
    // 用于保存解析xml中的bean实例,key为对应id value为class
    private Map<String, Object> sigletons = new HashMap<String, Object>();

    public MyClassPathXmlApplicationContext(String xmlName) {
        this.readXml(xmlName); // 解析xml
    }

    private void readXml(String xmlName) {
        SAXReader saxReader = new SAXReader();
        Document document = null;
        URL xmlPath = this.getClass().getClassLoader().getSystemResource(xmlName);
        try {
            document = saxReader.read(xmlPath);
        } catch (DocumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Map<String, String> nsMap = new HashMap<String, String>();    //用于缓存命名空间map集
        nsMap.put("ns", "http://www.springframework.org/schema/beans");// 命名空间
        XPath xsub = document.createXPath("//ns:beans/ns:bean"); // 创建beans/bean查询路径
        xsub.setNamespaceURIs(nsMap);    //设置命名空间uri
        List<Element> beansElements = xsub.selectNodes(document);    //获取对应空间下节点集

        for (Element element : beansElements) {
            String id = element.attributeValue("id");
            String clazz = element.attributeValue("class");    //<bean>标签的id属性值及class属性值
            MyBeanDefinition beanDefinition = new MyBeanDefinition(id, clazz);
            beanDefinitions.add(beanDefinition);

            List<Object> propNames = new ArrayList<Object>();    //bean子标签<property>的name属性对应的值,也即实体对象如HellowWorld的属性名
            List<Object> propValues = new ArrayList<Object>();//bean子标签<property>的value属性对应的值,也即实体对象如HellowWorld的属性值
            //采用迭代方法获取<bean>标签中子标签
            for (Iterator<Element> iterator = element.elementIterator(); iterator
                    .hasNext();) {
                Element elementSon = (Element) iterator.next();
                for (Iterator ia = elementSon.attributeIterator(); ia.hasNext();) {
                    ia.next();//获取子标签的name对应的属性值及value属性对应的值
                    propNames.add(elementSon.attributeValue("name"));
                    propValues.add(elementSon.attributeValue("value"));
                }
            }
            try {
                //将<bean>标签class属性值转换为Class实例
                Class clazzClass = Class.forName(clazz);
                Object object = clazzClass.newInstance();//通过反射,将class转换为实体对象实例
                Field[] fields = clazzClass.getDeclaredFields();    //获取实体对象所有属性
                for (Field f : fields) {
                    for (int i = 0; i < propNames.size(); i++) {
                        String propername = f.getName();
                        if (propNames.get(i).equals(propername)) {
                            StringBuilder sb = new StringBuilder();
                            String methodName = sb        //根据属性名拼接setXxx方法
                                    .append("set")
                                    .append(propername.substring(0, 1)
                                            .toUpperCase())
                                    .append(propername.substring(1)).toString();
                            Method method = clazzClass.getMethod(methodName,
                                    String.class);
                            method.invoke(object, propValues.get(i));        //执行set方法,为属性赋值,模拟spring的依赖注入
                        }
                    }
                }
                sigletons.put(id, object);
            } catch (Exception e) {
                e.printStackTrace();
            }
            propNames.clear();
            propValues.clear();
        }
    }

    public Object getBean(String beanName) {
        return this.sigletons.get(beanName);
    }
}
第四步:测试
实体类不需要任何改变,将测试类修改如下
public class SayMsg {
    public static void main(String[] args) {
        /*ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
        HelloWorld hw = (HelloWorld) app.getBean("sayBean");
        hw.say();*/
       
        MyClassPathXmlApplicationContext applicationContext = new MyClassPathXmlApplicationContext("beans.xml");
        HelloWorld hWorld = (HelloWorld) applicationContext.getBean("sayBean");
        hWorld.say();              
    }
}

至此,spring bean原理模拟结束,相比正在的spring bean依赖注入实现,这可谓是小巫见大巫,不是同个级别,不过原理都是那么一回事!

原文地址:http://blog.163.com/langfei520@yeah/blog/static/172710222201282472944140/

原文地址:https://www.cnblogs.com/edison2012/p/2882677.html