代码编织梦想

目录

@Profile注解 -【Spring底层原理】

一、注解用法

二、实例分析

三、源码追踪

四、总结


一、注解用法

在我们开发开发测试部署当中,有不同的环境,比如有:开发环境、测试环境、上产环境,不同的环境有不同的组件,这听着怎么那么像springboot中的多环境配置呢?今天,咱们来看看在spring中是如何实现的。

为了多环境开发,Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能,比如数据源组件的配置,不同开发环境连接的数据源可能会不同,就可以使用@Profile注解进行配置,根据环境动态切换数据源组件。

@Profile:指定组件在哪个环境下才能被注册到容器中,不指定则任何环境都能注册这个组件,加了环境标识的bean,只有这个环境被激活的时候才能注册到容器

二、实例分析

就以数据源配置为例,不同环境数据源的配置往往不同,如何使用@Profile注解在不同环境下进行数据源的注册呢,通过实例来进行分析。

【1】@Profile环境搭建

// 启动测试类
@Test
public void TestMain() {
    // 创建IOC容器
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
    for (String string : namesForType) {
        System.out.println(string);
    }
    // applicationContext.close();
}

// 配置类
@Configuration
public class AppConfig {

    // 测试环境
    // @Profile("test")
    @Bean("testDataSource")
    public DataSource dataSourceTest() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("806188");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/vhr");
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        return dataSource;
    }

    // 开发环境
    // @Profile("dev")
    @Bean("devDataSource")
    public DataSource dataSourceDev() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("806188");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/vhr");
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        return dataSource;
    }

    // 生产环境
    // @Profile("pro")
    @Bean("proDataSource")
    public DataSource dataSourcePro() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("806188");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/vhr");
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        return dataSource;
    }
}

可以看到,在配置类中,有不同的数据源配置,分别是测试环境、开发环境、生产环境,我们可以通过@Profile注解来指定注入哪种环境,当不指定则任何环境都能注册这个组件,也就是上面代码,运行测试类,输出结果如下,三个数据源组价都进行了注入:

image-20210316111408626

将数据源使用@Profile注解进行标识,也就是将上面代码三个@Profile注解打开,此时因为没有激活注册环境,所以这个三个数据源都不能被注入。下面来进行激活。

【2】激活注册环境

  • default默认环境:@Profile("default")
  • 使用命令行动态参数:-Dspring.profiles.active=dev
  • 使用代码手动激活指定环境:要使用无参构造方法
  • 配置在配置类上:只有在指定环境的时候,整个配置类的所有配置才能生效
  1. default默认环境,使用@Profile("default"),标识默认当前环境
// 测试环境
@Profile("default")
// @Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest() throws PropertyVetoException {
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setUser("root");
    dataSource.setPassword("806188");
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/vhr");
    dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
    return dataSource;
}

image-20210316114109583

  1. 使用命令行动态参数,编辑运行配置的VM options,参数为:-Dspring.profiles.active=dev,通过该配置来标注是何种环境

image-20210316114355679

运行启动类,可以看到devDataSource被注入:

image-20210316114612732

  1. 使用代码手动激活指定环境,使用这种方法不能让有参构造器代码执行,因为执行有参构造器加载配置类的时候,执行refresh()方法容器就启动刷新了,就将配置写死了,所以这里要用无参构造器,手动激活指定环境。修改启动类:
@Test
public void TestMain() {
    // 创建IOC容器
    // AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    // 设置需要激活的环境
    applicationContext.getEnvironment().setActiveProfiles("pro");
    // 注册主配置类
    applicationContext.register(AppConfig.class);
    // 启动刷新容器
    applicationContext.refresh();
    String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
    for (String string : namesForType) {
        System.out.println(string);
    }
}

修改后,手动设置需要激活的环境,运行启动类,输出结果如下:

image-20210316142824671

  1. 在配置类上加@Profile注解,则只有在指定环境的时候,整个配置类的所有配置才能生效,如下:
@Profile("test")
@Configuration
public class AppConfig {...}

运行启动类,此时启动类是手动代码配置了pro环境,因为配置类上是@Profile("test"),只有在test环境下该配置类才会生效,所以没有输出:

image-20210316144725462

三、源码追踪

查看@Profile注解源码,我们可以看到,@Profile实际上是一个@Conditional注解:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
    String[] value();
}

@Conditional注解在前面的文章讲解过,可以进行参考:@Conditional注解 -【Spring底层原理】,这里简单分析一下

@Conditinal是一个条件注解,参数是一个class,这个class都要实现Condition接口,重写matches()方法。例如,上面示例代码中的ProfileCondition.class

class ProfileCondition implements Condition {
    ProfileCondition() {
    }
	// Spring从ConditionContext中拿到激活的Profile和注解上的字符串进行比对,以决定是否实例化这个bean
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
        if (attrs != null) {
            Iterator var4 = ((List)attrs.get("value")).iterator();

            Object value;
            do {
                if (!var4.hasNext()) {
                    return false;
                }

                value = var4.next();
            } while(!context.getEnvironment().acceptsProfiles(Profiles.of((String[])((String[])value))));

            return true;
        } else {
            return true;
        }
    }
}
  • mathces()方法的返回值是一个布尔值,返回true时,spring就会创建这个bean,返回false时就不会创建
  • mathes()方法上有两个参数,分别是ConditionContextAnnotatedTypeMetadata,这连个参数中包含了大量的信息,ConditionContext中有EnvironmentClassLoader等信息,AnnotatedTypeMetadata可以获得注解的信息
  • Spring从ConditionContext中拿到激活的Profile和注解上的字符串进行比对,以决定是否实例化这个bean

四、总结

@Profile注解是用来指定组件在哪个环境下才能被注册到容器中的,只有这个环境被激活的时候才能注册到容器,激活总结为以下几种方式:

  • default默认环境:@Profile("default")
  • 使用命令行动态参数:-Dspring.profiles.active=dev
  • 使用代码手动激活指定环境:要使用无参构造方法
  • 配置在配置类上:只有在指定环境的时候,整个配置类的所有配置才能生效
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/One_L_Star/article/details/114915592

cruzer profile 原理分析-爱代码爱编程

(此文写于2007年,部分概念现在不适用) (原博客空间不靠谱,内容都被截断了,文章里...的部分基本找不回来了) 今日购得Cruzer Profile一枚,由网上得知该程序无法运行在linux和vista系统上,于是乎试图分析其原理。 相关文件如下: F:\Program Files\eXeScope\cruz

spring框架总结之profile使用-爱代码爱编程

Spring框架总结之Profile使用 【注意】原创来之不易,转载请标明地址:http://blog.csdn.net/why_2012_gogo/article/details/73730535 在实际开发中,有一个比较大的挑战就是将程序从一个环境迁移或切换到另一个环境。我们知道,测试或开发环境与正式或生产环境中的某些配置是不同的,如:数据库配置、

阅读源码理解springcloud的@loadbalanced的原理-爱代码爱编程

我们在使用RestTemplate类调用其他服务的时候,如果配置了 @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); }

spring boot profile加载原理_frank_bettterman的博客-爱代码爱编程_springboot profile原理

文章目录 Spring Boot的配置文件Spring Boot默认配置文件加载顺序以及优先级项目内部加载顺序项目外部配置文件加载顺序: 自定义指定配置文件路径Spring Boot 指定启动profile的方式

@profile注解详解-爱代码爱编程

@Profile注解详解  @Profile:           Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;      开发环境develop、测试环境test、生产环境master   数据源:(/dev) (/test) (/master)     @Profile:指定组件在哪个环境的情况下才能被注册到容器中,

springframework之@profile注解_choubayan4320的博客-爱代码爱编程

    SpringFrame的版本5.0.9.release。     我们会使用@Profile来分开开发环境和生产环境,Profile是如何实现的呢,如List-1,注意@Conditional的value是ProfileCondition     List-1 @Target({ElementType.TYPE, ElementT

Spring注解-Day3--自动注入-爱代码爱编程

Day 3 @Value @Value注解用于设置需要注入容器的Bean的值,使用方法有以下三种: 基本数值可以写SpEL,#{}可以通过${}取出配置文件中的值(在运行环境变量里面的值)要读取配置文件,需要在配置类上方添加@PropertySource(value = {“classpath:/person.properties”},encodin

Spring注解——@Profile详解-爱代码爱编程

目录 一、Spring中的Profile二、为什么要使用Profile三、配置Profile示例四、激活Profile的方式 一、Spring中的Profile Spring中的Profile功能可以理解为我们在Spring容器中所定义的Bean的逻辑组名称,只有当这些Profile被激活的时候,才会将Profile中所对应的Bean注册到S

《spring设计思想》31-Spring组合注解-爱代码爱编程

上一节介绍了spring注解中的范式注解以及@Component和@ComponentScan的简单实现原理。 这一节介绍一下spring的组合注解。 所谓组合注解跟java中的组合模式差不多,多个注解组合使用,以达到特殊的功能。 比如Spring-boot中的@SpringBootApplication注解,是个非常有代表性的组合注解: @Tar

8.3、Spring Profiles 功能及内部原理-爱代码爱编程

如何使用多个配置文件 在项目中,为了清晰起见,会将不用功能的配置项配置在不同的配置文件中,我们可以在主配置文件中配置: spring.profiles.include= online1,online2 这样 application-online1.properties 和application-online2.properties 就都会被 Spr

spring注解开发:@Bean、@ComponentScan、@Conditional、@Profile等-爱代码爱编程

一、组件注册 1.1-spring注解驱动开发 1.3-组件注册 Configuration、Bean、ComponentScan(s)、TypeFilter 以前添加组件的方式 现在添加的方式:使用配置类 项目目录 2. @ComponentScan() <!--包扫描、只要标注了@Controller、@Servic

Spring注解-自动装配-爱代码爱编程

自动装配; Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值; 自动注入 @Autowired:自动注入: 1)、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值 2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找

Spring注解-IOC-爱代码爱编程

Spring注解-IOC 组件注册 @Configuration&@Bean @Configuration:声明一个配置类 @Bean:向容器中注册bean,bean的类型为方法返回值类型,ID默认就是方法名。可以通过修改@Bean注解的value属性或者方法名来修改beanID //配置类=配置文件 @Configuration pub

spring系列-注解驱动原理及源码-自动装配-爱代码爱编程

目录 一、spring规范的bean自动注入 1、使用@Autowired自动注入 2、使用@Qualifier指定需要装配的组件 3、使用@Autowired装配null对象 4、使用@Primary装配首选bean 5、总结 6、自动装配原理 二、java规范的bean自动注入 1、@Resource自动装配 2、@Inject自动