在 spring 4 后才引入了 @conditional 等条件注解,它是 spring boot 中实现自动配置的最大功臣!
那么问题来了:如果我们还在使用 spring 3.x 的老版本,这时候要怎么实现一个自动配置呢?
需求和问题
核心的诉求
比如说:
面临的问题
因为没有条件注解,所以我们不清楚在什么时候 需要/不需要 配置这些东西
此时我们没有办法像 spring boot 的自动配置那样让框架自动加载我们的配置,我们要使用一些别的手段让 spring 可以加载到我们定制的这些功能。
核心解决思路
条件判断
spring 为我们提供了一个扩展点,我们可以通过 beanfactorypostprocessor 来解决条件判断的问题,它可以让我们在 beanfactory 定义完之后、bean 的初始化之前对我们这些 bean 的定义做一些后置的处理。可以在这个时候对我们的 bean 定义做判断,看看当前 存在/缺少 哪些 bean 的定义,还可以增加一些 bean 的定义 —— 加入一些自己定制的 bean。
配置加载
可以考虑编写自己的 java config 类,并把它加到 component-scan 里面,然后想办法让现在系统的 component-scan 包含我们编写的 java config 类;也可以编写 xml 文件,如果当前系统使用 xml 的方式,那么它加载的路径上是否可以加载我们的 xml 文件,如果不行就可以使用手动 import 这个文件。
spring 提供的两个扩展点
beanpostprocessor
beanfactorypostprocessor
关于 bean 的一些定制
既然上面提到了 spring 的两个扩展点,这里就延展一下关于 bean 的一些定制的方式。
lifecycle callback
initializingbean / @postconstruct / init-method
这部分是关于初始化的,可以在 bean 的初始化之后做一些定制,这里有三种方式:
这些都可以让我们这个 bean 在创建之后去调用特定的方法。
disposablebean / @predestroy / destroy-method
这部分是在 bean 回收的时候,我们该做的一些操作。可以指定这个 bean 在销毁的时候,如果:
xxxaware 接口
可以把整个 applicationcontext 通过接口进行注入,在这个 bean 里我们就可以获得一个完整的 applicationcontext。
与 applicationcontextaware 类似。
可以把 bean 的名字注入到这个实例中来。
如果对源码感兴趣,可见:org.springframework.beans.factory.support.abstractbeanfactory.dogetbean\
如果当前 bean 存在 close 或 shutdown 方法名的方法时,会被 spring 视为 destroy-method,在销毁时会进行调用。
一些常用操作
判断类是否存在
调用 spring 提供的 classuitls.ispresent() 来判断一个类是否存在当前 class path 下。
判断 bean 是否已定义
注册 bean 定义
撸起袖子加油干
理论就科普完了,下面就开始实践。
在当前的例子中,我们假定一下当前环境为:没有使用 spring boot 以及高版本的 spring。
step 1:模拟低版本的 spring 环境
这里只是简单地引入了 spring-context 依赖,并没有真正的使用 spring 3.x 的版本,但也没有使用 spring 4 以上的一些特性。
<dependencies> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-context</artifactid> </dependency> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> </dependency> <dependency> <groupid>io.github.y0ngb1n.samples</groupid> <artifactid>custom-starter-core</artifactid> <scope>provided</scope> </dependency> </dependencies>
step 2:以实现 beanfactorypostprocessor 接口为例
@slf4j public class greetingbeanfactorypostprocessor implements beanfactorypostprocessor { @override public void postprocessbeanfactory(configurablelistablebeanfactory beanfactory) throws beansexception { // 判断当前 class path 下是否存在所需要的 greetingapplicationrunner 这么一个类 boolean hasclass = classutils .ispresent("io.github.y0ngb1n.samples.greeting.greetingapplicationrunner", greetingbeanfactorypostprocessor.class.getclassloader()); if (!hasclass) { // 类不存在 log.info("greetingapplicationrunner is not present in classpath."); return; } // 是否存在 id 为 greetingapplicationrunner 的 bean 定义 boolean hasdefinition = beanfactory.containsbeandefinition("greetingapplicationrunner"); if (hasdefinition) { // 当前上下文已存在 greetingapplicationrunner log.info("we already have a greetingapplicationrunner bean registered."); return; } register(beanfactory); } private void register(configurablelistablebeanfactory beanfactory) { if (beanfactory instanceof beandefinitionregistry) { genericbeandefinition beandefinition = new genericbeandefinition(); beandefinition.setbeanclass(greetingapplicationrunner.class); ((beandefinitionregistry) beanfactory) .registerbeandefinition("greetingapplicationrunner", beandefinition); } else { beanfactory.registersingleton("greetingapplicationrunner", new greetingapplicationrunner()); } } }
注册我们的 bean(见 customstarterautoconfiguration),如下有几点是需要注意的:
@configuration public class customstarterautoconfiguration { @bean public static greetingbeanfactorypostprocessor greetingbeanfactorypostprocessor() { return new greetingbeanfactorypostprocessor(); } }
step 3:验证该自动配置是否生效
在其他项目中添加依赖:
<dependencies> ... <dependency> <groupid>io.github.y0ngb1n.samples</groupid> <artifactid>custom-starter-spring-lt4-autoconfigure</artifactid> </dependency> <dependency> <groupid>io.github.y0ngb1n.samples</groupid> <artifactid>custom-starter-core</artifactid> </dependency> ... </dependencies>
启动项目并观察日志(见 custom-starter-examples),验证自动配置是否生效了:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: spring boot :: (v2.1.0.release) 2019-05-02 20:47:27.692 info 11460 --- [ main] i.g.y.s.d.autoconfiguredemoapplication : starting autoconfiguredemoapplication on hp with pid 11460 ... 2019-05-02 20:47:27.704 info 11460 --- [ main] i.g.y.s.d.autoconfiguredemoapplication : no active profile set, falling back to default profiles: default 2019-05-02 20:47:29.558 info 11460 --- [ main] i.g.y.s.g.greetingapplicationrunner : initializing greetingapplicationrunner. 2019-05-02 20:47:29.577 info 11460 --- [ main] i.g.y.s.d.autoconfiguredemoapplication : started autoconfiguredemoapplication in 3.951 seconds (jvm running for 14.351) 2019-05-02 20:47:29.578 info 11460 --- [ main] i.g.y.s.g.greetingapplicationrunner : hello everyone! we all like spring!
到这里,已成功在低版本的 spring 中实现了类似自动配置的功能。clap
代码托管于 github,欢迎 star
参考链接
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。
如对本文有疑问, 点击进行留言回复!!
before社区电量是什么意思 Before社区电量获得方法
RecycleView入门详解(教你全面掌握RecycleView用法)
动态权限请求框架RxPermissions(几行代码搞定权限)
URL路径@PathVariable出现点号“.“时值遭截断问题
网友评论