Spring入门实战之Profile详解

作者:daisy 时间:2021-10-03 19:08:34 

前言

Spring中的Profile功能其实早在Spring 3.1的版本就已经出来,它可以理解为我们在Spring容器中所定义的Bean的逻辑组名称,只有当这些Profile被激活的时候,才会将Profile中所对应的Bean注册到Spring容器中。

看到Profile这个关键字,或许你从来没有正眼瞧过他,又或者脑海中有些模糊的印象,比如除了这里Springmvc中的Profile,maven中也有Profile的标签。

从字面意思来看,Profile表示侧面,那什么情况下才会用到侧面这个功能呢,而侧面具体又有什么含义呢

打一个比方,对于数据库的配置问题,在开发的眼中可以使用嵌入的数据库,并且加载测试数据(后面会给出代码示例)。但是在测试的眼中,可能会配一个数据库连接池类似这样


@Bean(destroyMethod="close")
public DataSource dataSource () {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl("jdbc:h2:tcp://dbserver/~/test");
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUsername("sa");
dataSource.setPassword("password");
dataSource.setInitialSize(20);
dataSource.setMaxActive(30);
return dataSource;
}

当然还有产品环境下的配置等等。对于这种百花齐放的配置方式你还能说什么,默默的为这一套套的环境都部署相应的配置文件啊,没有profile这套我们一直都是这么做。

但是现在有了Profile,我们就多了一种选择,一种更加智能省心的配置方式。通过Profile配置,Spring可以在根据环境在运行阶段来决定bean的创建与否,先举例如下,主要从Profile bean的配置和激活来展开。

Profile bean的配置

通过注解@Profile配置

对于上面比方中的第一种情况,在开发环境中我们配置一个数据源可能是这样的


@Bean(destroyMethod = "shutdown")
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}

这里会使用EmbeddedDatabaseBuilder创建一个嵌入式数据库,模式定义在类文件下的schema.sql文件中

schema.sql


create table Things (
id identity,
name varchar(100)
);

这里定义了一张Things表包含了两个字段

除了模式文件,还需要通过test-data.sql加载测试数据

test-data.sql


insert into Things (name) values ('A')

对于这个@Bean完全不知道是放在开发的环境下创建还是产品的环境下。所以我们这里可以使用注解@Profile帮助我们为这个bean打上标识。

从Spring 3.1版本中就引入了bean profile的功能,可以让你将不同的bean定义到一个或者多个profile里,然后在部署应用时告知要激活那个profile,则相应的bean就会被创建。

比如这里


@Configuration
@Profile("dev")
public class DevelopmentProfileConfig {

@Bean(destroyMethod = "shutdown")
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
}

通过@Profile("dev")为EmbedderDataSource bean标记为dev环境下要创建的bean。

注意:1. @Profile被加载类级别上,如果dev profile没有被激活,那么类中对应的所有bean就不会被创建

         2. 如果当前是dev环境被激活了,那么对于没有使用@Profile的bean都会被创建,被标记为其他的profile如prod,则不会创建相应的bean

         3. 从3.2开始@Profile不仅仅可以加载类级别上,还可以加载方法上,具体代码如下


package com.myapp;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jndi.JndiObjectFactoryBean;

@Configuration
public class DataSourceConfig {

@Bean(destroyMethod = "shutdown")
@Profile("dev")
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}

@Bean
@Profile("prod")
public DataSource jndiDataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}

}

通过xml配置文件配置

除了简单的注解方式,我们哈可以通过在xml配置文件中声明的方式,具体配置如下

datasource-config.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<beans profile="dev">
<jdbc:embedded-database type="H2">
<jdbc:script location="classpath:schema.sql" />
<jdbc:script location="classpath:test-data.sql" />
</jdbc:embedded-database>
</beans>

<beans profile="prod">
<jee:jndi-lookup
lazy-init="true"
jndi-name="jdbc/myDatabase"
resource-ref="true"
proxy-interface="javax.sql.DataSource" />
</beans>
</beans>

这里分别声明了两种环境以及对应的profile。

profile激活

虽然我们已经配置好了profile,但是如何激活相应的环境呢。这里我们需要两个属性spring.profile.active以及spring.profile.default

如果spring.profile.active被赋值了,则spring.profile.default就不会起作用,如果spring.profie.active没有赋值,则使用默认的spring.profile.default设置的值。当然,如果两者都没有设置的话,则只会创建那些定义在相应的profile中的bean。

设置这两个属性的方式有很多:

作为DispactcherServlet的初始化参数

作为Web应用上下文参数

作为JNDI条目

作为环境变量

作为JVM的系统属性

在集成测试类上,使用@ActiveProfiles注解设置

比如我们在web.xml中可以声明代码如下


<?xml version="1.0" encoding="UTF-8"?>
<web -app version="2.5"
...>

//为上下文设置默认的profile
<context-param>
<param-name>spring.profile.default</param-name>
<param-value>dev</param-value>
</context-param>

...

<servlet>
...
//为Serlvet设置默认的profile
<init-param>
 <param-name>spring-profiles.default</param-name>
 <param-value>dev</param-value>
</init-prama>

...
<web-app>

这样就可以指定需要启动那种环境,并准备相应的bean。

另外对于测试,spring为什么提供了一个简单的注解可以使用@ActiveProfiles,它可以指定运行测试的时候应该要激活那个profile。比如这里的测试类DevDataSourceTest


package profiles;

import static org.junit.Assert.*;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.myapp.DataSourceConfig;

public class DataSourceConfigTest {

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=DataSourceConfig.class)
@ActiveProfiles("dev")
public static class DevDataSourceTest {
@Autowired
private DataSource dataSource;

@Test
public void shouldBeEmbeddedDatasource() {
assertNotNull(dataSource);
JdbcTemplate jdbc = new JdbcTemplate(dataSource);
List<String> results = jdbc.query("select id, name from Things", new RowMapper<String>() {
@Override
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
 return rs.getLong("id") + ":" + rs.getString("name");
}
});

assertEquals(1, results.size());
assertEquals("1:A", results.get(0));
}
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=DataSourceConfig.class)
@ActiveProfiles("prod")
public static class ProductionDataSourceTest {
@Autowired
private DataSource dataSource;

@Test
public void shouldBeEmbeddedDatasource() {
// should be null, because there isn't a datasource configured in JNDI
assertNull(dataSource);
}
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:datasource-config.xml")
@ActiveProfiles("dev")
public static class DevDataSourceTest_XMLConfig {
@Autowired
private DataSource dataSource;

@Test
public void shouldBeEmbeddedDatasource() {
assertNotNull(dataSource);
JdbcTemplate jdbc = new JdbcTemplate(dataSource);
List<String> results = jdbc.query("select id, name from Things", new RowMapper<String>() {
@Override
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
 return rs.getLong("id") + ":" + rs.getString("name");
}
});

assertEquals(1, results.size());
assertEquals("1:A", results.get(0));
}
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:datasource-config.xml")
@ActiveProfiles("prod")
public static class ProductionDataSourceTest_XMLConfig {
@Autowired(required=false)
private DataSource dataSource;

@Test
public void shouldBeEmbeddedDatasource() {
// should be null, because there isn't a datasource configured in JNDI
assertNull(dataSource);
}
}

}

运行shouldBeEmbeddedDatasource方法,测试通过

Spring入门实战之Profile详解

总结

标签:spring,profile
0
投稿

猜你喜欢

  • C#加密知识整合 (AES,MD5,RSA,SHA256)

    2023-07-19 09:09:13
  • Java并发编程之显示锁ReentrantLock和ReadWriteLock读写锁

    2023-06-04 04:54:22
  • Spring Boot多个定时任务阻塞问题的解决方法

    2023-09-20 11:43:36
  • C#中Equals方法的常见误解

    2023-03-10 21:59:23
  • C#生成二维码的方法

    2021-11-11 02:49:21
  • java复制文件的4种方式及拷贝文件到另一个目录下的实例代码

    2023-05-15 16:03:25
  • Android仿百度图片查看功能

    2023-09-26 07:50:24
  • Java中try catch处理异常示例

    2023-11-07 00:43:17
  • Java工具类之@RequestMapping注解

    2023-11-16 03:00:54
  • SpringBoot@DeleteMapping(/xxx/{id})请求报405的解决

    2022-06-13 14:00:48
  • Java 详解循环屏障CyclicBarrier如何实现多线程分段等待执行完成

    2022-02-16 04:50:49
  • 推荐两款java开发实用工具 hutool 和 lombok

    2022-04-06 10:20:14
  • 关于Jedis的用法以及Jedis使用Redis事务

    2023-06-28 07:22:56
  • java 保留两位小数的几种方法

    2022-05-11 16:38:27
  • Android webview旋转屏幕导致页面重新加载问题解决办法

    2021-10-18 17:07:35
  • 解决JSTL foEach标签 刷新报错的方法

    2021-08-12 13:48:25
  • 详解SpringBoot下文件上传与下载的实现

    2021-08-15 15:44:13
  • 详解OpenCV For Java环境搭建与功能演示

    2023-05-27 09:13:50
  • Java通过SSM完成水果商城批发平台流程

    2023-01-04 01:32:04
  • Android 自定义View实现抽屉效果

    2022-08-03 01:27:00
  • asp之家 软件编程 m.aspxhome.com