java 读取配置文件原理_Spring读取xml配置文件的原理与实现

本篇博文的目录:

一:前言

二:spring的配置文件

三:依赖的第三方库、使用技术、代码布局

四:Document实现

五:获取Element的实现

六:解析Element元素

七:Bean创造器

八:Ioc容器的创建

九:总结

一:前言:

Spring作为Bean的管理容器,在我们的项目构建中发挥了举足轻重的作用,尤其是控制反转(IOC)和依赖(DI)注入的特性,将对象的创建完全交给它来实现,当我们把与其他框架进行整合时,比如与Mybatis整合,可以把sqlMapClientTemplate、数据源等Bean交给它来管理,这样在我们程序需要的时候,只需要调用它的getBean(String id)方法就可以获取它的一个实例。这一点我们都知道它是利用反射的原理,取得class然后获取constructor-arg配置的参数,然后调用newInstance()方法进行实例化的,那么我们在spring配置文件中配置的Bean的属性,比如Lazy-int、AutoWire属性Spring是如何解析的呢?这背后又是怎样的原理呢。这就是本篇博文将要探讨的问题,我们将深入到代码层面,看一看Spring解析Xml的具体原理

二:Spring的配置文件

我们先来看一个简单的Spring配置文件,由配置文件我们看到了Spring配置文件的一系列属性,配置Bean的id,class属性,还有Lazy-int、AutoWire、SingleTon等属性,配置了这些东西,那么Spring就会按照我们配置的东西进行解析,从而得到我们需要的值。

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd

http://www.springframework.org/schema/jdbc

http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd

http://www.springframework.org/schema/cache

http://www.springframework.org/schema/cache/spring-cache-3.1.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd

http://www.springframework.org/schema/util

http://www.springframework.org/schema/util/spring-util.xsd">

1999.9

三:依赖的第三方库、使用技术、代码布局

spring解析xml配置的第三方库需要的是dom4j,使用的技术是java,代码布局会按照Document、Element、BeanCreator的方式进行,首先定义相关的接口,然后定义子类去实例化,我们来展示一下Spring解析配置文件接口的思维导图,从中可以看出我们定义了一系列的读取属性的接口,比如AutoWire属性,因为有两种情况ByName和no、byType三种情况(这里只为了说明问题,我们只定义两个实现类,正式的话是三个实现类),这里采用的是状态设计模式,设计一个总接口,然后对不同的情况,我们定义相关的实现类,是那种情况,就返回具体的类。如图展示的是接口和具体的实现类,接下来我们将会按照这样的方式去讲解每一个接口对应的实现类。

0971709f6b8caab270b09e10ab9d782d.png

四:获取Document实现

按照从大到小的思维,我们先来实现DocumenHoler接口,可以看出这个接口我们只定义了一个方法,根据路径返回具体的Document。然后我们来写具体的实现子类,有了这样的类,我们只需要传入一个路径,那么就会返回一个模拟的Document对象

importorg.dom4j.Document;public interfaceDocumentHolder {

Document getDocument(String filePath);

}

importjava.io.File;importjava.util.HashMap;importjava.util.Map;importorg.dom4j.Document;importorg.dom4j.io.SAXReader;public class XMLDocumentHolder implementsDocumentHolder{//建立一个HashMap用来存放字符串和文档

private Map docs = new HashMap();

@OverridepublicDocument getDocument(String filePath) {

Document doc=this.docs.get(filePath);//用HashMap先根据路径获取文档

if (doc==null) {this.docs.put(filePath, readDocument(filePath)); //如果为空,把路径和文档放进去

}return this.docs.get(filePath);

}/*** 根据路径读Document

*@paramfilePath

*@return

*/

privateDocument readDocument(String filePath) {

Document doc=null;try{

SAXReader reader= new SAXReader(true);//借用dom4j的解析器

reader.setEntityResolver(newIocEntityResolver());

File xmlFile= new File(filePath); //根据路径创建文件

doc= reader.read(xmlFile);//用dom4j自带的reader读取去读返回一个Document

}catch(Exception e) {

e.printStackTrace();

}returndoc;

}

}

五:获取Element的接口实现

有了Document,按照从大到小的逻辑,我们就需要解析Element元素了,也就是具体的元素加载器,首先我们先来定义一个接口,然后再定义具体的类去实现这个接口。中间主要定义了三个方法添加元素和获取元素,和获取所有的元素,在子类中定义了一个HashMap用于键用来存储属性名,值用来存储具体的Element元素。

importjava.util.Collection;importorg.dom4j.Document;importorg.dom4j.Element;/*** 加载Element元素

*@authorYiron

**/

public interfaceElementLoader {voidaddElements(Document doc);//添加元素

Element getElement(String id);//获取元素

CollectiongetElements();//获取所有的元素

}

importjava.util.Collection;importjava.util.HashMap;importjava.util.List;importjava.util.Map;importorg.dom4j.Document;importorg.dom4j.Element;public class ElementLoaderImpl implementsElementLoader{private Map elements=new HashMap();public voidaddElements(Document doc) {

@SuppressWarnings("unchecked")

List eles =doc.getRootElement().elements();for(Element e : eles) {

String id=e.attributeValue("id");

elements.put(id, e);

}

}publicElement getElement(String id) {returnelements.get(id);

}public CollectiongetElements() {return this.elements.values();

}

}

六:解析Element元素

在第五步中,我们主要是获取了Element元素,获取到了之后,我们就要对其进行解析了。我们首先来定义一个解析Element的接口,接口里面的方法主要是对xml文件配置的元素作出解析,比如boolean isLazy(Element element)就是对其是否进行懒加载进行判断,然后实现该接口:

importjava.util.List;importorg.dom4j.Element;/*** 解析Element元素

*@authorYiron

**/

public interfaceElementReader {booleanisLazy(Element element);

ListgetConstructorElements(Element element);

String getAttribute(Element element,String name);booleanisSingleTon(Element element);

ListgetPropertyElements(Element element);

AutoWire getAutoWire(Element element);

ListgetConstructorValue(Element element);

ListgetPropertyValue(Element element);

}

packagecom.wyq.ResolveElement;importjava.util.ArrayList;importjava.util.List;importorg.dom4j.Element;importorg.omg.PortableServer.ID_ASSIGNMENT_POLICY_ID;public class ElementReaderImpl implementsElementReader{/*** 判断是否延迟加载*/@Overridepublic booleanisLazy(Element element) {

String lazy= getAttribute(element, "lazy-int");//得到是否懒加载这个元素

Element parent=element.getParent();

Boolean parentLazy= new Boolean(getAttribute(parent, "default-lazy-int"));if(parentLazy) {if ("false".equals(lazy)) return false;return true;

}else{if ("true".equals(lazy)) return true;return false;

}

}/*** 获取constructor-arg节点*/@Overridepublic ListgetConstructorElements(Element element) {

List childrens = element.elements();//得到bean节点下的所有节点

List reslut=new ArrayList();//存放节点的链表

for (Element e : childrens) {//遍历

if ("constructor-arg".equals(e.getName())) {//如果是constructor-arg节点

reslut.add(e);//放入到预设的链表中

}

}return reslut; //返回这个链表

}/*** 根据元素的name获取元素的值*/

publicString getAttribute(Element element, String name) {

String value=element.attributeValue(name);returnvalue;

}/*** 判断是不是单例模式*/

public booleanisSingleTon(Element element) {

Boolean singleTon= new Boolean(getAttribute(element, "singleTon"));returnsingleTon;

}/*** 获得自动注入*/@OverridepublicAutoWire getAutoWire(Element element) {

String value= this.getAttribute(element, "autoWire");

String parentValue=this.getAttribute(element.getParent(),"default-autowire");if ("no".equals(parentValue)) {if ("byName".equals(parentValue)) {return newByNameAutoWire(value);

}else{return newNoAutoWire(value);

}

}else if ("byName".equals(parentValue)) {if("no".equals(value)) return newNoAutoWire(value);return newByNameAutoWire(value);

}return newNoAutoWire(value);

}

@Overridepublic ListgetConstructorValue(Element element) {

List cons=getConstructorElements(element);

List result = new ArrayList();for(Element e : cons) {

List els =e.elements();

DataElement dataElement= getDataElement(els.get(0));

result.add(dataElement);

}returnresult;

}

@Overridepublic ListgetPropertyValue(Element element) {

List properties=getPropertyElements(element);

List reslut=new ArrayList();for(Element e : properties) {

List els=e.elements();

DataElement dataElement= getDataElement(els.get(0));

String value= getAttribute(e, "name");

PropertyElement pe= newPropertyElement(value, dataElement);

reslut.add(pe);

}returnreslut;

}privateDataElement getDataElement(Element element){

String name=element.getName();if ("value".equals(name)) {

String classTypeName=element.attributeValue("type");

String data=element.getText();return newValueElement(getValue(classTypeName,data));

}else if ("ref".equals(name)) {return new RefElement(this.getAttribute(element, "bean"));

}return null;

}privateObject getValue(String className,String data){if (isType(className,"Integer")) {returnInteger.parseInt(data);

}else{returndata;

}

}private booleanisType(String className, String type) {if (className.indexOf(type)!=-1) {return true;

}else{return false;

}

}

@Overridepublic ListgetPropertyElements(Element element) {

List elements =element.elements();returnelements;

}

}

6.2:解析Element的时候,我们定义了几个接口,我们来看看自动注入的源代码,自动注入返回三种情况,我们来模拟实现其中两种ByNameAutoWire与NoAutoWire。还有DataElement,其子类分别是:RefElement和ValueElement分别表示引用元素和值元素。

public interfaceAutoWire { //自动注入

String getValue();

}

public class ByNameAutoWire implementsAutoWire{privateString value;publicByNameAutoWire(String value) {this.value =value;

}

@OverridepublicString getValue() {returnvalue;

}

}

public class NoAutoWire implementsAutoWire{

String value;publicNoAutoWire(String value) {this.value =value;

}publicString getValue() {returnvalue;

}

}

public interfaceDataElement {

String getType();

Object getValue();

}

public class RefElement implementsDataElement{privateObject value;publicRefElement(Object value) {this.value=value;

}

@OverridepublicString getType() {return "ref";

}

@OverridepublicObject getValue() {return this.value;

}

}

public class ValueElement implementsDataElement {privateObject value;publicValueElement(Object value) {this.value =value;

}

@OverridepublicString getType() {return "value";

}

@OverridepublicObject getValue() {return this.value;

}

}

七:Bean创造器

主要创造Bean不使用默认构造器和使用定义的construction-arg参数,当我们配置构造参数的时候,就会拿到这些信息,然后进行反射来调用构建对象,其中还包括获取Setter方法。其代码如下:

importjava.lang.reflect.Method;importjava.util.List;importjava.util.Map;public interfaceBeanCreator {

Object createBeanUseDefaultConstruct(String className);//使用空构造器

Object createBeanUseDefineConstruce(String className,Listargs);//使用定义的构造器

MapgetSetterMethodsMap(Object obj);voidexecuteMethod(Object object,Object argBean,Method method);

}

packagecom.wyq.Bean;importjava.lang.reflect.Constructor;importjava.lang.reflect.Method;importjava.util.ArrayList;importjava.util.HashMap;importjava.util.List;importjava.util.Map;public class BeanCreatorImpl implementsBeanCreator{

@OverridepublicObject createBeanUseDefaultConstruct(String className) {

Object object=null;try{

Class clazz=Class.forName(className);

object=clazz.newInstance();

}catch(ClassNotFoundException e) {

e.printStackTrace();

}catch(InstantiationException e) {

e.printStackTrace();

}catch(IllegalAccessException e) {

e.printStackTrace();

}returnobject;

}/*** className:类的名字

* args:配置的构造参数*/@Overridepublic Object createBeanUseDefineConstruct(String className, Listargs) {

Class[] argsClass=getArgsClasses(args);try{

Class clazz=Class.forName(className);

Constructor constructor=findConstructor(clazz,argsClass);

}catch(Exception e) {//TODO: handle exception

}return null;

}/*** 根据类型和参数查找构造器

*@paramclazz

*@paramargsClass

*@return

*/

private Constructor findConstructor(Class clazz, Class[] argsClass) throwsNoSuchMethodException{

Constructor constructor=getConstructor(clazz,argsClass);if (constructor==null) {

Constructor[] constructors=clazz.getConstructors();for(Constructor c : constructors) {

Class[] constructorArgsClass=c.getParameterTypes();if (constructorArgsClass.length==argsClass.length) {if(isSameArgs(argsClass,constructorArgsClass)) {returnc;

}

}

}

}return null;

}private booleanisSameArgs(Class[] argsClass, Class[] constructorArgsClass) {for (int i = 0; i < argsClass.length; i++) {try{

argsClass[i].asSubclass(constructorArgsClass[i]);if (i==(argsClass.length-1)) {return true;

}}catch(Exception e) {

e.printStackTrace();break;

}

}return false;

}private Constructor getConstructor(Class clazz, Class[] argsClass) throwsSecurityException, NoSuchMethodException {try{

Constructor constructor=clazz.getConstructor(argsClass);returnconstructor;

}catch(Exception e) {return null;

}

}private Class[] getArgsClasses(Listargs) {//装有class类的list集合

List reslut =new ArrayList();for(Object arg : args) {

reslut.add(getClass(arg));

}

Class[] a= newClass[reslut.size()];returnreslut.toArray(a);

}privateClass getClass(Object obj) {if (obj instanceofInteger) {returnInteger.TYPE;

}else if (obj instanceofDouble) {returnDouble.TYPE;

}else if (obj instanceofLong) {returnLong.TYPE;

}else if (obj instanceofFloat) {returnFloat.TYPE;

}else if (obj instanceofCharacter) {returnCharacter.TYPE;

}else if (obj instanceofByte) {returnByte.TYPE;

}returnobj.getClass();

}

@Overridepublic voidexecuteMethod(Object object, Object argBean, Method method) {try{

Class[] paramterTypes=method.getParameterTypes();if (paramterTypes.length==1) {if (isMethodArgs(method,paramterTypes[0])) {

method.invoke(object, argBean);

}

}

}catch(Exception e) {try{throw new BeanCreateException("autoWire exception"+e.getMessage());

}catch(BeanCreateException e1) {

e1.printStackTrace();

}

}

}private booleanisMethodArgs(Method method, Class class1) {

Class[] c=method.getParameterTypes();if (c.length==1) {try{

class1.asSubclass(c[0]);return true;

}catch(Exception e) {

e.printStackTrace();return false;

}

}return false;

}

@Overridepublic MapgetSetterMethodsMap(Object obj) {

List methods=getSetterMethodsList(obj);

Map result=new HashMap();for(Method method : methods) {

String propertyName=getMethodNameWithOutSet(method.getName());

}return null;

}/*** 还原setter方法

*@parammethodname

*@return

*/

privateString getMethodNameWithOutSet(String methodname) {

String propertyName=methodname.replace("set", "");

String firstWord=propertyName.substring(0,1);

String lowerFirstWord=firstWord.toLowerCase();returnpropertyName.replaceFirst(firstWord, lowerFirstWord);

}private ListgetSetterMethodsList(Object obj) {

Class clazz=obj.getClass();

List result=new ArrayList();

Method[] methods=clazz.getMethods();for(Method method : methods) {if (method.getName().startsWith("Set")) {

result.add(method);

}

}returnresult;

}

}

八:定义自己的IoC容器:

首先我们来定义自己的ApplicationContext接口,其中有getBean(String id)方法,通过这个方法就可以获取具体的对象实例,也是我们使用Spring框架中用的最多的一个方法。然后来定义具体的实现子类

public interfaceApplicationContext {

Object getBean(String id);booleancontainsBean(String id);booleanisSingleton(String id);

packagecom.wyq.Ioc;importjava.io.UnsupportedEncodingException;importjava.lang.reflect.Method;importjava.net.URL;importjava.util.ArrayList;importjava.util.Collection;importjava.util.HashMap;importjava.util.List;importjava.util.Map;importorg.dom4j.Document;importorg.dom4j.Element;importcom.wyq.Bean.BeanCreateException;importcom.wyq.Bean.BeanCreator;importcom.wyq.Bean.BeanCreatorImpl;importcom.wyq.Element.ElementLoader;importcom.wyq.Element.ElementLoaderImpl;importcom.wyq.ReadXml.DocumentHolder;importcom.wyq.ReadXml.XMLDocumentHolder;importcom.wyq.ResolveElement.AutoWire;importcom.wyq.ResolveElement.ByNameAutoWire;importcom.wyq.ResolveElement.DataElement;importcom.wyq.ResolveElement.ElementReader;importcom.wyq.ResolveElement.ElementReaderImpl;importcom.wyq.ResolveElement.NoAutoWire;importcom.wyq.ResolveElement.PropertyElement;importcom.wyq.ResolveElement.RefElement;importcom.wyq.ResolveElement.ValueElement;importcom.wyq.SetInput.PropertyHandler;importcom.wyq.SetInput.PropertyHandlerImpl;public class AbstractApplicationContext implementsApplicationContext {protected ElementLoader elementLoader = newElementLoaderImpl();protected DocumentHolder documentHolder = newXMLDocumentHolder();protected Map beans = new HashMap();protected BeanCreator beanCreator = newBeanCreatorImpl();protected ElementReader elementReader = newElementReaderImpl();protected PropertyHandler propertyHandler = newPropertyHandlerImpl();protected voidsetUpElements(String[] xmlPaths){

URL classPathUrl= AbstractApplicationContext.class.getClassLoader().getResource(".");

String classpath;try{

classpath= java.net.URLDecoder.decode(classPathUrl.getPath(), "utf-8");for(String path : xmlPaths) {

Document doc= documentHolder.getDocument(classpath +path);

elementLoader.addElements(doc);

}

}catch(UnsupportedEncodingException e) {

e.printStackTrace();

}

}

@OverridepublicObject getBean(String id) {

Object bean= this.beans.get(id);if (bean == null) {

bean=handleSingleton(id);

}returnbean;

}privateObject handleSingleton(String id) {

Object bean=createBean(id);if(isSingleton(id)) {this.beans.put(id, bean);

}returnbean;

}privateObject createBean(String id) {

Element e=elementLoader.getElement(id);if (e == null) {try{throw new BeanCreateException("element not found" +id);

}catch(BeanCreateException e1) {

e1.printStackTrace();

}

}

Object result=instance(e);

System.out.println("创建bean" +id);

System.out.println("该bean的对象是" +result);

AutoWire autoWire=elementReader.getAutoWire(e);if (autoWire instanceofByNameAutoWire) {//使用名称自动装配

autowireByName(result);

}else if (autoWire instanceofNoAutoWire) {

setterInject(result, e);

}return null;

}protected voidcreateBeans(){

Collection elements =elementLoader.getElements();for(Element element : elements) {boolean lazy =elementReader.isLazy(element);if (!lazy) {

String id= element.attributeValue("id");

Object bean= this.getBean(id);if (bean==null) {

handleSingleton(id);

}

}

}

}private voidsetterInject(Object obj, Element e) {

List properties =elementReader.getPropertyValue(e);

Map propertiesMap =getPropertyArgs(properties);

propertyHandler.setProperties(obj, propertiesMap);

}private Map getPropertyArgs(Listproperties) {

Map result = new HashMap();for(PropertyElement p : properties) {

DataElement de=p.getDataElement();if (de instanceofRefElement) {

result.put(p.getName(),this.getBean((String) de.getValue()));

}else if (de instanceofValueElement) {

result.put(p.getName(), de.getValue());

}

}returnresult;

}private voidautowireByName(Object obj) {

Map methods =propertyHandler.getSetterMethodsMap(obj);for(String s : methods.keySet()) {

Element e=elementLoader.getElement(s);if (e == null)continue;

Object bean= this.getBean(s);

Method method=methods.get(s);

propertyHandler.executeMethod(obj, bean, method);

}

}privateObject instance(Element e) {

String className= elementReader.getAttribute(e, "class");

List constructorElements =elementReader.getConstructorElements(e);if (constructorElements.size() == 0) {returnbeanCreator.createBeanUseDefaultConstruct(className);

}else{

List args =getConstructArgs(e);returnbeanCreator.createBeanUseDefineConstruct(className, args);

}

}private ListgetConstructArgs(Element e) {

List datas =elementReader.getConstructorValue(e);

List result = new ArrayList();for(DataElement d : datas) {if (d instanceofValueElement) {

d=(ValueElement) d;

result.add(d.getValue());

}else if (d instanceofRefElement) {

d=(RefElement) d;

String refid=(String) d.getValue();

result.add(this.getBean(refid));

}

}returnresult;

}

@Overridepublic booleancontainsBean(String id) {

Element element=elementLoader.getElement(id);return element == null ? false : true;

}

@Overridepublic booleanisSingleton(String id) {

Element e=elementLoader.getElement(id);returnelementReader.isSingleTon(e);

}

}

}

九:总结

我们经过上面的代码就完整实现了一个Ioc容器,其中从Document的创造,再到Element的创建,再到解析Element,然后设置我们的Bean创造器,再实现AppliacionContext,这一过程在编码中是不可逆的,因为只有有了Document,才有Element,再然后才有解析,我们的方法进行下一步的解析都需要上层作为参数,只有这样才能完整解析Spring配置文件。我详细阐述了这一流程,其中参阅了不少资料,耗时一星期,希望大家细细体会其中的方法,尤其是方法之间的串联与解析的思路。从中过我们看出Spring解析的原理,我们进一步深入理解Spring框架起到了很大的的作用。同时本篇博文只是起到一个抛砖引玉的作用,示范了一些特性。按照这样的逻辑,我们可以举一反三,像mybaits、Hibernate的配置文件,我们大概就可以知道是如何解析的了。比如mybatis的配置文件,里面的sql配置,如何去拼接sql,无非就是解析文档,把标签里面的内容拿出来,拼接成String,交给jdbc去运行,这就是编程中的举一反三,所以这篇博文不仅仅局限于Spring,有更多的东西值得我们去思考,加深自己的理解。好了,本次讲解就到这里,我们下篇再见,谢谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值