- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章使用Spring自定义命名空间由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
Spring在解析xml文件中的标签的时候会区分当前的标签是四种基本标签(import、alias、bean和beans)还是自定义标签,如果是自定义标签,则会按照自定义标签的逻辑解析当前的标签。另外,即使是bean标签,其也可以使用自定义的属性或者使用自定义的子标签。本文将对自定义标签和自定义属性的使用方式进行讲解,并且会从源码的角度对自定义标签和自定义属性的实现方式进行讲解.
Spring框架从2.0版本开始,提供了基于Schema风格的Spring XML格式用来定义bean的扩展机制。引入Schema-based XML是为了对Traditional的XML配置形式进行简化。通过Schema的定义,把一些原本需要通过几个bean的定义或者复杂的bean的组合定义的配置形式,用另外一种简单而可读的配置形式呈现出来.
Schema-based XML由三部分构成,我们由一幅图说明:
namespace
―― 拥有很明确的逻辑分类element
―― 拥有很明确的过程语义attributes
―― 拥有很简明的配置选项例如,<mvc:annotation-driven />这段配置想要表达的意思,就是在mvc的空间内实现Annotation驱动的配置方式。其中,mvc表示配置的有效范围,annotation-driven则表达了一个动态的过程,实际的逻辑含义是:整个SpringMVC的实现是基于Annotation模式,请为我注册相关的行为模式.
下面将阐述一下怎么写自定义XML的bean definition解析和集成这个解析到Spring IOC容器中。在后面的内容中我们将会提到一个重要的概念那就是bean definition.其实Spring中有一个重要的概念那就是bean.而BeanDefinition这个对象就是对应的标签解析后的对象.
利用下面几个简答的步骤可以创建新的xml配置扩展:
Authoring
一个XML schema用来描述你的自定义element(s)Coding
一个自定义的NamespaceHandler实现(这是一个很简答的步骤,don't worry)Coding
一个或者多个BeanDefinitionParse实现(这是最主要的)Registeringr
把注册上面的到Spring(这也是一个简答的步骤)下面将会依次阐述上面的步骤。例如:我们需要创建一个XML扩展(自定义xml element)允许我们可以用一种简单的方式来配置SimpleDateFormat对象(在java.text包中)。最后我们可以定义一个SimpleDateFormat类型的bean definition如下:
<myns:dateformat id="dateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true"/>
。
创建一个Spring IOC容器的XML配置扩展,我们需要编写一个XML Schema用来描述这个扩展。下面的schema我们可以用来配置SimpleDateFormat对象 。
<!-- myns.xsd (inside package org/springframework/samples/xml) --> <?xml version="1.0" encoding="UTF-8"?><xsd:schema xmlns="http://www.mycompany.com/schema/myns" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" targetNamespace="http://www.mycompany.com/schema/myns" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsd:import namespace="http://www.springframework.org/schema/beans"/> <xsd:element name="dateformat"> <xsd:complexType> <xsd:complexContent> <xsd:extension base="beans:identifiedType"> <xsd:attribute name="lenient" type="xsd:boolean"/> <xsd:attribute name="pattern" type="xsd:string" use="required"/> </xsd:extension> </xsd:complexContent> </xsd:complexType> </xsd:element></xsd:schema>
上面的schema将会用来配置SimpleDateFormat对象。直接在一个xml应用context文件使用<myns:dateformat /> element. 。
<myns:dateformat id="dateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true"/>
注意:上面的XML片段本质上和下面的XML片段意义一样.
<bean id="dateFormat" class="java.text.SimpleDateFormat"> <constructor-arg value="yyyy-HH-dd HH:mm"/> <property name="lenient" value="true"/></bean>
。
针对于上面的的schema,我们需要一个NamespaceHandler用来解析Spring遇到的所有这个特定的namespace配置文件中的所有elements.这个NamespaceHandler将会关心解析myns:dateformat元素.
这个NamespaceHandler接口相对简单,它包括三个重要的方法.
在很多情况下在spring xml配置文件中每个top-level的xml元素代表一个single类型的bean definition(在我们的例子中,是一个single的<myns:dateformat>元素代表一个single的SimpleDateFormat bean definition).Spring提供了很多便利的类用来支持这种场景。在这个例子中,我们将使用NamespaceHandlerSupport.
package org.springframework.samples.xml; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class MyNamespaceHandler extends NamespaceHandlerSupport { public void init() { registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser()); } }
下面讲一下为什么要继承NamespaceHandlerSupport这个抽象类 。
先看一下Spring自定义命名空间加载过程:
XmlBeanDefinitionReader入口 。
DefaultDocumentLoader加载并解析一个XML文件成Document实例,从BeanDefinitionReader中获取EntityResolver和ErrorHandler.
在XmlBeanDefinitionReader中创建BeanDefinitionDocumentReader,在这个BeanDefinitionDocumentReader中遍历Document中的每个Element.
对每个Element,如果是默认的URI(即beans命名空间内的定义),调用parseDefaultElement()方法,否则调用BeanDefinitionParserDelegate中的parseCustomElement()方法.
在parseCustomElement()方法中,它找到当前Element的namespaceURI,然后从NamespaceHandlerResolver中获取自定义的NamespaceHandler,使用该NamespaceHandler来解析这个Element,由于我们已经在init()方法中注册了不同的element name对应的BeanDefinitionParser,因而可以使用这个自定义的BeanDefinitionParser来解析自定义的Element.
其中默认的NamespaceHandlerResolver(DefaultNamespaceHandlerResolver)会找到当前classpath下的所有META-INF/spring.handlers文件,加载进来,读取里面的内容成namespaceURI到NamespaceHandler的map,并初始化所有的NamespaceHandler.
看一下BeanDefinitionParserDelegate中的parseCustomElement()方法:
public BeanDefinition parseCustomElement(Element ele) { return this.parseCustomElement(ele, (BeanDefinition)null);}public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd){ String namespaceUri = this.getNamespaceURI(ele); NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } else { return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }}
其中parse方法为抽象类NamespaceHandlerSupport中的方法:
public BeanDefinition parse(Element element, ParserContext parserContext) { return this.findParserForElement(element, parserContext).parse(element, parserContext);}private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { String localName = parserContext.getDelegate().getLocalName(element); BeanDefinitionParser parser = (BeanDefinitionParser)this.parsers.get(localName); if (parser == null) { parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]", element); } return parser;}
而其中的 。
BeanDefinitionParser parser = (BeanDefinitionParser)this.parsers.get(localName);这句中的parsers是一个HashMap 在继承NamespaceHandlerSupport这个抽象类中的重写了init()方法:
public void init() { registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser()); }
NamespaceHandlerSupport这个类的registerBeanDefinitionParser方法:
private final Map<String, BeanDefinitionParser> parsers = new HashMap(); protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) { this.parsers.put(elementName, parser); }
return this.findParserForElement(element, parserContext).parse(element, parserContext);中的parse方法为抽象类AbstractBeanDefinitionParser中的方法:
public final BeanDefinition parse(Element element, ParserContext parserContext) { AbstractBeanDefinition definition = this.parseInternal(element, parserContext); if (definition != null && !parserContext.isNested()) { try { String id = this.resolveId(element, definition, parserContext); if (!StringUtils.hasText(id)) { parserContext.getReaderContext().error("Id is required for element '" + parserContext.getDelegate().getLocalName(element) + "' when used as a top-level tag", element); } String[] aliases = null; if (this.shouldParseNameAsAliases()) { String name = element.getAttribute("name"); if (StringUtils.hasLength(name)) { aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name)); } } BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases); this.registerBeanDefinition(holder, parserContext.getRegistry()); if (this.shouldFireEvents()) { BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); this.postProcessComponentDefinition(componentDefinition); parserContext.registerComponent(componentDefinition); } } catch (BeanDefinitionStoreException var8) { parserContext.getReaderContext().error(var8.getMessage(), element); return null; } } return definition; }
this.registerBeanDefinition(holder, parserContext.getRegistry());最终会调用BeanDefinitionReaderUtils的registerBeanDefinition方法向IoC容器注册解析的Bean,BeanDefinitionReaderUtils的注册的源码如下:
//将解析的BeanDefinitionHold注册到容器中 public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { //获取解析的BeanDefinition的名称 String beanName = definitionHolder.getBeanName(); //向IoC容器注册BeanDefinition registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); //如果解析的BeanDefinition有别名,向容器为其注册别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String aliase : aliases) { registry.registerAlias(beanName, aliase); } } }
当调用BeanDefinitionReaderUtils向IoC容器注册解析的BeanDefinition时,真正完成注册功能的是DefaultListableBeanFactory.
DefaultListableBeanFactory向IoC容器注册解析后的BeanDefinition:
DefaultListableBeanFactory中使用一个HashMap的集合对象存放IoC容器中注册解析的BeanDefinition,向IoC容器注册的主要源码如下:
//存储注册的俄BeanDefinition private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(); //向IoC容器注册解析的BeanDefiniton public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); //校验解析的BeanDefiniton if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } //注册的过程中需要线程同步,以保证数据的一致性 synchronized (this.beanDefinitionMap) { Object oldBeanDefinition = this.beanDefinitionMap.get(beanName); //检查是否有同名的BeanDefinition已经在IoC容器中注册,如果已经注册, //并且不允许覆盖已注册的Bean,则抛出注册失败异常 if (oldBeanDefinition != null) { if (!this.allowBeanDefinitionOverriding) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else {//如果允许覆盖,则同名的Bean,后注册的覆盖先注册的 if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } } //IoC容器中没有已经注册同名的Bean,按正常注册流程注册 else { this.beanDefinitionNames.add(beanName); this.frozenBeanDefinitionNames = null; } this.beanDefinitionMap.put(beanName, beanDefinition); //重置所有已经注册过的BeanDefinition的缓存 resetBeanDefinition(beanName); } }
。
BeanDefinitionParser的责任是解析定义schema在top-level的XML元素.在解析过程中,我们必须访问XML元素,因此我们可以解析我们自定义的XML内容,例如下面的例子:
package org.springframework.samples.xml; import org.springframework.beans.factory.support.BeanDefinitionBuilder;import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;import org.springframework.util.StringUtils;import org.w3c.dom.Element; import java.text.SimpleDateFormat;public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { 1 protected Class getBeanClass(Element element) { return SimpleDateFormat.class; 2 } protected void doParse(Element element, BeanDefinitionBuilder bean) { // this will never be null since the schema explicitly requires that a value be supplied String pattern = element.getAttribute("pattern"); bean.addConstructorArg(pattern); // this however is an optional property String lenient = element.getAttribute("lenient"); if (StringUtils.hasText(lenient)) { bean.addPropertyValue("lenient", Boolean.valueOf(lenient)); } } }
注意:
。
编码工作完成了.下面是事情就是在Spring XML解析的逻辑能够织入我们定义的element;我们需要在两个特定的目标properties文件中注册我们自定义的namespaceHandler和自定义的XSD文件。这些properties文件都需要被放置在你项目中的'META-INF'应用下.
1) META-INF/spring.handlers 。
这个properties文件叫做'spring.handlers',包含XML Schema URIS与namespace处理类的映射。在我们的例子中,我们需要像下面:
http\://www.mycompany.com/schema/myns=org.springframework.samples.xml.MyNamespaceHandler
键值对的第一个部分(key)是关联你自定义namespace扩展的URI,并且需要匹配你自定义XSD Schema中的'targetNamespace'属性.
NamespaceHandlerResolver(DefaultNamespaceHandlerResolver)会找到当前classpath下的所有META-INF/spring.handlers文件,加载进来,读取里面的内容成namespaceURI到NamespaceHandler的map,并初始化所有的NamespaceHandler.
DefaultNamespaceHandlerResolver的resolve方法:
public NamespaceHandler resolve(String namespaceUri) { Map<String, Object> handlerMappings = this.getHandlerMappings(); Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler)handlerOrClassName; } else { String className = (String)handlerOrClassName; try { Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } else { NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(handlerClass); namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } } catch (ClassNotFoundException var7) { throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", var7); } catch (LinkageError var8) { throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", var8); } } } private Map<String, Object> getHandlerMappings() { if (this.handlerMappings == null) { synchronized(this) { if (this.handlerMappings == null) { try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (this.logger.isDebugEnabled()) { this.logger.debug("Loaded NamespaceHandler mappings: " + mappings); } Map<String, Object> handlerMappings = new ConcurrentHashMap(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } catch (IOException var5) { throw new IllegalStateException("Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", var5); } } } } return this.handlerMappings; }
PropertiesLoaderUtils中的loadAllProperties方法:
public static Properties loadAllProperties(String resourceName, ClassLoader classLoader) throws IOException { Assert.notNull(resourceName, "Resource name must not be null"); ClassLoader classLoaderToUse = classLoader; if (classLoader == null) { classLoaderToUse = ClassUtils.getDefaultClassLoader(); } Enumeration<URL> urls = classLoaderToUse != null ? classLoaderToUse.getResources(resourceName) : ClassLoader.getSystemResources(resourceName); Properties props = new Properties(); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); URLConnection con = url.openConnection(); ResourceUtils.useCachesIfNecessary(con); InputStream is = con.getInputStream(); try { if (resourceName.endsWith(".xml")) { props.loadFromXML(is); } else { props.load(is); } } finally { is.close(); } } return props; }
2) META-INF/spring.schemas 。
这个properties文件叫做'spring.schemas',包含XML Schema在classpath中的位置。这个文件主要是防止spring在网络上加载这个schema文件。如果你指定这个properties文件影射,Spring将会在classpath中查找这个schema(在这种情况下会在'org.springframework.samples.xml'包下面的'myns.xsd') 。
http\://www.mycompany.com/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd
你最好部署你的XSD文件在你的NamespaceHandler和BeanDefinition类的classpath中.
。
如果你已经完成了以上步骤,那么你就成功的定义了你自己的BeanDefinition在Spring IOC容器中。我们可以Spring IOC容器获取到并使用这个Bean对象.
1)配置文件 。
schema-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:myns="http://www.mycompany.com/schema/myns" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.mycompany.com/schema/myns http://www.mycompany.com/schema/myns/myns.xsd"> <myns:dateformat id="dateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true"/> </beans>
2)测试类 。
SchemaBeanDefinitionTest.java 。
package org.springframework.samples.xml; import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext; import java.text.SimpleDateFormat;public class SchemaBeanDefinitionTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("schema-beans.xml"); SimpleDateFormat dateFormat = context.getBean("dateFormat", SimpleDateFormat.class); System.out.println("-------------------gain object--------------------"); System.out.println(dateFormat); } }
3)项目结构:
注意:在idea静态资源必须放在resources下面,不然会报错.
4)运行结果:
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我.
原文链接:https://blog.csdn.net/u014082714/article/details/81283952 。
最后此篇关于使用Spring自定义命名空间的文章就讲到这里了,如果你想了解更多关于使用Spring自定义命名空间的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我尝试阅读有关 Spring BOM、Spring Boot 和 Spring IO 的文档。 但是没有说明,我们应该如何一起使用它们? 在我的项目中,我们已经有了自己的 Parent POM ,所以
我正在开发的很酷的企业应用程序正在转向 Spring。这对所有团队来说都是非常酷和令人兴奋的练习,但也是一个巨大的压力源。我们所做的是逐渐将遗留组件移至 Spring 上下文。现在我们有一个 huuu
我正在尝试使用 @Scheduled 运行 Spring 批处理作业注释如下: @Scheduled(cron = "* * * * * ?") public void launchMessageDi
我对这两个概念有点困惑。阅读 Spring 文档,我发现,例如。 bean 工厂是 Spring 容器。我还读到“ApplicationContext 是 BeanFactory 的完整超集”。但两者
我们有一个使用 Spring BlazeDS 集成的应用程序。到目前为止,我们一直在使用 Spring 和 Flex,它运行良好。我们现在还需要添加一些 Spring MVC Controller 。
假设我有一个类(class) Person带属性name和 age ,它可以像这样用 Spring 配置: 我想要一个自定义的 Spring 模式元素,这很容易做到,允许我在我的 Sp
如何在 Java 中以编程方式使用 Spring Data 创建 MongoDB 复合索引? 使用 MongoTemplate 我可以创建一个这样的索引:mongoTemplate.indexOps(
我想使用 spring-complex-task 执行我的应用程序,并且我已经构建了复杂的 spring-batch Flow Jobs,它执行得非常好。 你能解释一下spring批处理流作业与spr
我实现了 spring-boot 应用程序,现在我想将它用作非 spring 应用程序的库。 如何初始化 lib 类,以便 Autowiring 的依赖项按预期工作?显然,如果我使用“new”创建类实
我刚开始学习 spring cloud security,我有一个基本问题。它与 Spring Security 有何不同?我们是否需要在 spring boot 上构建我们的应用程序才能使用 spr
有很多人建议我使用 Spring Boot 而不是 Spring 来开发 REST Web 服务。我想知道这两者到底有什么区别? 最佳答案 总之 Spring Boot 减少了编写大量配置和样板代码的
您能向我解释一下如何使用 Spring 正确构建 Web 应用程序吗?我知道 Spring 框架的最新版本是 4.0.0.RELEASE,但是 Spring Security 的最新版本是 3.2.0
我如何才能知道作为 Spring Boot 应用程序的一部分加载的所有 bean 的名称?我想在 main 方法中有一些代码来打印服务器启动后加载的 bean 的详细信息。 最佳答案 如spring-
我有一个使用 Spring 3.1 构建的 RESTful API,也使用 Spring Security。我有一个 Web 应用程序,也是一个 Spring 3.1 MVC 应用程序。我计划让移动客
升级到 Spring 5 后,我在 Spring Rabbit 和 Spring AMQP 中遇到错误。 两者现在都设置为 1.5.6.RELEASE 有谁知道哪些版本应该与 Spring 5 兼容?
我现在已经使用 Spring Framework 3.0.5 和 Spring Security 3.0.5 多次了。我知道Spring框架使用DI和AOP。我还知道 Spring Security
我收到错误 Unable to Location NamespaceHandler when using context:annotation-config running (java -jar) 由
在 Spring 应用程序中嵌入唯一版本号的策略是什么? 我有一个使用 Spring Boot 和 Spring Web 的应用程序。 它已经足够成熟,我想对其进行版本控制并在运行时看到它显示在屏幕上
我正在使用 spring data jpa 进行持久化。如果存在多个具有相同名称的实体,是否有一种方法可以将一个实体标记为默认值。类似@Primary注解的东西用来解决多个bean的依赖问题 @Ent
我阅读了 Spring 框架的 DAOSupport 类。但是我无法理解这些 DAOSuport 类的优点。在 DAOSupport 类中,我们调用 getXXXTemplate() 方法来获取特定的
我是一名优秀的程序员,十分优秀!