时间:2021-05-19
在 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 接口为例
@Slf4jpublic 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),如下有几点是需要注意的:
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: default2019-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
参考链接
https://github.com/y0ngb1n/spring-boot-samples
https://github.com/digitalsonic/geektime-spring-family
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
在浩辰CAD软件中,浩辰CAD2019可以打开所有版本的dwg图纸,但是低版本的软件无法打开高版本的dwg图纸。所以建议大家保存低版本,那如何保存低版本呢?
word兼容模式的原因有: 1、适应低版本的需要。如果word文档升级了高版本,其中的一些功能是低版本没有的。如果使用低版本系统打开,就需要开启兼容模式。
以WORD2003为例,word有兼容模式的原因如下: 1、适应低版本的需要。如果word文档升级了高版本,其中的一些功能是低版本没有的。如果使用低版本系统打
本文实例讲述了js自定义trim函数实现删除两端空格功能。分享给大家供大家参考,具体如下:兼容IE低版本浏览器,以及其他一些低版本脚本的浏览器js中本身是没有t
AndroidNotification使用详解Notification核心代码(链式调用):适用于Android4.0以上(不兼容低版本)Notificatio