经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
Spring @Profile注解使用和源码解析
来源:cnblogs  作者:刘牌  时间:2023/4/14 9:19:45  对本文有异议

介绍

在之前的文章中,写了一篇使用Spring @Profile实现开发环境,测试环境,生产环境的切换,之前的文章是使用SpringBoot项目搭建,实现了不同环境数据源的切换,在我们实际开发中,会分为dev,test,prod等环境,他们之间数独立的,今天进来详解介绍Spring @Profile的原理。

# Spring注解@Profile实现开发环境,测试环境,生产环境的切换

使用

带有@Profile的注解的bean的不会被注册进IOC容器,需要为其设置环境变量激活,才能注册进IOC容器,如下通过setActiveProfiles设置了dev值,那么这三个值所对应的Bean会被注册进IOC容器。当然,我们在实际使用中,不会这样去做,使用SpringBoot的话,我们一般是使用yml,在yml中配置spring.profiles.active,也可以通过配置jvm参数。

通过Environment设置profile

我们可以直接通过Environment来设置环境属性,这是比较原生的方法。

  1. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  2. context.getEnvironment().setActiveProfiles("dev");

通过JVM参数设置

可以通过JVM参数来设置环境变量的值,在开发中,这种方式也是使用得比较普遍。

SpringBoot通过yml进行配置

在SpringBoot项目中,我们得配置项一般都是配置在yml文件中,这样就能和代码分开,并且也能进行动态配置。

从上面我们看出可以通过好几种方式进行配置,但是他们最终其实都是将环境变量设置进Environment中,这样,spring在后续得流程里面,就能从Environment中获取环境变量,然后进行相应的逻辑处理。

源码解析

BeanDefinition注册

首先,需要注册bean的元信息BeanDefinition,不过对于@Profile标注的方法,如果环境变量中有对应的变量值,那么就能注册,没有的话则不会进行注册,我们来看关键的代码,在ConfigurationClassBeanDefinitionReader中,有一个shouldSkip判断,它会筛选出符合的bean,不符合条件的bean则被加入skippedBeanMethods集合中,不会被注册。

  1. private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
  2. ConfigurationClass configClass = beanMethod.getConfigurationClass();
  3. MethodMetadata metadata = beanMethod.getMetadata();
  4. String methodName = metadata.getMethodName();
  5. // Do we need to mark the bean as skipped by its condition?
  6. if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
  7. configClass.skippedBeanMethods.add(methodName);
  8. return;
  9. }
  10. if (configClass.skippedBeanMethods.contains(methodName)) {
  11. return;
  12. }
  13. }

shouldSkip源码

在shouldSkip中,会使用Condition接口,@Profile使用的是ProfileCondition,然后调用matches方法。

  1. public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationCondition.ConfigurationPhase phase) {
  2. for (Condition condition : conditions) {
  3. ConfigurationCondition.ConfigurationPhase requiredPhase = null;
  4. if (condition instanceof ConfigurationCondition configurationCondition) {
  5. requiredPhase = configurationCondition.getConfigurationPhase();
  6. }
  7. if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
  8. return true;
  9. }
  10. }
  11. return false;
  12. }

ProfileCondition匹配

在ProfileCondition的matches方法中,主要就是去Environment中寻找环境变量,然后解析@Profile注解设置的value值,如果Environment中激活的配置中包含当前的配置,包含则能为true,不包含则为false,如上通过setActiveProfiles设置Environment中激活的配置为dev,当前传过来的配置为dev,那么就能匹配上,就能装配进IOC容器。

  1. public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  2. MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
  3. if (attrs != null) {
  4. for (Object value : attrs.get("value")) {
  5. if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
  6. return true;
  7. }
  8. }
  9. return false;
  10. }
  11. return true;
  12. }

从源码可以看出,其最核心的思想就是是否注册bean的元信息BeanDefinition,因为只有注册了BeanDefinition,后续才能为创建bean提供元数据支持,判断是否注册bean元信息,主要就是从Environment中取出profiles的值,然后和@Profile注解设置的值进行匹配,匹配得上就注册,bean不上就不注册。

总结

上面我们对@Profile的使用做了详细的介绍,并对它的核心源码进行解剖,无非就是判断是否要注册BeanDefinition,如果我们需要做一些环境隔离的工作,使用@Profile还是比较不错的。

今天的分享就到这里,感谢你的观看,下期见!

原文链接:https://www.cnblogs.com/steakliu/p/17315703.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号