博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring源码-自定义标签-4
阅读量:5793 次
发布时间:2019-06-18

本文共 7890 字,大约阅读时间需要 26 分钟。

  一、自定义标签,自定义标签在使用上面相对来说非常常见了,这个也算是spring对于容器的拓展。通过自定义标签的方式可以创造出很多新的配置方式,并且交给容器直接管理,不需要人工太多的关注。这也是spring对于配置拓展的一个很重要的方式。

  二、自定义标签的几个步骤:1、创建可扫描的标签和对应的解析类  2、读取页面元素解析 3、加入容器管理

  三、涉及到的常用类:BeanDefinitionParser、NamespaceHandlerSupport;文件:spring.handlers、spring.schemas、*.xsd

  四、实现过程:

  1)需要实现的类:

  

  pojo:

public class User{    private String id;    private String name;    private String age;    public String getId() {        return id;    }    public void setId(String id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getAge() {        return age;    }    public void setAge(String age) {        this.age = age;    }}

  UserBeanDefinitionParser:

public class UserBeanDefinitionParser implements BeanDefinitionParser {    //解析xml    public BeanDefinition parse(Element element, ParserContext parserContext) {        //通过读取xml的元素来实现具体配置        String id = element.getAttribute("id");        String name = element.getAttribute("name");        String age = element.getAttribute("age");        //注册到spring容器        BeanDefinitionRegistry registry = parserContext.getRegistry();        BeanDefinition beanDefinition = null;        try {            //这里的RootBeanDefinition为我们常用的注册形式            beanDefinition = new RootBeanDefinition(User.class);            beanDefinition.getPropertyValues().add("id", id);            beanDefinition.getPropertyValues().add("name", name);            beanDefinition.getPropertyValues().add("age", age);            //注册            registry.registerBeanDefinition(id, beanDefinition);        } catch (Exception e) {            e.printStackTrace();        }        return beanDefinition;    }}

  UserNameSpaceHandler:

//用于提供namespace的扫描public class UserNameSpaceHandler extends NamespaceHandlerSupport {    //初始化    public void init() {        //注册解析手段        registerBeanDefinitionParser("user",new UserBeanDefinitionParser());    }}

  2)因为来了,这个没有啥关联啊。那么久需要用到具体的配置来做关联(spring.handlers、spring.schemas、*.xsd)

  

  备注:这个自己默认配置在resources里面就可以。目录必须为META-INF,默认两个文件:spring.handlers、spring.schemas。标签:*.xsd

  spring.handlers:默认处理的NamespaceHandlerSupport

http\://www.pinnet.com/schema/user=com.pinnet.customLabel.UserNameSpaceHandler

  spring.schemas:用于使用的标签格式以及验证

http\://www.pinnet.com/schema/user.xsd=META-INF/user.xsd

  user.xsd:这里写了一个简单的例子(标签的编写这里不做介绍,可以自己查询schema的官网

  3)好了上面的基本准备工作ok了。具体就是容器做的处理了,我们来关注源码的实现部分。

  spring-bean.xml的配置方式:

  注意:有下划线的部分,这里就是引用过后,然后需要进行的配置。xmlns:pinnet中的pinnet可以自己随便取

  测试:

public class Test {    public static void main(String[] args) {        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");        User user1 = (User) context.getBean("user1");        User user2 = (User) context.getBean("user2");        User user3 = (User) context.getBean("user3");        System.out.println(user1);        System.out.println(user2);        System.out.println(user3);    }}

  4)好了重点来了,源码部分。

  这里从自定义标签开始讲起:其他部分可以参考:到7)部分

  parseCustomElement

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {        //判断根节点是否是默认的(也就是spring提供的beans)        if (delegate.isDefaultNamespace(root)) {            NodeList nl = root.getChildNodes();            for(int i = 0; i < nl.getLength(); ++i) {                Node node = nl.item(i);                if (node instanceof Element) {                    Element ele = (Element)node;                    if (delegate.isDefaultNamespace(ele)) {                        this.parseDefaultElement(ele, delegate);                    } else {                        //这里就是自定义的配置方式                        delegate.parseCustomElement(ele);                    }                }            }        } else {            //如果根节点不是自定义的那就自己自定义处理            delegate.parseCustomElement(root);        }    }
public BeanDefinition parseCustomElement(Element ele) {        //解析配置        return this.parseCustomElement(ele, (BeanDefinition)null);    }    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {        //获取Namespace的uri        String namespaceUri = this.getNamespaceURI(ele);        //然后获取NamespaceHandler的实现类NamespaceHandlerSupport的对应实现        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));        }    }

  resolve:

public NamespaceHandler resolve(String namespaceUri) {        //获取所有NamespaceHandler的NamespaceHandlerSupport实现类(过程不详解了)        Map
handlerMappings = this.getHandlerMappings(); //获取具体的NamespaceHandlerSupport实现类 Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler)handlerOrClassName; } else { //我们默认第一次通过spring.handlers,一般都是String类型的 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 = (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); } } }

  parse

public BeanDefinition parse(Element element, ParserContext parserContext) {        //发现解析的BeanDefinitionParser,并调用实现类的parse方法        return this.findParserForElement(element, parserContext).parse(element, parserContext);    }    private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {        //获取本地的localName,在NamespaceHandlerSupport中进行了init,所以会直接获取到UserNameSpaceHandler        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;    }

  五:上面基本的实现过程就是这样子了,包含了源码的实现逻辑。这里需要关注的点在于匹配的过程。其他都是自己手动完成的。

  六、用处:相对于我们人的解析过程,自定义标签的解析的过程并不是很复杂,更多需要自己手动去完成解析。自定义标签的好处在于,可以很大程度的减少代码冗余的情况。通过同一套流程,开发者只需要关注配置就可以了,而不需要关注具体的解析过程和实现逻辑。另外可以更加方便的与spring容器进行深度的整合!

 

 

  

转载于:https://www.cnblogs.com/ll409546297/p/10108555.html

你可能感兴趣的文章
Kotlin 更加优雅的 Builder - 理解 with
查看>>
前端日拱一卒D6——字符编码与浏览器解析
查看>>
深入理解浏览器的缓存机制
查看>>
微软向Linux社区开放60000多项专利:对开源微软是认真的
查看>>
Hoshin Kanri在丰田的应用
查看>>
又拍云沈志华:如何打造一款安全的App
查看>>
克服大数据集群的挑战
查看>>
PostgreSQL并发控制(MVCC, 事务,事务隔离级别)
查看>>
DM***的第二阶段OSPF
查看>>
20180702搭建青岛RAC记录
查看>>
Spring Security OAuth 实现OAuth 2.0 授权
查看>>
linux文件及简单命令学习
查看>>
dubbo源码分析-架构
查看>>
新 Terraform 提供商: Oracle OCI, Brightbox, RightScale
查看>>
6套毕业设计PPT模板拯救你的毕业答辩
查看>>
IT兄弟连 JavaWeb教程 JSP与Servlet的联系
查看>>
Windows phone 8 学习笔记
查看>>
linux并发连接数:Linux下高并发socket最大连接数所受的各种限制
查看>>
详解区块链中EOS的作用。
查看>>
我的友情链接
查看>>