JDOM解析xml文件,通过反射实现ClassPathXmlApplicationContext()方法

一、方法简介

Java读取Xml文件常见的库有:DOM,DOM4J,JDOM,SAX

这里使用的是JDOM。JDOM是一个Java语言用来读写 XML 文档的类库。JDOM 与现行的SAX 和DOM标准兼容,为Java 程序员提供了一个简单、轻量的XML文档操作方法。由于JDOM是专门为Java 程序员开发的,所以采用许多Java语言的优秀特性,比如方法重载、集合(Collections)和类映射(Reflection)。

二、使用示例

xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    1<bean id="user" class="com.xxx.pojo.User">
    </bean>
    2<bean id="userService" class="com.xxx.service.UserService">
        <property name="user" ref="user" />
    3</bean>
    4<sa></sa>
5</beans>

读取xml的代码:

public class JDOMTest {
    public static void main(String[] args) throws Exception {
        SAXBuilder sBuilder = new SAXBuilder();
        Document document = sBuilder.build(new File("D:\\JavaWeb\\beanss.xml"));

        Element root = document.getRootElement();
        List<Content> content1 = root.getContent();
        System.out.println("root的大小为:" + content1.size());
        for (int i = 0; i < content1.size(); i++) {
            System.out.println("内容分别为 " +
                    (i+1));
            System.out.println(content1.get(i));
        }
        System.out.println("---------------------");

        List<Element> children = root.getChildren();
        System.out.println("root包含的Element个数为:"+children.size() );
        
        List beanElementList = root.getChildren("bean");
        System.out.println("beanElementList大小为 = " + beanElementList.size() );
        for (int i = 0; i < beanElementList.size(); i++) {
            System.out.println("---------------------");
            Element element = (Element) beanElementList.get(i);
            List<Content> content2 = element.getContent();
            for (int j = 0; j < content2.size(); j++) {
                System.out.println("beanElement的内容 = " + content2.get(j));
            }

            String id = element.getAttributeValue("id");
            String clazz = element.getAttributeValue("class");
            System.out.println(id + ":" + clazz);

            List<Attribute> attributes = element.getAttributes();
            System.out.println("beanElement的attributes大小为 = " + attributes.size());
            for (int j = 0; j < attributes.size(); j++) {
                System.out.println("attributes内容为 = " + attributes.get(j));
            }

            //bean的子标签中的内容
            for (Element propertyElement : element.getChildren()) {
                String name = propertyElement.getAttributeValue("name");
                String bean = propertyElement.getAttributeValue("ref");
                System.out.println("name = " + name);
                System.out.println("bean = " + bean);
            }
            System.out.println("++++++++++++++++++++++");
        }
    }
}

经过xml与输出内容的对比,得出xml文件的结构为:

  • root=最外层的元素标签(即...),"..."为root的content
  • "..."的内容有文本和元素标签两种 = ...2...3...4
  • 所以root的content大小为:7,包含3个文本和4个元素标签(只要是标签形式即可识别,标签的name可以自定义,如这里的sa标签)
  • root.getChildren("bean")得到所有的 ...
  • 对于每一个... ,id 和 class是该标签的attributes,通过element.getAttributeValue("id")获得
  • 获得 ... 中的...内容,同样使用element.getChildren()
  • children的attributes获取方法也一致。

三、通过反射实现IOC

定义一个BeanFactory的接口,声明一个getBean方法

public interface BeanFactory {
    public Object getBean(String name);
}

定义该接口的实现类ClassPathXmlApplicationContext

public class ClassPathXmlApplicationContext implements BeanFactory {

    //bean的id和对象的映射
    private Map<String, Object> beans = new HashMap<String, Object>();
	
    //有参构造,将得到的相关信息存储在beans中
    public ClassPathXmlApplicationContext(String xmlname) throws Exception {
        SAXBuilder sBuilder = new SAXBuilder();
        Document document = sBuilder.build(this.getClass().getClassLoader().getResourceAsStream(xmlname));

        Element root = document.getRootElement();
        List beanElementList = root.getChildren("bean");

        for (int i = 0; i < beanElementList.size(); i++) {
            Element element = (Element) beanElementList.get(i);
            List<Attribute> attributes = element.getAttributes();

            String id = element.getAttributeValue("id");
            String clazz = element.getAttributeValue("class");
            System.out.println(id + ":" + clazz);
            Object o = Class.forName(clazz).newInstance();
            beans.put(id, o);

            for (Element propertyElement : element.getChildren()) {
                String name = propertyElement.getAttributeValue("name");
                String bean = propertyElement.getAttributeValue("ref");
                Object beanObject = beans.get(bean);
                System.out.println("ref-obj = "+ beanObject);
                String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
                System.out.println("method = " + methodName);
                Method method = null;

                method = o.getClass().getDeclaredMethod(methodName, new Class[]{beanObject.getClass()});
                method.invoke(o, beanObject);
            }
        }
    }
    
	@Override
    public Object getBean(String name) {
        for (Map.Entry e :
                beans.entrySet()) {
            System.out.println("key = " + e.getKey());
            System.out.println("Value = " + e.getValue());
            if (e.getKey().equals(name)){
                return e.getValue();
            }
        }
        return null;
    }
}

UserService类:

public class UserService {

    private User user;

    public void setUser(User user) {
        this.user = user;
    }

    public User getUser() {
        return user;
    }

    public void test(){
        System.out.println("success!");
    }
}

测试类:

public class UserServiceTest {
    @Test
    public void test() throws Exception {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("beanss.xml");
        UserService userService = (UserService) beanFactory.getBean("userService");
        userService.test();
    }
}

成功输出:"success!"

四、带有命名空间的xml解析

现有配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

   <bean id="user" class="com.xxx.pojo.User">
    </bean>
    <bean id="userService" class="com.xxx.service.UserService">
        <property name="user" ref="user"/>
    </bean>
</beans>

针对这种情况,JDOM进行了考虑。区别只是在于getChildren方法。查看其源码,发现该方法有两个参数

/*
 * @param cname local name for the children to match
 * @param ns <code>Namespace</code> to search within. A null implies Namespace.NO_NAMESPACE.
 * @return all matching child elements
 */
public List<Element> getChildren(final String cname, final Namespace ns) {
   return content.getView(new ElementFilter(cname, ns));
}

jdom2的NameSpace提供了两个方法和两个常量,其中的常量XML_NAMESPACE默认的是http://www.w3.org/XML/1998/namespace,但这里使用的spring的命名空间,所以改为如下代码:

List beanElementList = root.getChildren("bean",Namespace.getNamespace("http://www.springframework.org/schema/beans"));

这个时候就可以正常解析xml了,不会识别到有关命名空间的内容。

以上方法都是基于实体类的set方法,改进代码,实现构造器方式的注入。

五、实现构造器方式的注入

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

   <bean id="user" class="com.xxx.pojo.User">
       <constructor-arg type="java.lang.Integer" name="id" value="3"/>
       <constructor-arg type="java.lang.String" name="name" value="xxx"/>
   </bean>
   <bean id="userService" class="com.xxx.service.UserService">
        <property name="user" ref="user"/>
   </bean>
</beans>

ClassPathXmlApplicationContext类:

public class ClassPathXmlApplicationContext implements BeanFactory {

    //bean的id和对象的映射
    private Map<String, Object> beans = new HashMap<String, Object>();

    public ClassPathXmlApplicationContext(String xmlname) throws Exception {

        SAXBuilder sBuilder = new SAXBuilder();
        Document document = sBuilder.build(this.getClass().getClassLoader().getResourceAsStream(xmlname));

        Element root = document.getRootElement();
        List beanElementList = root.getChildren("bean",Namespace.getNamespace("http://www.springframework.org/schema/beans"));

        for (int i = 0; i < beanElementList.size(); i++) {
            Element element = (Element) beanElementList.get(i);
            List<Attribute> attributes = element.getAttributes();
            String id = element.getAttributeValue("id");
            String clazz = element.getAttributeValue("class");
            Object o = Class.forName(clazz).newInstance();
            beans.put(id, o);

            List<String> valueList = new ArrayList<String>();
            List<String> typeList = new ArrayList<String>();
            for (Element propertyElement : element.getChildren()) {
                if(propertyElement.getName().equals("property")){
                    String name = propertyElement.getAttributeValue("name");
                    String bean = propertyElement.getAttributeValue("ref");

                    Object beanObject = beans.get(bean);
                    String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
                    Method method = null;

                    method = o.getClass().getDeclaredMethod(methodName, new Class[]{beanObject.getClass()});
                    method.invoke(o, beanObject);
                }

                if(propertyElement.getName().equals("constructor-arg")){

                    String type = propertyElement.getAttributeValue("type");
                    //type = int
                    typeList.add(type);
                    String name = propertyElement.getAttributeValue("name");
                    //name = id
                    nameAndType.put(name,type);
                    String value = propertyElement.getAttributeValue("value");

                    //value = 1
                    valueList.add(value);

                    if (nameAndType.size()!=element.getChildren().size()){
                        continue;
                    }
                    Object beanObject = beans.get(id);

                    //beanObject = User
                    Class[] classes = new Class[2];
                    for (int i1 = 0; i1 < classes.length; i1++) {
                        classes[i1] = Class.forName(typeList.get(i1));
                    }
                    Constructor declaredConstructor = beanObject.getClass().getDeclaredConstructor(classes);
					
                    //强制类型转换
                    Object[] objects = new Object[2];
                    for (int i1 = 0; i1 < valueList.size(); i1++) {
                        if (typeList.get(i1).equals("java.lang.Integer")){
                            objects[i1] = Integer.valueOf(valueList.get(i1));
                        }
                        if (typeList.get(i1).equals("java.lang.String")){
                            objects[i1] = String.valueOf(valueList.get(i1));
                        }
                    }

                    Object xxx = declaredConstructor.newInstance(objects);
                    beans.put(id,xxx);
                }
            }
        }
    }

    public Object getBean(String name) {
        for (Map.Entry e :
                beans.entrySet()) {
            if (e.getKey().equals(name)){
                return e.getValue();
            }
        }
        return null;
    }
}

posted @ 2021-04-18 15:19  Rookie--;  阅读(166)  评论(0编辑  收藏  举报