spring篇-(spring自定义标签,属性)

初始化项目

项目使用maven构建,项目结构如下
在这里插入图片描述
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>consutom-element</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.5</version>
        </dependency>
    </dependencies>

</project>

基于spring,实现自定义标签

spring自定义标签的规则如下,必须在META-INF下面存在spring.handlers和spring.schemas两个文件,handlers文件用于通过定义的scheme找到对应解析的handler,schemas文件则是通过scheme找到xsd文件
1.定义custom.xsd文件
这里我们定义自定义的xsd文件,用于定义xml里面我们自定义标签的规范
如下,在META-INF目录下面创建schemas目录,将自定义xml的xsd约束放在这个目录下面
在这里插入图片描述

custom.xsd

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://lhstack.com/schema/custom">
    <!--targetNamespace用于定义custom的命名空间-->
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
    <xsd:element name="custom-bean">
        <xsd:annotation>
            <xsd:documentation>
                <![CDATA[
                custom-bean
                ]]>
            </xsd:documentation>
        </xsd:annotation>
        <xsd:complexType>
            <!--这里通过属性的方式定义custom的class-->
            <xsd:attribute name="class" type="xsd:string" />
            <!--这里通过属性的方式定义custom的class name-->
            <xsd:attribute name="name" type="xsd:string" />
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

2.定义spring.schemas文件,用于指向自定义的custom.xsd文件
在这里插入图片描述
spring.schemas

http\://lhstack.com/schema/custom.xsd=META-INF/schemas/custom.xsd

3.定义spring.handlers,用于定义解析自定义标签的handler
在这里插入图片描述

spring.handlers

http\://lhstack.com/schema/custom=com.lhstack.custom.handler.NamespaceParseHandler

4.创建spring.handlers里面指定的class类
在这里插入图片描述
NamespaceParseHandler.java

package com.lhstack.custom.handler;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class NamespaceParseHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        //解析custom-bean标签
        registerBeanDefinitionParser("custom-bean",null);
    }
}

5.创建custom-bean解析器,并在NamespaceParseHandler中使用
在这里插入图片描述

CustomBeanParseDefinition.java

package com.lhstack.custom.parse;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

public class CustomBeanParseDefinition implements BeanDefinitionParser {
    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        //获取属性上面的name字段
        String name = element.getAttribute("name");

        //获取class
        String className = element.getAttribute("class");

        //获取bean工厂
        BeanDefinitionRegistry registry = parserContext.getRegistry();
        //创建beanDefinition
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        //设置bean的class
        beanDefinition.setBeanClassName(className);
        //注册bean到工厂
        registry.registerBeanDefinition(name,beanDefinition);

        return null;
    }
}

在这里插入图片描述
6.在resource目录下面创建spring.xml文件,添加custom-bean标签
在这里插入图片描述
spring.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:custom="http://lhstack.com/schema/custom"
       xsi:schemaLocation="http://lhstack.com/schema/custom http://lhstack.com/schema/custom.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <custom:custom-bean name="test" class="java.util.HashMap" />
</beans>

7.创建启动类,使用spring.xml,自定义标签注册的bean已经到容器里面了
在这里插入图片描述
Applicatin.java

package com.lhstack.custom;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        context.start();
        Object test = context.getBean("test");
        System.out.println(test.getClass());
    }
}

扩展自定义标签,添加对象属性的注入

1.修改custom.xsd定义,添加properties标签
custom.xsd

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://lhstack.com/schema/custom"
            targetNamespace="http://lhstack.com/schema/custom">
    <!--targetNamespace用于定义custom的命名空间-->
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>

    <!--  这里我们指向complexType,如果在element里面定义,不知道为什么,会报错  -->
    <xsd:element name="custom-bean" type="customBeanType">
        <xsd:annotation>
            <xsd:documentation>
                <![CDATA[
                custom-bean
                ]]>
            </xsd:documentation>
        </xsd:annotation>

    </xsd:element>
    
    <!--  定义entry标签,引用entryType  -->
    <xsd:element name="entry" type="entryType" />

    <!--  定义entryType的类型,拥有两个属性  -->
    <xsd:complexType name="entryType">
        <xsd:attribute name="key" type="xsd:string" use="required" />
        <xsd:attribute name="value" type="xsd:string" use="required" />
    </xsd:complexType>
    <xsd:element name="properties" type="propertiesType" />

    <!--  定义propertiesType,添加entry子标签  -->
    <xsd:complexType name="propertiesType">
        <xsd:sequence maxOccurs="unbounded">
            <xsd:element ref="entry" minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
    </xsd:complexType>

    <xsd:complexType name="customBeanType">
        <xsd:sequence minOccurs="0" maxOccurs="1">
            <!--     指向properties标签       -->
            <xsd:element ref="properties" maxOccurs="1" />
        </xsd:sequence>
        <!--这里通过属性的方式定义custom的class-->
        <xsd:attribute name="class" type="xsd:string" />
        <!--这里通过属性的方式定义custom的class name-->
        <xsd:attribute name="name" type="xsd:string" />
    </xsd:complexType>
</xsd:schema>

2.修改之前定义的CustomBeanParseDefinition,添加属性注入的功能
CustomBeanParseDefinition.java

package com.lhstack.custom.parse;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class CustomBeanParseDefinition implements BeanDefinitionParser {
    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        //获取属性上面的name字段
        String name = element.getAttribute("name");

        //获取class
        String className = element.getAttribute("class");

        Map<String,String> attributes = new HashMap<>();
        NodeList childNodes = element.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node item = childNodes.item(i);
            if(item.getNodeName().endsWith(":properties")){
                NodeList entryList = item.getChildNodes();
                for (int j = 0; j < entryList.getLength(); j++) {
                    Node node = entryList.item(j);
                    NamedNodeMap namedNodeMap = node.getAttributes();
                    if(Objects.nonNull(namedNodeMap)){
                        attributes.put(namedNodeMap.getNamedItem("key").getNodeValue(),namedNodeMap.getNamedItem("value").getNodeValue());
                    }

                }
            }
        }
        try{
            //这里转换成class
            Class<?> clazz = Class.forName(className);
            //获取bean工厂
            BeanDefinitionRegistry registry = parserContext.getRegistry();
            //创建beanDefinition
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            //设置bean的class
            beanDefinition.setBeanClass(clazz);

            //如果是map,则通过构造方法的方式把参数注入进去
            if(Map.class.isAssignableFrom(clazz)){
                ConstructorArgumentValues values = new ConstructorArgumentValues();
                values.addIndexedArgumentValue(0,attributes);
                beanDefinition.setConstructorArgumentValues(values);
            }else{
                //如果是普通bean,则通过propertyValues的方式注入
                beanDefinition.getPropertyValues().addPropertyValues(attributes);
            }
            //注册bean到工厂
            registry.registerBeanDefinition(name,beanDefinition);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

3.使用map方式,测试

<?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:custom="http://lhstack.com/schema/custom"
       xsi:schemaLocation="http://lhstack.com/schema/custom http://lhstack.com/schema/custom.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <custom:custom-bean name="test" class="java.util.HashMap" >
        <custom:properties>
            <custom:entry key="spring.application.name" value="app" />
            <custom:entry key="spring.application.test" value="appTest" />
        </custom:properties>
    </custom:custom-bean>
</beans>

在这里插入图片描述
4.创建bean测试
在这里插入图片描述
PropertiesBean.java

package com.lhstack.custom;

public class PropertiesBean {

    private String name;

    private String value;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "PropertiesBean{" +
                "name='" + name + '\'' +
                ", value='" + value + '\'' +
                '}';
    }
}

spring.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:custom="http://lhstack.com/schema/custom"
       xsi:schemaLocation="http://lhstack.com/schema/custom http://lhstack.com/schema/custom.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <custom:custom-bean name="test" class="com.lhstack.custom.PropertiesBean" >
        <custom:properties>
            <custom:entry key="name" value="app" />
            <custom:entry key="value" value="appTest" />
        </custom:properties>
    </custom:custom-bean>
</beans>

在这里插入图片描述

自定义属性

1.修改custom.xsd,在最后schema标签上面添加一行属性定义
在这里插入图片描述
2.创建属性解析器
在这里插入图片描述

package com.lhstack.custom.parse;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Node;

public class CustomAttributeBeanDefinitionDecorator implements BeanDefinitionDecorator {

	/**
	* 这里创建一个代理工厂,方便打印添加的description属性
	*/
    private static class CustomFactory implements FactoryBean {

        private Class  clazz;

        private String description;

        public CustomFactory(String clazz,String description) throws ClassNotFoundException {
            this.clazz = Class.forName(clazz);
            this.description = description;
        }

        @Override
        public Object getObject() throws Exception {
        	//使用cglib增强
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(this.clazz);
            enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
                System.out.println("bean attribute is [" + description + "]");
                return proxy.invokeSuper(obj, args);
            });
            return enhancer.create();
        }

        @Override
        public Class<?> getObjectType() {
            return clazz;
        }
    }

    @Override
    public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
        rootBeanDefinition.setBeanClass(CustomFactory.class);
        rootBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanDefinition().getBeanClassName());
        rootBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(node.getNodeValue());
        return new BeanDefinitionHolder(rootBeanDefinition,definition.getBeanName());
    }
}

3.注册属性解析器,解析刚刚custom.xsd里面定义的description属性
,

4.在spring.xml里面添加一个bean定义,并添加刚刚定义的属性
在这里插入图片描述
5.启动程序,自定义属性注入成功了
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值