dubbo注入找不到bean_想要理解 Spring IoC,那你知道如何扩展 Spring 自定义 Bean吗?...

本文介绍了如何扩展Spring自定义Bean,以提高易用性和封装性。通过实现Hacker类、编写xsd描述文件、NamespaceHandler和BeanDefinitionParser,以及在Spring配置文件中使用,展示了自定义Bean的完整过程。该知识有助于理解Spring核心的IoC原理,并可应用于如Dubbo框架中自定义bean的配置。
摘要由CSDN通过智能技术生成
ef66154b-362f-eb11-8da9-e4434bdf6706.png

Spring 最核心的部分就是控制反转,而要被控制的对象就是各种各样的 Bean。

虽然现在大部分团队都直接用 Spring Boot 了,很少有人用 Spring MVC 了,但是基础还是 Spring,只不过更多的是把 XML 配置改成了注解形式。

如果你用过 XML 配置的形式,那你知道 、 、 这些标签配置是怎么实现的吗,了解了这些,相信对你进一步认识 Spring 会有很大帮助。

来吧,开始了!

Spring mvc 提供了扩展 xml 的机制,用来编写自定义的 xml bean ,例如 dubbo 框架,就利用这个机制实现了好多的 dubbo bean,比如 、 等等,只要安装这个标准的扩展方式实现配置即可。

扩展自定义 bean 的意义何在

假设我们要使用一个开源框架或者一套 API,我们肯定希望以下两点:

1. 易用性,即配置简单,要配置的地方越少越好
2. 封装性,调用简单,也就是越高层封装越好,少暴露底层实现

基于以上两点,假设我们要实现一个自定义功能,用现有的 Spring 配置项也可以实现,但可能要配置的内容较多,而且还有可能要加入代码辅助。导致逻辑分散,不便于维护。

所以我们用扩展 Spring 配置的方式,将一些自定义的复杂功能封装,实现配置最小化。

实现自定义扩展的步骤

本例只做简单示范,功能简单,即实现一个可配置参数的 Hacker bean,然后提供一个toString() 方法,输入参数信息。 我们最终实现的 bean 配置如下:

用 Spring 自带的配置做个比较,例如:

1、实现自定义 bean 类,命名为 Hacker ,并在方法中重载toString()方法,输入属性名称,代码如下:
package kite.lab.spring.config;/** * Hacker * @author fengzheng */public class Hacker {    private String name;    private String age;    private String language;    private boolean isHide;    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;    }    public String getLanguage() {        return language;    }    public void setLanguage(String language) {        this.language = language;    }    public boolean isHide() {        return isHide;    }    public void setHide(boolean hide) {        isHide = hide;    }    @Override    public String toString() {        StringBuilder builder = new StringBuilder();        builder.append("======================");        builder.append(String.format("hacker's name is :%s ", this.getName()));        builder.append(String.format("hacker's age is :%s ", this.getAge()));        builder.append(String.format("hacker's language is :%s ", this.getLanguage()));        builder.append(String.format("hacker's status is :%s ", this.isHide()));        builder.append("======================");        return builder.toString();    }}
2、编写 xsd schema 属性描述文件,命名为 hacker.xsd ,这里把它放到项目 resources 目录下的 META-INF 目录中(位置可以自己决定),可以理解为:这个文件就是对应刚刚创建的实体类作一个 xml 结构描述,内容如下:
<?xml version="1.0" encoding="UTF-8"?>

注意上面的

xmlns="http://code.fengzheng.com/schema/kite

targetNamespace="http://code.fengzheng.com/schema/kite"

一会儿有地方要用到。

3、实现 NamespaceHandler 类,代码如下:
package kite.lab.spring.config;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.xml.NamespaceHandlerSupport;/** * HackNamespaceHandler * @author fengzheng */public class HackNamespaceHandler extends NamespaceHandlerSupport {    private static final Logger logger = LoggerFactory.getLogger(HackNamespaceHandler.class);    @Override    public void init() {        logger.info("执行 HackNamespaceHandler 的 init 方法");        registerBeanDefinitionParser("hacker",new HackBeanDefinitionParser(Hacker.class));        logger.info("注册 「hacker」 定义转换器成功");    }}

此类功能非常简单,就是继承 NamespaceHandlerSupport 类,并重载 init 方法,调用 registerBeanDefinitionParser 方法,其中第一个参数 hacker 即是我们之后在 spring 配置文件中要使用的名称,即kite:hacker 这里的hacker; 第二个参数是下一步要说的。

4、实现 BeanDefinitionParser 类,这个类的作用简单来说就是将第一步实现的类和 Spring xml中声明的 bean 做关联,实现属性的注入,来看代码:
package kite.lab.spring.config;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.MutablePropertyValues;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.beans.factory.support.RootBeanDefinition;import org.springframework.beans.factory.xml.BeanDefinitionParser;import org.springframework.beans.factory.xml.ParserContext;import org.w3c.dom.Element;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Parameter;/** * HackBeanDefinitionParser  * * @author fengzheng */public class HackBeanDefinitionParser implements BeanDefinitionParser {    private static final Logger logger = LoggerFactory.getLogger(HackBeanDefinitionParser.class);    private final Class> beanClass;    public HackBeanDefinitionParser(Class> beanClass) {        this.beanClass = beanClass;    }    @Override    public BeanDefinition parse(Element element, ParserContext parserContext) {        logger.info("进入 HckBeanDefinitionParser 的 parse 方法");        try {            String id = element.getAttribute("id");            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();            rootBeanDefinition.setBeanClass(beanClass);            rootBeanDefinition.setLazyInit(false);            //必须注册才可以实现注入            parserContext.getRegistry().registerBeanDefinition(id, rootBeanDefinition);            String name = element.getAttribute("name");            String age = element.getAttribute("age");            String language = element.getAttribute("language");            String isHide = element.getAttribute("isHide");            MutablePropertyValues pvs = rootBeanDefinition.getPropertyValues();            pvs.add("name", name);            pvs.add("age", Integer.valueOf(age));            pvs.add("language", language);            pvs.add("hide", isHide.equals(null) ? false : Boolean.valueOf(isHide));            return rootBeanDefinition;        } catch (Exception e) {            e.printStackTrace();        }        return null;    }}

此类实现自 BeanDefinitionParser,并且重载 parse 方法,parse 方法有两个参数,第一个Element可以理解为 Spring xml 配置的 bean 的实体对应,通过 element.getAttribute 方法可以获取 配置的参数值,第二个参数 ParserContext ,可以理解为 Spring 提供的接口对象,通过它实现注册 bean 的注入。 通过 RootBeanDefinition 实体对象的 getPropertyValues 方法可获取自定义bean的属性 kv 集合,然后像其中添加属性值。 注意:kv 集合中的 key 并不是实体类中的属性名称,而是属性对应的 setter 方法的参数名称,例如布尔型参数如果命名为 is 开头的,使用编辑器自动生成 setter 方法时,对应的 setter 方法的参数就会去掉 is ,并把后面的字符串做驼峰命名规则处理。当然了如果要规避的话,可以自己写 setter 方法。

5、注册 handler 和 xsd schema Spring 规定了两个 xml 注册文件,并且规定这两个文件必须项目资源目录下的 META-INF 目录中,并且文件名称和格式要固定。

spring.handlers 用于注册第三步实现的 Handler 类

内容如下:

http://code.fengzheng.com/schema/kite=kite.lab.spring.config.HackNamespaceHandler

这是一个键值对形式,等号前面为命名空间,第一步已经提到,这里就用到了,等号后面是 Handler 类的完全类名称。注意冒号前面加转义符

spring.schemas 用于注册第二步中的 xsd 文件

内容如下:

http://code.fengzheng.com/schema/kite/kite.xsd=META-INF/hacker.xsd

等号前面是声明的 xsd 路径,后面是实际的 xsd 路径。

6、 在 Spring 配置文件中使用
<?xml version="1.0" encoding="UTF-8"?>

注意前面引入了命名空间

xmlns:kite="http://code.fengzheng.com/schema/kite"

后面指定了 xsd 文件位置

http://code.fengzheng.com/schema/kite http://code.fengzheng.com/schema/kite/kite.xsd
7、测试

直接获取配置文件的方式测试

public static void main(String[] args){        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");         Hacker hacker = (Hacker) ac.getBean("hacker");         System.out.println(hacker.toString());    }

使用 SpringJUnit4ClassRunner 测试

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = { "classpath:application.xml" })public class HackTest {    @Resource(name = "hacker")    private Hacker hacker;    @Test    public void propertyTest() {        System.out.println(hacker.toString());    }}

测试结果如图:

f166154b-362f-eb11-8da9-e4434bdf6706.png

感谢观看,喜欢的话可以点个关注谢谢

f266154b-362f-eb11-8da9-e4434bdf6706.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值