介绍

在之前的文章中,写了一篇使用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来设置环境属性,这是比较原生的方法。

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

通过JVM参数设置

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

SpringBoot通过yml进行配置

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

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

源码解析

BeanDefinition注册

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

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
String methodName = metadata.getMethodName();
// Do we need to mark the bean as skipped by its condition?
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}
}

shouldSkip源码

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

    public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationCondition.ConfigurationPhase phase) {
for (Condition condition : conditions) {
ConfigurationCondition.ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition configurationCondition) {
requiredPhase = configurationCondition.getConfigurationPhase();
}
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
}
return false;
}

ProfileCondition匹配

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

    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
return true;
}
}
return false;
}
return true;
}

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

总结

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

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

Spring @Profile注解使用和源码解析的更多相关文章

  1. Spring源码解析02:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

  2. Spring源码解析 | 第二篇:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

  3. Dubbo原理和源码解析之标签解析

    一.Dubbo 配置方式 Dubbo 支持多种配置方式: XML 配置:基于 Spring 的 Schema 和 XML 扩展机制实现 属性配置:加载 classpath 根目录下的 dubbo.pr ...

  4. Dubbo原理和源码解析之服务引用

    一.框架设计 在官方<Dubbo 开发指南>框架设计部分,给出了引用服务时序图: 另外,在官方<Dubbo 用户指南>集群容错部分,给出了服务引用的各功能组件关系图: 本文将根 ...

  5. Dubbo原理和源码解析之“微内核+插件”机制

    github新增仓库 "dubbo-read"(点此查看),集合所有<Dubbo原理和源码解析>系列文章,后续将继续补充该系列,同时将针对Dubbo所做的功能扩展也进行 ...

  6. Dubbo原理和源码解析之服务暴露

    github新增仓库 "dubbo-read"(点此查看),集合所有<Dubbo原理和源码解析>系列文章,后续将继续补充该系列,同时将针对Dubbo所做的功能扩展也进行 ...

  7. Go语言备忘录:net/http包的使用模式和源码解析

    本文是晚辈对net/http包的一点浅显的理解,文中如有错误的地方请前辈们指出,以免误导! 转摘本文也请注明出处:Go语言备忘录:net/http包的使用模式和源码解析,多谢!  目录: 一.http ...

  8. Go语言备忘录(3):net/http包的使用模式和源码解析

    本文是晚辈对net/http包的一点浅显的理解,文中如有错误的地方请前辈们指出,以免误导! 转摘本文也请注明出处:Go语言备忘录(3):net/http包的使用模式和源码解析,多谢!  目录: 一.h ...

  9. rest-framework之视图和源码解析

    视图和源码解析 通过使用mixin类编写视图: from rest_framework import mixins from rest_framework import generics class ...

  10. Spring提取@Transactional事务注解的源码解析

    声明:本文是自己在学习spring注解事务处理源代码时所留下的笔记: 难免有错误,敬请读者谅解!!! 1.事务注解标签 <tx:annotation-driven /> 2.tx 命名空间 ...

随机推荐

  1. Linux让部署在服务器上的项目一直保持运行状态…&跑多个项目

    在idea通过package得到的.jar包或者.war包可通过 java -jar xxx.jar/xxx.war 命令直接在linux或者windows系统运行:   将打好包的项目放在linux ...

  2. jQuery实现论坛发帖Demo

    目录 效果展示 思路 代码 改进空间 效果展示 思路 主要知识点:运用jQuery对HTML元素节点的操作(append)来添加帖子. 交互设计:用户点击页面上的"论坛发帖"按钮, ...

  3. c语言数据结构 树的基本操作

    树的基本操作有创建,插入,删除,以及各种遍历的应用,如:利用后序遍历求高度,利用前序遍历求层数的结点 基本算法思路:创建二叉树函数参数必须接受二级指针!如果使用同级指针,无法返回创建后的结果,利用递归 ...

  4. 寄存器与RAM的区别

    转自:https://blog.csdn.net/qq_18191333/article/details/106912668 概述 寄存器是"存储设备",主要用于存储和检查微型计算 ...

  5. 在centos上使用nginx-rtmp实现hls(Http live Streaming)直播

    欢迎访问我的个人博客:xie-kang.com 原文地址 以下步骤建议在同一目录(如:~/Downloads)下执行(防止小白把目录弄混乱) 下载并解压nginx-rtmp(建议在~/Download ...

  6. 3---java中的集合

    集合是什么:表示一组元素的对象,有的是有序的,有的是无序的,有的是可重复的,有的是不可重复的. 首先根是:Collection 1:Set 没有重复元素  SortedSet 有序的Set 2:Lis ...

  7. 第二章启动引导器GRUB2

    第二章启动引导器GRUB2grub的配置文件路径:vim /boot/grub2/grub.cfg (不建议直接编辑)vim /etc/default/grub (可编辑的文件)将编辑的操作刷新到/b ...

  8. 数制、ip地址及子网

    一.数制 数制:计数的方法,指用一组固定的符号和统一的规则表示数值的方法 数位:指数字符号在一个数中所处的位置 基数:指在某种进制计数中,数位上所能使用的数字符号的个数 位权:指在某种进制计数中,数位 ...

  9. Nodejs 发送 TCP 消息的正确姿势

    最近使用 NODE-RED 跟 TCP 打交道.NODE-RED 里内建了一个节点叫"tcp-out",看文档呢使用这个节点可以很方便的把 payload 用 TCP 协议发送出去 ...

  10. python内置函数open()

    open()函数 介绍 open()函数用于打开文件并创建文件对象. open()函数的语法格式: file = open(filename, mode='r', buffering=-1, enco ...