SpringのschemaResolaver

26166 ワード

Spring解析xmlは以上を参考にして、カスタムschemaを指定して、Jdkで提供されるxml API解析xmlを使用します.
次にSpringは自分のschemaに対して、自分で定義したxml元素を解析してSpringのBeanに注入します.
Springの高拡張性を維持するために、ユーザーはSpringに基づいて最大限に開放することができます.ここではSchema Resolaverを採用しています.解析器は最も基本的なDockment Elementを採用しています.
ここでは基本的なSpringのプラグインシステムではないが、理由は同じです.

package org.frame.base.xml.jdk.bk;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.frame.base.xml.jdk.Contact;
import org.frame.base.xml.jdk.ContactName;
import org.frame.base.xml.jdk.MaySimpleSaxErrorHandler;
import org.frame.base.xml.jdk.MyPluggableSchemaResolver;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
* Spring schema, schema , Spring
*
* @author ycl
* @version 1.0 2012-12-17 4:59:08
* @since 1.0
*
*/
public class TestSpringNamespaceHandler {
protected final static Log logger = LogFactory.getLog(TestSpringNamespaceHandler.class);

public static MyNamespaceHandlerResolver namespaceHandlerResolver = new DefaultMyNamespaceHandlerResolver();

public static void main(String[] args) {
// set jaxp debug
System.setProperty("jaxp.debug", "1");
DocumentBuilderFactory builderFactory = DocumentBuilderFactory
.newInstance();

//
builderFactory.setNamespaceAware(true);
builderFactory.setValidating(true);
builderFactory.setIgnoringComments(true);
builderFactory.setAttribute(
"http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema");
// xsd, schemaLanguage schema .

ErrorHandler errorHandler = new MaySimpleSaxErrorHandler(logger);
//
EntityResolver entityResolver = new MyPluggableSchemaResolver(
TestDocumentBuilderFactory.class.getClassLoader());
//

Document document = parse(builderFactory, getInputSource(),
entityResolver, errorHandler);

print(document);
printByHander(document);
}

private static void printByHander(Document document){
Map context = new HashMap();
Element root = document.getDocumentElement();

String namespaceUri = getNamespaceURI(root);
MyNamespaceHandler handler = namespaceHandlerResolver.resolve(namespaceUri);
handler.parse((Element) root,context);
//
parserElement(root,context);
Contact contact = (Contact)context.get("contact");
System.out.println(contact);
}

private static void parserElement(Element root,Map context) {
NodeList nodes = root.getChildNodes();
if(nodes!=null&&nodes.getLength()>0){
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
String namespaceUri = getNamespaceURI(node);
if(namespaceUri!=null){
MyNamespaceHandler handler = namespaceHandlerResolver.resolve(namespaceUri);
handler.parse((Element) node,context);
parserElement((Element)node,context);
}
}
}
}

private static void print(Document document) {
Element root = document.getDocumentElement();

List contactNameList = new ArrayList();

ContactName contactItem;

//
NodeList nodes = root.getChildNodes();

/**
* code this is so tied.
*/
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node instanceof Element) {
// a child element to process
Element child = (Element) node;
String width = child.getAttribute("width");
contactItem = new ContactName();
contactItem.setWidth(width);
NodeList itemSub = node.getChildNodes();
for (int j = 0; j < itemSub.getLength(); j++) {
Node itemSubNode = itemSub.item(j);
if (itemSubNode instanceof Element) {
if (((Element) itemSubNode).getTagName().equals("uic")) {
contactItem.setUid(itemSubNode.getTextContent());
} else if (((Element) itemSubNode).getTagName().equals(
"fullName")) {
contactItem.setFullName(itemSubNode
.getTextContent());
}
}
}
contactNameList.add(contactItem);
}
}
System.out.println(contactNameList);
}

/**
* namespace
* @param node
* @return
*/
public static String getNamespaceURI(Node node) {
return node.getNamespaceURI();
}


private static InputSource getInputSource() {
StringBuffer xml = new StringBuffer(
"");
xml.append("");
xml.append("");
xml.append("");
xml.append("1");
xml.append("ycl1");
xml.append("
");

xml.append("");
xml.append("1 06:05 Jutarnji]]> 2");
xml.append("ycl2");
xml.append("
");

xml.append("
");

InputSource is = new InputSource(new StringReader(xml.toString()));
return is;
}

private static Document parse(DocumentBuilderFactory builderFactory,
InputSource is, EntityResolver entityResolver,
ErrorHandler errorHandler) {
DocumentBuilder builder = null;

Document document = null;
try {
builder = builderFactory.newDocumentBuilder();
if (entityResolver != null) {
builder.setEntityResolver(entityResolver);
}
if (errorHandler != null) {
builder.setErrorHandler(errorHandler);
}
document = builder.parse(is);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return document;
}
}

テストクラスは主にschemaを使って、schema Resolaverを定義します.

package org.frame.base.xml.jdk.bk;


public interface MyNamespaceHandlerResolver {

/**
* schema name handler
* @param namespaceUri
* @return
*/
MyNamespaceHandler resolve(String namespaceUri);
}


/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.frame.base.xml.jdk.bk;

import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader;
import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.NamespaceHandlerResolver;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;

/**
* Default implementation of the {@link NamespaceHandlerResolver} interface.
* Resolves namespace URIs to implementation classes based on the mappings
* contained in mapping file.
*
*

By default, this implementation looks for the mapping file at
* META-INF/spring.handlers, but this can be changed using the
* {@link #DefaultNamespaceHandlerResolver(ClassLoader, String)} constructor.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 2.0
* @see NamespaceHandler
* @see DefaultBeanDefinitionDocumentReader
*/
public class DefaultMyNamespaceHandlerResolver implements MyNamespaceHandlerResolver {

/**
* The location to look for the mapping files. Can be present in multiple JAR files.
*/
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/myschema.handlers";


/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());

/** ClassLoader to use for NamespaceHandler classes */
private final ClassLoader classLoader;

/** Resource location to search for */
private final String handlerMappingsLocation;

/** Stores the mappings from namespace URI to NamespaceHandler class name / instance */
private volatile Map handlerMappings;


/**
* Create a new DefaultNamespaceHandlerResolver using the
* default mapping file location.
*

This constructor will result in the thread context ClassLoader being used
* to load resources.
* @see #DEFAULT_HANDLER_MAPPINGS_LOCATION
*/
public DefaultMyNamespaceHandlerResolver() {
this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}

/**
* Create a new DefaultNamespaceHandlerResolver using the
* default mapping file location.
* @param classLoader the {@link ClassLoader} instance used to load mapping resources
* (may be null, in which case the thread context ClassLoader will be used)
* @see #DEFAULT_HANDLER_MAPPINGS_LOCATION
*/
public DefaultMyNamespaceHandlerResolver(ClassLoader classLoader) {
this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}

/**
* Create a new DefaultNamespaceHandlerResolver using the
* supplied mapping file location.
* @param classLoader the {@link ClassLoader} instance used to load mapping resources
* may be null, in which case the thread context ClassLoader will be used)
* @param handlerMappingsLocation the mapping file location
*/
public DefaultMyNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
this.handlerMappingsLocation = handlerMappingsLocation;
}


/**
* Locate the {@link NamespaceHandler} for the supplied namespace URI
* from the configured mappings.
* @param namespaceUri the relevant namespace URI
* @return the located {@link NamespaceHandler}, or null if none found
*/
public MyNamespaceHandler resolve(String namespaceUri) {
Map handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof MyNamespaceHandler) {
return (MyNamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
Class> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!MyNamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + MyNamespaceHandler.class.getName() + "] interface");
}
MyNamespaceHandler namespaceHandler = (MyNamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "] not found", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "]: problem with handler class file or dependent class", err);
}
}
}

/**
* Load the specified NamespaceHandler mappings lazily.
*/
private Map getHandlerMappings() {
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map handlerMappings = new ConcurrentHashMap();
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return this.handlerMappings;
}


@Override
public String toString() {
return "NamespaceHandlerResolver using mappings " + getHandlerMappings();
}

}

私の怠けを許してください.裸のcopy Springソースです.

public interface MyNamespaceHandler {

public void init();
public ContactName parse(Element element,Map context);
}

package org.frame.base.xml.jdk.bk;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.frame.base.xml.jdk.ContactName;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.w3c.dom.Element;

public class SchemaNamespaceHandler implements MyNamespaceHandler {

/**
* The location to look for the mapping files. Can be present in multiple JAR files.
*/
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/schema/contact.handler";

/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());

/** ClassLoader to use for NamespaceHandler classes */
private final ClassLoader classLoader;

/** Resource location to search for */
private final String handlerMappingsLocation;

/** Stores the mappings from namespace URI to NamespaceHandler class name / instance */
private volatile Map handlerMappings;

public SchemaNamespaceHandler(){
this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}

public SchemaNamespaceHandler(ClassLoader classLoader) {
this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}

public SchemaNamespaceHandler(ClassLoader classLoader, String handlerMappingsLocation) {
Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
this.handlerMappingsLocation = handlerMappingsLocation;
}


@Override
public void init() {
// TODO Auto-generated method stub
}

@Override
public ContactName parse(Element element,Map context) {
return resolve(element.getLocalName()).parse(element,context);
}

public MyNamespaceHandler resolve(String namespaceUri) {
Map handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof MyNamespaceHandler) {
return (MyNamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
Class> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!MyNamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + MyNamespaceHandler.class.getName() + "] interface");
}
MyNamespaceHandler namespaceHandler = (MyNamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "] not found", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "]: problem with handler class file or dependent class", err);
}
}
}

/**
* Load the specified NamespaceHandler mappings lazily.
*/
private Map getHandlerMappings() {
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map handlerMappings = new ConcurrentHashMap();
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return this.handlerMappings;
}


@Override
public String toString() {
return "NamespaceHandlerResolver using mappings " + getHandlerMappings();
}


}

次に私達の配置ファイルを見ます.
myschema.handers

http\://www.ycl.com/schema/schema=org.frame.base.xml.jdk.bk.SchemaNamespaceHandler
contact.handler

contact=org.frame.base.xml.jdk.bk.ContactHandler
item=org.frame.base.xml.jdk.bk.ItemHandler
uic=org.frame.base.xml.jdk.bk.UicHandler
fullName=org.frame.base.xml.jdk.bk.FullNameHandler
ここではxmlの元素ごとにハードラーを定義しています.

package org.frame.base.xml.jdk.bk;

import java.util.Map;

import org.frame.base.xml.jdk.Contact;
import org.frame.base.xml.jdk.ContactName;
import org.w3c.dom.Element;

/**
*
*
* @author ycl
* @version 1.0 2012-12-18 9:32:12
* @since 1.0
*
*/
public class ContactHandler implements MyNamespaceHandler {

@Override
public void init() {
// TODO Auto-generated method stub

}

@Override
public ContactName parse(Element element,Map context) {
Contact contactName = new Contact();
context.put("contact", contactName);
return null;
}

}


package org.frame.base.xml.jdk.bk;

import java.util.Map;

import org.frame.base.xml.jdk.Contact;
import org.frame.base.xml.jdk.ContactName;
import org.frame.base.xml.jdk.Item;
import org.w3c.dom.Element;

public class ItemHandler implements MyNamespaceHandler {

@Override
public void init() {
// TODO Auto-generated method stub

}

@Override
public ContactName parse(Element element, Map context) {
Contact contactName = (Contact) context.get("contact");
String width = element.getAttribute("width");
Item item = new Item();
item.setWidth(width);
contactName.addItem(item);
context.put("curentItem", item);
return null;
}

}


package org.frame.base.xml.jdk.bk;

import java.util.Map;

import org.frame.base.xml.jdk.Contact;
import org.frame.base.xml.jdk.ContactName;
import org.frame.base.xml.jdk.Item;
import org.w3c.dom.Element;

public class UicHandler implements MyNamespaceHandler {

@Override
public void init() {
// TODO Auto-generated method stub

}

@Override
public ContactName parse(Element element, Map context) {
Item item = (Item)context.get("curentItem");
String uic = element.getTextContent();
item.setUic(uic);
return null;
}

}


package org.frame.base.xml.jdk.bk;

import java.util.Map;

import org.frame.base.xml.jdk.ContactName;
import org.frame.base.xml.jdk.Item;
import org.w3c.dom.Element;

public class FullNameHandler implements MyNamespaceHandler {

@Override
public void init() {
// TODO Auto-generated method stub

}

@Override
public ContactName parse(Element element, Map context) {
Item item = (Item)context.get("curentItem");
String fullName = element.getTextContent();
item.setFullName(fullName);
return null;
}

}

もちろん、ここの図は簡単で、全体のxmlを解析しますが、reolverでは自分の必要なxml元素を自分のConteextの中に解析できます.contextは任意の中のものを置くことができます.これは文脈です.
出力データは以下の通りです

20121218-10:56:08 main org.springframework.beans.factory.xml.PluggableSchemaResolver Loading schema mappings from [META-INF/myschema.schemas]
20121218-10:56:08 main org.springframework.beans.factory.xml.PluggableSchemaResolver Loaded schema mappings: {http://www.ycl.com/schema/schema.xsd=org/frame/base/xml/jdk/schema.xsd}
20121218-10:56:08 main org.springframework.beans.factory.xml.PluggableSchemaResolver Found XML schema [http://www.ycl.com/schema/schema.xsd] in classpath: org/frame/base/xml/jdk/schema.xsd
[uid:1,fullName:ycl1,width:10, uid:1 06:00 Vesti
06:05 Jutarnji 2,fullName:ycl2,width:11]
20121218-10:56:08 main org.frame.base.xml.jdk.bk.DefaultMyNamespaceHandlerResolver Loaded NamespaceHandler mappings: {http://www.ycl.com/schema/schema=org.frame.base.xml.jdk.bk.SchemaNamespaceHandler}
20121218-10:56:08 main org.frame.base.xml.jdk.bk.SchemaNamespaceHandler Loaded NamespaceHandler mappings: {contact=org.frame.base.xml.jdk.bk.ContactHandler, uic=org.frame.base.xml.jdk.bk.UicHandler, item=org.frame.base.xml.jdk.bk.ItemHandler, fullName=org.frame.base.xml.jdk.bk.FullNameHandler}
[width:10,uic:1,fullName:ycl1,sex:null, width:11,uic:1 06:00 Vesti
06:05 Jutarnji 2,fullName:ycl2,sex:null]

ここではカスタムのhandlerと自分が望む結果を出力できます.
このような解析モードを使うと、xml構造に関係する必要はなく、Javaオブジェクトとの関係は不要です.
あなたが関心を持っているのはxmlだけです.私はxmlの中の元素を解析したいです.xml元素とJavaオブジェクトには必然的な関係がありません.これも伝説の中の「結合」ですか?
私はただ各要素をHandlerに設計しただけです.この解析モードを使えば、属性を使うとコードが短縮され、分かりやすくなります.
SpringはBeanの設計に対してもサブ要素が許されています.それはProptiesです.実は属性であり、サブ要素に置くだけでもより美しく見えます.論理がもっとはっきりしていて、Ben注入の強い優位性がもっと際立っています.