Статьи

Пример шаблона Spring Profile

Недавно мы познакомились с концепцией Spring Profiles .

Эта концепция представляет собой простой дифференциатор конфигурации для различных сред развертывания.

Прямой случай использования (который был представлен) заключался в том, чтобы аннотировать соответствующие классы, чтобы Spring загружал соответствующий класс в соответствии с активным профилем.

Тем не менее, этот подход не всегда может служить распространенному случаю … часто ключи конфигурации будут одинаковыми, и только значения будут меняться в зависимости от среды.

В этом посте я хотел бы представить шаблон для поддержки загрузки данных конфигурации для каждой среды без необходимости создавать / поддерживать несколько классов для каждого профиля (т.е. для каждой среды).

В этом посте я бы взял в качестве примера конфигурацию соединения с БД, предполагая, что у нас есть разные определения БД (например, имя пользователя или URL-адрес соединения) для каждой среды развертывания.

Основная идея состоит в том, чтобы использовать один класс для загрузки конфигурации (т. Е. Один класс для определения соединения с БД) и внедрить в него соответствующий экземпляр, который содержит правильные данные конфигурации профиля.

Для удобства и ясности процесс был разделен на 3 этапа:

Фаза 1 : подготавливается
Шаг 1.1 — создайте файл свойств, который содержит все данные конфигурации
Шаг 1.2 — создайте аннотацию для каждого профиля
шаг 1.3 — убедитесь, что профиль загружен во время загрузки контекста

Этап 2 : реализация шаблона профиля
Шаг 2.1 — создать интерфейс свойств
Шаг 2.2 — создать класс для каждого профиля
Шаг 2.3 — создайте абстрактный файл, содержащий все данные

Этап 3 : использование шаблона
Шаг 3.1 — пример использования шаблона

Модель Spring Profile — фаза 1: подготовка к инфраструктуре

На этом этапе будет установлена ​​начальная инфраструктура для использования Spring Profile и файлов конфигурации.

Шаг 1.1 — создайте файл свойств, который содержит все данные конфигурации

Предполагая, что у вас есть проект в стиле maven, создайте файл в src / main / resources / properties для каждой среды, например:

my_company_dev.properties
my_company_test.properties
my_company_production.properties

пример для содержимого my_company_dev.properties:

jdbc.url = JDBC: MySQL: // локальный: 3306 / my_project_db
db.username = dev1
db.password = dev1
hibernate.show_sql = верно

пример содержания my_company_production.properties:

jdbc.url = JDBC: MySQL: //10.26.26.26: 3306 / my_project_db
db.username = prod1
db.password = fdasjkladsof8aualwnlulw344uwj9l34
hibernate.show_sql = ложь

Шаг 1.2 — создайте аннотацию для каждого профиля

В src.main.java.com.mycompany.annotation создайте аннотацию для каждого профиля, например:

1
2
3
4
5
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("DEV")
public @interface Dev {
}
1
2
3
4
5
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("PRODUCTION")
public @interface Production {
}

Создайте перечисление для каждого профиля:
открытый интерфейс MyEnums {

1
2
3
4
5
public enum Profile{
DEV,
TEST,
PRODUCTION
}

Шаг 1.3 — убедитесь, что профиль загружен во время загрузки контекста

  • Определите системную переменную, чтобы указать, в какой среде выполняется код.
    В Tomcat перейдите в $ {tomcat.di} /conf/catalina.properties и вставьте строку:
    профиль = DEV (в соответствии с вашей средой)
  • Определите класс для установки активного профиля
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    public class ConfigurableApplicationContextInitializer implements
      ApplicationContextInitializer<configurableapplicationcontext> {
     
     @Override
     public void initialize(ConfigurableApplicationContext applicationContext) {
           
      String profile = System.getProperty("profile");
         
      if (profile==null || profile.equalsIgnoreCase(Profile.DEV.name())){
       applicationContext.getEnvironment().setActiveProfiles(Profile.DEV.name());  
      }else if(profile.equalsIgnoreCase(Profile.PRODUCTION.name())){
       applicationContext.getEnvironment().setActiveProfiles(Profile.PRODUCTION.name());
      }else if(profile.equalsIgnoreCase(Profile.TEST.name())){
       applicationContext.getEnvironment().setActiveProfiles(Profile.TEST.name());
            }
     }
    }
  • Убедитесь, что класс загружается во время загрузки контекста
    в проекте web.xml вставьте следующее:

    1
    2
    3
    4
    <context-param>
         <param-name>contextInitializerClasses</param-name>
         <param-value>com.matomy.conf.ConfigurableApplicationContextInitializer</param-value>
    </context-param>

Этап 2: реализация шаблона профиля

На этом этапе используется инфраструктура, которую мы создали ранее, и реализуется шаблон профиля.

Шаг 2.1 — создать интерфейс свойств
Создайте интерфейс для данных конфигурации, которые у вас есть.
В нашем случае интерфейс предоставит доступ к четырем элементам данных конфигурации.
так это будет выглядеть примерно так:

1
2
3
4
5
6
7
public interface SystemStrings {
 
String getJdbcUrl();
String getDBUsername();
String getDBPassword();
Boolean getHibernateShowSQL();
//.....

Шаг 2.2 — создать класс для каждого профиля

Пример для профиля разработки:

1
2
3
4
5
6
7
8
9
@Dev //Notice the dev annotation
@Component("systemStrings")
public class SystemStringsDevImpl extends AbstractSystemStrings implements SystemStrings{
       
 public SystemStringsDevImpl() throws IOException {
                //indication on the relevant properties file
  super("/properties/my_company_dev.properties");
 }
}

Пример для производственного профиля:

1
2
3
4
5
6
7
8
9
@Prouction //Notice the production annotation
@Component("systemStrings")
public class SystemStringsProductionImpl extends AbstractSystemStrings implements SystemStrings{
       
 public SystemStringsProductionImpl() throws IOException {
                //indication on the relevant properties file
  super("/properties/my_company_production.properties");
 }
}

Два вышеупомянутых класса — то, где происходит связывание между файлом свойств и связанной средой.

Вы, наверное, заметили, что классы расширяют абстрактный класс. Этот метод полезен, поэтому нам не нужно определять каждый получатель для каждого профиля, это не будет управляемым в долгосрочной перспективе, и действительно, нет смысла делать это.

Сладость и мед лежат на следующем этапе, где определяется абстрактный класс.

Шаг 2.3 — создайте абстрактный файл, содержащий все данные

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public abstract class AbstractSystemStrings implements SystemStrings{
 
 //Variables as in configuration properties file
private String jdbcUrl;
private String dBUsername;
private String dBPassword;
private boolean hibernateShowSQL;
 
public AbstractSystemStrings(String activePropertiesFile) throws IOException {
  //option to override project configuration from externalFile
  loadConfigurationFromExternalFile();//optional..
                //load relevant properties
  loadProjectConfigurationPerEnvironment(activePropertiesFile); 
 }
 
private void loadProjectConfigurationPerEnvironment(String activePropertiesFile) throws IOException {
  Resource[] resources = new ClassPathResource[ ]  {  new ClassPathResource( activePropertiesFile ) };
  Properties props = null;
  props = PropertiesLoaderUtils.loadProperties(resources[0]);
                jdbcUrl = props.getProperty("jdbc.url");
                dBUsername = props.getProperty("db.username");
                dBPassword = props.getProperty("db.password");
                hibernateShowSQL = new Boolean(props.getProperty("hibernate.show_sql")); 
}
 
//here should come the interface getters....

Этап 3: использование шаблона

Как вы помните, на предыдущих шагах мы определили интерфейс для данных конфигурации.

Теперь мы будем использовать интерфейс в классе, который требует разных данных для каждой среды.

Обратите внимание, что этот пример является ключевым отличием от примера, приведенного в блоге Spring , поскольку теперь нам не нужно создавать класс для каждого профиля , так как в этом случае мы используем один и тот же метод для разных профилей, и только изменения данных .

Шаг 3.1 — пример использования шаблона

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@Configuration
@EnableTransactionManagement
//DB connection configuration class
//(don't tell me you're still using xml... 😉
public class PersistenceConfig {
 
 @Autowired
 private SystemStrings systemStrings; //Spring will wire by active profile
 
 @Bean
 public LocalContainerEntityManagerFactoryBean entityManagerFactoryNg(){
  LocalContainerEntityManagerFactoryBean factoryBean
  = new LocalContainerEntityManagerFactoryBean();
  factoryBean.setDataSource( dataSource() );
  factoryBean.setPersistenceUnitName("my_pu");      
  JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(){
   {
    // JPA properties
    this.setDatabase( Database.MYSQL);
this.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
    this.setShowSql(systemStrings.getShowSqlMngHibernate());//is set per environemnt..          
    
   }
  };      
  factoryBean.setJpaVendorAdapter( vendorAdapter );
  factoryBean.setJpaProperties( additionalProperties() );
 
  return factoryBean;
 }
//...
@Bean
 public ComboPooledDataSource dataSource(){
  ComboPooledDataSource poolDataSource = new ComboPooledDataSource();
  try {
   poolDataSource.setDriverClass( systemStrings.getDriverClassNameMngHibernate() );
  } catch (PropertyVetoException e) {
   e.printStackTrace();
  }      
                 //is set per environemnt..
  poolDataSource.setJdbcUrl(systemStrings.getJdbcUrl());
  poolDataSource.setUser( systemStrings.getDBUsername() );
  poolDataSource.setPassword( systemStrings.getDBPassword() );
  //.. more properties...      
  return poolDataSource;
 }
}

Буду признателен за комментарии и улучшения.
Наслаждайтесь!

Ссылка: шаблон Spring Profile от нашего партнера JCG Гала Левинского в блоге Гала Левинского .