spring开放式扩展的理解

来梳理自己对spring扩展的简单理解.
从大的方面来看,我们可以扩展spring来让我们自己开发的框架融入她的体系中.小的方面,我们可以利用她开放的各种功能接口来自定义一些功能替代spring的默认值.我就从这俩方面来举几个例子来讲下spring扩展的特点.
那现在比如我们自己捣鼓了一个框架,想把她给用起来.然后发现,呀.我们很多系统都是基于spring做的,那么我们自己搞的框架兼容到spring里面啊.放不到spring里面,那我们不是白做了这么牛鼻哄哄的框架了嘛.
这里spring的那些专家或许也考虑过,我们开发的spring这么牛鼻,怎么让人家把自家的框架都兼容咱家的spring啊.哈哈,于是我们就看见现在的mybatis-spring,spring-data-redis等,她们都是通过spring自定义schemas实现的,也就是说只要你按照spring要求的做,保证你在spring的怀里舒舒服服的.
那我们怎么做了.首先,我们知道spring本质是一个bean容器.而bean就是spring定义的一个实体类BeanDefinition.所以,我们要做的就是把我们自定义框架的东西转化为spring里面的一个或多个bean.那想想我们平常是怎么做的?我们是不是要在xml里面各种的写出来,那同理,现在我们要做的也就是做一个xml让spring来把里面的内容解析成bean.
spring需要我们自定义三个文件,一个Spring.handlers,一个Spring.schemas.
,还有一个example.xsd(这个名字随意).我们看看这都写了啥.

http\://www.aaa.com/schema/example=com.ccc.MyNamespaceHandler
http\://www.aaa.com/schema/example.xsd=META-INF/example.xsd
<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.aaa.com/schema/example"
    xmlns:tns="http://www.aaa.com/schema/example" elementFormDefault="qualified">

    <xsd:import namespace="http://www.springframework.org/schema/beans" />


    <xsd:element name="user">
        <xsd:complexType>
            <xsd:attribute name="name" type="string"/>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

我们自己先看一下,这三文件有啥关系,Spring.handlers好像定义了一个叫MyNamespaceHandler类的路径.Spring.schemas定义了example.xsd的路径.而example.xsd是啥了?她就是我们要拜托spring去解析的xml,大意我想都能看出去定义一个叫user的标签,她有个属性叫name.不过我们还看到xml上面有个

  targetNamespace="http://www.aaa.com/schema/example"
    xmlns:tns="http://www.aaa.com/schema/example"

这个也是前面俩文件等号左边的东西,是不是像我们每次在使用springxml的时候都要复制粘贴的一大段头部代码.

<beans 
xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:aop="http://www.springframework.org/schema/aop" 
xsi:schemaLocation=" 
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-3.0.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
">

其实和我们的意图一样,也是告诉spring所需要的bean都在哪些xml里面.哈哈,是不是很有趣,我们也加了个www上去,nice.
好了,然后我们去看看自定义的MyNamespaceHandler

public class MyNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        this.registerBeanDefinitionParser("user", new AbstractSimpleBeanDefinitionParser (){
     @Override
    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        String name = element.getAttribute("name");
        BeanDefinitionRegistry registry = parserContext.getRegistry();
        User user = new User();
        user.setName(name);
        GenericBeanDefinition def = new GenericBeanDefinition();
        def.setBeanClass(user.getClass());
        def.setLazyInit(false);
        parserContext.getRegistry().registerBeanDefinition("user", def);
    }
        });
    }

}

其实也就是通过继承NamespaceHandlerSupport,然后注册了个bean的解析器,解析器会去解析xml,我们通过定义了一个GenericBeanDefinition成功将user融入spring里面.然后user这个bean就可以被我们平常那样随便使用了.
这就是成功创建一个bean标签的过程,以后我们就可以使用这样来定义个bean了.通过这样我们就可以将牛鼻哄哄的自家框架兼容到spring了,非常nice.
小的方面我们来说下spring里的通知机制.
比如用户注册功能,注册函数做完了需要像用户发一封欢迎邮件.如果我们直接在注册函数里面写,一方面不符合解耦合原则,另一方面可能发送功能报错了,导致注册失败就更糟了.所以,我们一般就会考虑用异步做,比如消息队列.但是其实spring自带了一种通知机制,非常的方便易用.通过applicationevent实现,我们来看看如何使用.
我们先定义一个listener用于监听MessageEvent事件的发布.

public class MessageEvent extends ApplicationEvent {
    public MessageEvent(Object msg) {
        super(msg);
    }
}

public MessageEventListener implements ApplicationListener<ApplicationEvent> {

    @Override
    public void onApplicationEvent(final ApplicationEvent event) {
        if (event instanceof MessageEvent) {
            MessageEvent msgEvent = (MessageEvent) event;
            doSomething(msgEvent.getSource());
        }
    }
}

代码一目了然,获取到MessageEvent事件,然后处理事件中包含的消息.然后我们在具体的代码里面怎么做了.

@Autowired
private ApplicationEventPublisher eventPublisher;

public void register() {
    doRegister();
    this.eventPublisher.publishEvent(new MessageEvent(msg));
}

ApplicationEventPublisher是spring自身用于发布事件的bean,我们通过调用publishEvent来发布事件,这样就实现了很好的解耦.
那我们来看看,spring是如何让我们实现解耦的.
publishEvent是在AbstractApplicationContext里面实现的,里面关键代码是这样的

getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);

这句代码的意思获取事件广播器然后广播我们的事件,那么去看看事件广播器做了啥,我们进入到SimpleApplicationEventMulticaster里面看multicastEvent方法做了啥.

ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        invokeListener(listener, event);
                    }
                });
            }
            else {
                invokeListener(listener, event);
            }
        }

这里获取所有的监听器,然后判断是否定义了异步线程池,如果有,则放入线程池,这里推荐使用线程池异步调用,可以防止意外错误的发生影响主业务的进行.之后通过invokeListener一步步到最后的调用代码是

listener.onApplicationEvent(event);

看到没,这就是我们自定义事件监听器所重写的代码,完成我们定义的事件调用.
spring事件广播机制讲完了,我们总结下这里的扩展.首先,spring定义了一个总的ApplicationEventMulticaster然后自己做了一个简化版的实现,在我们发布事件的时候,通过筛选我们监听器,来回调onApplicationEvent方法.可以看到,spring在每一步都做了开放式设计,包括监听器,线程池,接口设计,来保证用户最大的可定制化.
好了,通过spring里面常用的俩个功能来说了下spring的扩展机制.总的来说学习一个框架或者一种方法的时候,我觉得如何设计才是最重要的.如上面我们所讲的,spring几乎在接口和抽象上面做的非常的好,我们既可以把spring看作一个空的架子,因为她给了我们足够开放的接口.又可以把spring当作一个开箱即用的框架,因为她提供了我们很多既定的默认实现.所以,当我学习spring的时候,我并没有去纠结spring的每一句代码,太多了,没有必要.我们需要知道的是她做了什么功能,大致的结构是什么.观其大略.只在我们对某个细节很困惑的时候才去深究一下,这也是我目前学习的一种方式.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值