聊聊spring的那些扩展机制
1.背景
慎⼊:本⽂将会有⼤量代码出⼊。
在看⼀些框架源码的时候,可以看见他们很多都会和Spring去做结合。举个例⼦dubbo的配置:
很多⼈其实配置了也就配置了,没有去过多的思考:为什么这么配置spring就能识别,dubbo就能启动?
如果你也需要做⼀个框架和Spring结合,或者你想知道Spring其他框架是如何和Spring做结合的,那么你应该了解⼀下Spring的扩展机制。
2.如何扩展
本篇⽂章想从Spring的两个流程去介绍如何扩展,⼀个是容器初始化流程,⼀个是Bean的创建流程。
2.1 容器的初始化
要想使⽤Spring,第⼀步肯定是需要先让容器初始化。在AbstractApplicationContext中有⼀个refresh⽅法定义了容器如何进⾏刷新:
在refresh中的具体流程如下图:
其中⽐较常见的扩展在加载BeanDefinition中和执⾏BeanPostProcessor。下⾯讲述⼀下如何进⾏这两个的扩展。
2.1.1 加载BeanDefinition
在介绍加载BeanDefinition之前,先让我们了解⼀下什么是BeanDefinition,顾名思义BeanDefinition描述Bean的信息的,⽐如他的class信息,属性信息,是否是单例,是否延迟加载等。
如何加载呢?⼀般有两种⼿段,⼀个是通过我们的xml,⼀个是通过⼀些扩展⼿段。
xml加载如下:
我们在spring的XML中配置这样⼀个bean的定义,他会进⾏解析然后转换成我们的BeanDefinition。
还有种⽅式是通过XML schema扩展的⽅式,关于xsd的⼀些详细介绍可以参考这篇⽂章: 。有些同学会问不是还有个注解的⽅式吗?我们在学的时候⼀般书上都写XML和注解两种⽅式,注解其实也是使⽤了XML schema的扩展机制,等会我会细讲。
2.1.1.1 XML schema扩展
什么是XML schema的扩展呢?
Spring允许你⾃⼰定义XML的的结构并且可以⽤⾃⼰的bean解析器进⾏解析。这⾥参考⼀下进⾏⾃定义扩展的4个步骤:
编写⼀个 XML schema ⽂件描述的你节点元素。在resources/META-INF/⽬录下定义demo.xsd⽂件。这⾥定义了⼀个demo的节点元素,其中定义了⼀个name字段。
编写⼀个 NamespaceHandler 的实现类
编写⼀个或者多个 BeanDefinitionParser 的实现 (关键步骤).
注册上述的 schema 和 handler。在resources/META-INF/ ⽬录下⾯创建spring.handler⽂件输⼊:
http\://www.demo/schema/demo = xsd.DemoNameSpaceHandler
复制代码
,这⼀步将我们之前的标签的url映射到我们NamespaceHandler。再创建⼀个spring.schemas⽂件,输⼊:
http\:///schema/demo/demo.xsd= META-INF/demo.xsd
复制代码
这⼀步将xsd的url进⾏了映射。
回到注解,⼤家配置注解的时候⼀般都是使⽤下图进⾏配置:
但是可以看见其依然是使⽤XML schema扩展进⾏处理,在Spring中有个叫ContextNamespaceHandler,注册很多解析器:  其中有⼀个解析器是compnent-scan,在他的parse⽅法中定义了如何进⾏注解扫描,获取注解:
利⽤这个扩展机制的还有AOP,MVC,Spring-Cache以及我们的⼀些开源框架⽐如Dubbo等。
2.1.1.2 BeanFactoryPostProcessor扩展实例化bean的三种方式
这个机制可以让我们在真正的实例化Bean之前对BeanDefinition进⾏修改。
这⾥我举例⼀个实战的例⼦,想必⼤家很多都配置过数据库连接池吧,这⾥拿Druid来举例:
然后我们创建⼀个druid.properties输⼊:
url=jdbc:mysql://localhost:3306/test
username=root
password=123456
复制代码
对于这种配置⾃⼰玩玩已经满⾜,但是在公司有个问题,密码放在项⽬中明码存储,这样是不⾏的,别⼈只要获得了你项⽬的查看权限那么密码就会被泄漏,所以⼀般的公司会有⼀个统⼀的密码存储服务,只有⾜够的权限才能够使⽤,那么我们可以把密码放在统⼀存储服务中,通过对服务的调⽤才能进⾏密码的使⽤,那么我们怎么把从远程服务中获取到的密码注⼊到我们Bean中呢?那么就要使⽤我们的BeanFactoryPostpRrocessor,下⾯的代码继承PropertyPlaceholderConfigurer(BeanFactoryPostpRrocessor的实现类):
在XML中有:
通过这种⽅式我们可以有⼏个好处:
设置统⼀配置中⼼,那么我们不需要修改我们项⽬中的⽂件,只需要在配置中⼼页⾯中修改即可。
设置统⼀密码中⼼,那么我们不需要暴露明⽂在项⽬中,密码如何保护那么就直接丢给密码中⼼即可。
2.2 Bean的创建
⼀般我们在API中获取⼀个Bean都会如下操作:
通过GetBean操作进⾏获取,前⾯我们讲到过如果是⾮延迟加载的单例Bean那么会在容器刷新的时候进⾏加载,如果是延迟加载的Bean那么会在我们获取Bean的时候根据BeanDefinition进⾏加载。⾸先在AbstractBeanFactory有两个⽅法⼀个是doCreate,⼀个是create⽤来描述如何创建⼀个Bean。这⾥说⼀下单例Bean是如何创建的:
doCreateBean操作流程如下图:
可以看见真正的创建bean的操作在CreateBean中,对于真正的创建Bean有如下流程:
2.2.1 Aware接⼝
Spring提供了很多Aware接⼝⽤于进⾏扩展,通过Aware我们可以设置很多想设置的东西:
invokeAwareMethod提供了三种最基本的Aware,如果是ApplicationContext的话那么在ApplicationContextAwareProcessor⼜进⾏了⼀轮Aware注⼊。
BeanNameAware:如果Spring检测到当前对象实现了该接⼝,会将该对象实例的beanName设置到对钱对象实例中。
BeanClassLoaderAware:会将加载当前Bean的ClassLoader注⼊进去。
BeanFactoryAware:将当前BeanFactory容器注⼊进去。
如果使⽤ApplicaitonContext类型的容器的话⼜会有下⾯⼏种:
EnvironmentAware:将上下⽂中Enviroment注⼊进去,⼀般获取配置属性时可以使⽤。
EmbeddedValueResolverAware:将上下⽂中EmbeddedValueResolver注⼊进去,⼀般⽤于参数解析。 ResourceLoaderAware:将上下⽂设置进去。
ApplicationEventPublisherAware:在ApplicationContext中实现了ApplicationEventPublisher接⼝,所以可以将⾃⼰注⼊进去。
MessageSourceAware:将⾃⾝注⼊。
ApplicationContextAware:这个是我们见的⽐较多的,会将⾃⾝容器注⼊进去。
2.2.2 BeanPostProcessor
在前⾯我们说过BeanFactoryPostProcessor,这两个名字很像,BeanFactoryPostProcessor是⽤来对我们BeanFactory中的BeanDefinition进⾏处理,此时Bean还未⽣成。⽽BeanPostProcessor⽤来对我们⽣成的Bean进⾏处理。
在BeanPostProcessor分为两个⽅法,⼀个是⽤于初始化前置处理,⼀个是初始化⽤于后置处理。
有⼀种特殊的BeanPostProcessor,InstantiationAwareBeanPostProcessor,其会在我们实例化流程之前,如果实现了这个接⼝,那么就会使⽤其返回的对象实例,不会进⼊后续流程。
实战:BeanPostProcessor有什么⽤呢?
如果你有⼀个需求,打点项⽬中⽅法每个⽅法的运⾏时常,你很容易想到⽤AOP去做,如果不⽤AOP的话那么你可以使⽤
BeanPostProcessor的后置处理⽅法,将对应的每个Bean都进⾏动态代理。
2.2.3 InitializingBean/init-method
Spring提供了我们对Bean进⾏初始化逻辑的扩展:
实现InitalizingBean接⼝:  在afterPropertiesSet()⽅法中我们可以写⼊我们的初始化逻辑。
通过xml⽅式:
在init-method中定义了我们初始化⽅法。
2.2.4 DisposableBean/destory-method
俗话说,⽣与死轮回不⽌。那么我们有了⽣的扩展,⾃然Spring提供了死的扩展。我们也可以通过下⾯两个扩展来实现我们销毁的逻辑: DisposableBean: 实现DisposableBean接⼝
实现destroy⽅法即可。
实现XML:在destroy-method中定义销毁⽅法。
PS: 在我们Spring容器中如果要在JVM关闭时⾃动调⽤关闭的⽅法那么我们可以((ClassPathXmlApplicationContext)
applicationContext).registerShutdownHook();注册关闭钩⼦,这样在关闭JVM的时候我们的Bean也能
安全销毁。
3.总结
本篇⽂章从Spring容器启动原理,以及Bean的初始化原理介绍,引出了多个基本的扩展点。当然这部分扩展点还仅仅是Spring中的⼀部分,感兴趣的可以阅读Spring的⽂档,或者阅读Spring源码。如果能掌握这些扩展,以后⾃⼰造轮⼦的时候和Spring结合这些扩展是不能少的。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。