一、方法简介
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;
}
}