Эта концепция представляет собой простой дифференциатор конфигурации для различных сред развертывания.
Прямой случай использования (который был представлен) заключался в том, чтобы аннотировать соответствующие классы, чтобы 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 (в соответствии с вашей средой) - Определите класс для установки активного профиля
0102030405060708091011121314151617
publicclassConfigurableApplicationContextInitializerimplementsApplicationContextInitializer<configurableapplicationcontext> {@Overridepublicvoidinitialize(ConfigurableApplicationContext applicationContext) {String profile = System.getProperty("profile");if(profile==null|| profile.equalsIgnoreCase(Profile.DEV.name())){applicationContext.getEnvironment().setActiveProfiles(Profile.DEV.name());}elseif(profile.equalsIgnoreCase(Profile.PRODUCTION.name())){applicationContext.getEnvironment().setActiveProfiles(Profile.PRODUCTION.name());}elseif(profile.equalsIgnoreCase(Profile.TEST.name())){applicationContext.getEnvironment().setActiveProfiles(Profile.TEST.name());}}} - Убедитесь, что класс загружается во время загрузки контекста
в проекте web.xml вставьте следующее:1234<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 fileprivate 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 Гала Левинского в блоге Гала Левинского .