解析xml及整个bean创建过程

1、spring中解析xml配置文件 原理

spring解析xml配置的第三方库需要的是dom4j,使用的技术是java,代码布局会按照Document、Element、BeanCreator的方式进行

spring boot xml文件解析成对象 spring解析xml工具_ide


spring解析xml原理思维导图

2、实现步骤

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

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

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

3、具体实现代码

3.1、解析xml文档

按照从大到小的思维,我们先来实现DocumenHoler接口,可以看出这个接口我们只定义了一个方法,根据路径返回具体的Document。然后我们来写具体的实现子类,有了这样的类,我们只需要传入一个路径,那么就会返回一个模拟的Document对象(获得Document对象成功,1、创建一个map存放document对象。2、用dom4j解析xml配置文件

public interface DocumentHolder {
    
    Document getDocument(String filePath);
}
*********************************************************************************************
创建一个具体的实现类实现这个方法
public class XMLDocumentHolder implements DocumentHolder{
    
    //建立一个HashMap用来存放字符串和文档
    private Map<String, Document> docs = new HashMap<String, Document>();
@Override
    public Document 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
     * @param filePath
     * @return
     */
    private Document readDocument(String filePath) {
        
        Document doc =null;
        
        try {
            
            SAXReader reader = new SAXReader(true);//借用dom4j的解析器
            
            reader.setEntityResolver(new IocEntityResolver());
            
            File xmlFile = new File(filePath); //根据路径创建文件
            
             doc = reader.read(xmlFile);//用dom4j自带的reader读取去读返回一个Document
            
        } catch (Exception e) {
            
            e.printStackTrace();
            
        }
        return doc;
    }
    
}
3.2、获取Element的接口实现

有了Document,按照从大到小的逻辑,我们就需要解析Element元素了,也就是具体的元素加载器,首先我们先来定义一个接口,然后再定义具体的类去实现这个接口。中间主要定义了三个方法添加元素和获取元素,和获取所有的元素,在子类中定义了一个HashMap用于键用来存储属性名,值用来存储具体的Element元素。(获得doc对象的ele标签,将标签中的id,ele对象分别作为key和value存放在一个hashmap中

/**
 * 加载Element元素
 * @author Yiron
 *
 */
public interface ElementLoader {
    
    void addElements(Document doc);//添加元素
    
    Element getElement(String id);//获取元素
    
    Collection<Element> getElements();//获取所有的元素
}
//************************************************************************************************
public class ElementLoaderImpl implements ElementLoader{
private Map<String, Element> elements=new HashMap<String, Element>();
public void addElements(Document doc) {
@SuppressWarnings("unchecked")
        List<Element> eles = doc.getRootElement().elements();
for (Element e : eles) {
String id=e.attributeValue("id");
elements.put(id, e);
        }
    }
public Element getElement(String id) {
return elements.get(id);
    }
public Collection<Element> getElements() {
return this.elements.values();
    }
}
3.3、解析Element元素

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

/**
 * 解析Element元素
 * @author Yiron
 *
 */
public interface ElementReader {
    
    boolean isLazy(Element element);
    
    List<Element> getConstructorElements(Element element);
    
    String getAttribute(Element element,String name);
    
    boolean isSingleTon(Element element);
    
    List<Element> getPropertyElements(Element element);
    
    AutoWire getAutoWire(Element element);
    
    List<DataElement> getConstructorValue(Element element);
    
    List<PropertyElement> getPropertyValue(Element element);
    
}
//********************************************************************************************
public class ElementReaderImpl implements ElementReader{
/**
     * 判断是否延迟加载
     */
    @Override
    public boolean isLazy(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节点
     */
    @Override
    public List<Element> getConstructorElements(Element element) {
List<Element> childrens = element.elements();//得到bean节点下的所有节点
List<Element> reslut=new ArrayList<Element>();//存放节点的链表
for (Element e : childrens) {//遍历
if ("constructor-arg".equals(e.getName())) {//如果是constructor-arg节点
            
                reslut.add(e);//放入到预设的链表中
            }
}
        return reslut; //返回这个链表
    }
/**
     * 根据元素的name获取元素的值
     */
    public String getAttribute(Element element, String name) {
String value = element.attributeValue(name);
    
         return value;
    }
/**
     * 判断是不是单例模式
     */
    public boolean isSingleTon(Element element) {
Boolean singleTon = new Boolean(getAttribute(element, "singleTon"));
return singleTon;
    }
/**
     * 获得自动注入
     */
    @Override
    public AutoWire 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 new ByNameAutoWire(value);
                
            }else {
                return new NoAutoWire(value);
            }
        }else if ("byName".equals(parentValue)) {
            
            if("no".equals(value)) return new NoAutoWire(value);
            
            return new ByNameAutoWire(value);
}
return new NoAutoWire(value);
    }
@Override
    public List<DataElement> getConstructorValue(Element element) {
        
        List<Element> cons=getConstructorElements(element);
        
        List<DataElement> result = new ArrayList<DataElement>();
        
        for (Element e : cons) {
            
            List<Element> els = e.elements();
            
            DataElement dataElement = getDataElement(els.get(0));
            
            result.add(dataElement);
            
        }
        
        return result;
    }
@Override
    public List<PropertyElement> getPropertyValue(Element element) {
        
        List<Element> properties=getPropertyElements(element);
        
        List<PropertyElement> reslut=new ArrayList<PropertyElement>();
        
        for (Element e : properties) {
            
            List<Element> els=e.elements();
            
            DataElement dataElement = getDataElement(els.get(0));
            
            String value = getAttribute(e, "name");
            
            PropertyElement pe = new PropertyElement(value, dataElement);
            
            reslut.add(pe);
            
        }
        
        
        return reslut;
    }
    
    
    private DataElement getDataElement(Element element){
        
        String name=element.getName();
        
        if ("value".equals(name)) {
            
            String classTypeName=element.attributeValue("type");
            
            String data = element.getText();
            
            return new ValueElement(getValue(classTypeName,data));
            
        }else if ("ref".equals(name)) {
            
            return new RefElement(this.getAttribute(element, "bean"));
            
        }
        
        return null;
    }
    
    private Object getValue(String className,String data){
        
        if (isType(className,"Integer")) {
            
            return Integer.parseInt(data);
            
        }else {
            
            return data;
        }
        
    }
private boolean isType(String className, String type) {
        
        if (className.indexOf(type)!=-1) {
            
            return true;
            
        }else {
            
            return false;
            
        }
        
    }
@Override
    public List<Element> getPropertyElements(Element element) {
    
           List<Element> elements = element.elements();
        
          return elements;
    }
    
}
3.4、解析Element的时候,我们定义了几个接口,我们来看看自动注入的源代码。

自动注入返回三种情况,我们来模拟实现其中两种ByNameAutoWire与NoAutoWire。还有DataElement,其子类分别是:RefElement和ValueElement分别表示引用元素和值元素。(得到所有标签的属性之后,用autowired举例怎么实现的到的属性)

public interface AutoWire {  //自动注入相关接口
    
    String getValue();
}
//********************************************************************
public class ByNameAutoWire implements AutoWire{
    
    private String value;
public ByNameAutoWire(String value) {
        
        this.value = value;
    }
@Override
    public String getValue() {
        
        return value;
    }
}
3.5、Bean创造器

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

public interface BeanCreator  { 
Object createBeanUseDefaultConstruct(String className);//使用空构造器
Object createBeanUseDefineConstruce(String className,List<Object> args);//使用定义的构造器
Map<String, Method> getSetterMethodsMap(Object obj);
void executeMethod(Object object,Object argBean,Method method);
}
//*************************************************************************************
public class BeanCreatorImpl implements BeanCreator{
@Override
    public Object 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();
        }
return object;
}
    /**
     * className:类的名字
     * args:配置的构造参数
     */
@Override
    public Object createBeanUseDefineConstruct(String className, List<Object> args) {
Class[] argsClass=getArgsClasses(args);
try {
Class clazz = Class.forName(className);
Constructor constructor=findConstructor(clazz,argsClass);
} catch (Exception e) {
            // TODO: handle exception
        }
return null;
    }
/**
     * 根据类型和参数查找构造器
     * @param clazz
     * @param argsClass
     * @return
     */
    private Constructor findConstructor(Class clazz, Class[] argsClass) throws NoSuchMethodException{
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)) {
return c;
}
}
            }
}
return null;
    }
private boolean isSameArgs(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) throws SecurityException, NoSuchMethodException {
try {
Constructor constructor = clazz.getConstructor(argsClass);
return constructor;
} catch (Exception e) {
return null;
        }
    }
private Class[] getArgsClasses(List<Object> args) {
//装有class类的list集合
        List<Class> reslut =new ArrayList<Class>();
for (Object arg : args) {
reslut.add(getClass(arg));
        }
Class[] a = new Class[reslut.size()];
        
        return reslut.toArray(a);
    }
private Class getClass(Object obj) {
if (obj instanceof Integer) {
return Integer.TYPE;
}else if (obj instanceof Double) {
return Double.TYPE;
        }else if (obj instanceof Long) {
return Long.TYPE;
}else if (obj instanceof Float) {
return Float.TYPE;
}else if (obj instanceof Character) {
return Character.TYPE;
}else if (obj instanceof Byte) {
return Byte.TYPE;
        }
return obj.getClass();
    }
@Override
    public void executeMethod(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 boolean isMethodArgs(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;
    }
    @Override
    public Map<String, Method> getSetterMethodsMap(Object obj) {
        
      List<Method>    methods=getSetterMethodsList(obj);
      
       Map<String, Method> result=new HashMap<String ,Method>();
        
        for (Method method : methods) {
            
            String propertyName=getMethodNameWithOutSet(method.getName());
            
        }
        
        
        
        
return null;
    }
    
    /**
     * 还原setter方法
     * @param methodname
     * @return
     */
    
    private String getMethodNameWithOutSet(String methodname) {
        
        String propertyName=methodname.replace("set", "");
        
        String firstWord=propertyName.substring(0,1);
        
        String lowerFirstWord = firstWord.toLowerCase();
        
        return propertyName.replaceFirst(firstWord, lowerFirstWord);
        
    }
    
    
    private List<Method> getSetterMethodsList(Object obj) {
        
        Class clazz = obj.getClass();
        
        List<Method> result=new ArrayList<Method>();
        
        Method[] methods = clazz.getMethods();
        
        for (Method method : methods) {
            
            if (method.getName().startsWith("Set")) {
                
                result.add(method);
                
            }
        }
        
        return result;
    }
}
3.6、自定义自己的IoC容器:

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

public interface ApplicationContext {
    
    Object getBean(String id);
    
    boolean containsBean(String id);
    
    boolean isSingleton(String id);
}
//********************************************************************************
public class AbstractApplicationContext implements ApplicationContext {
protected ElementLoader elementLoader = new ElementLoaderImpl();
protected DocumentHolder documentHolder = new XMLDocumentHolder();
protected Map<String, Object> beans = new HashMap<String, Object>();
protected BeanCreator beanCreator = new BeanCreatorImpl();
protected ElementReader elementReader = new ElementReaderImpl();
protected PropertyHandler propertyHandler = new PropertyHandlerImpl();
protected void setUpElements(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();
        }
    }
@Override
    public Object getBean(String id) {
Object bean = this.beans.get(id);
if (bean == null) {
bean = handleSingleton(id);
}
return bean;
    }
private Object handleSingleton(String id) {
Object bean = createBean(id);
if (isSingleton(id)) {
this.beans.put(id, bean);
}
return bean;
    }
private Object 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 instanceof ByNameAutoWire) {
// 使用名称自动装配
            autowireByName(result);
} else if (autoWire instanceof NoAutoWire) {
setterInject(result, e);
}
return null;
    }
protected void createBeans(){
        
        Collection<Element> 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 void setterInject(Object obj, Element e) {
List<PropertyElement> properties = elementReader.getPropertyValue(e);
Map<String, Object> propertiesMap = getPropertyArgs(properties);
propertyHandler.setProperties(obj, propertiesMap);
}
private Map<String, Object> getPropertyArgs(List<PropertyElement> properties) {
Map<String, Object> result = new HashMap<String, Object>();
for (PropertyElement p : properties) {
DataElement de = p.getDataElement();
if (de instanceof RefElement) {
result.put(p.getName(), this.getBean((String) de.getValue()));
} else if (de instanceof ValueElement) {
result.put(p.getName(), de.getValue());
}
}
return result;
    }
private void autowireByName(Object obj) {
Map<String, Method> 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);
}
}
private Object instance(Element e) {
String className = elementReader.getAttribute(e, "class");
List<Element> constructorElements = elementReader.getConstructorElements(e);
if (constructorElements.size() == 0) {
return beanCreator.createBeanUseDefaultConstruct(className);
} else {
List<Object> args = getConstructArgs(e);
return beanCreator.createBeanUseDefineConstruct(className, args);
        }
}
private List<Object> getConstructArgs(Element e) {
List<DataElement> datas = elementReader.getConstructorValue(e);
List<Object> result = new ArrayList<Object>();
for (DataElement d : datas) {
if (d instanceof ValueElement) {
d = (ValueElement) d;
result.add(d.getValue());
} else if (d instanceof RefElement) {
d = (RefElement) d;
String refid = (String) d.getValue();
result.add(this.getBean(refid));
}
        }
return result;
    }
@Override
    public boolean containsBean(String id) {
Element element = elementLoader.getElement(id);
return element == null ? false : true;
}
@Override
    public boolean isSingleton(String id) {
Element e = elementLoader.getElement(id);
return elementReader.isSingleTon(e);
    }
}

4、总结

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