Может ли быть несколько конфигураций Spring в одном проекте?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Несколько конфигураций Spring в одном проекте
Да, абсолютно можно иметь несколько конфигураций Spring в одном проекте, и это очень частая практика в реальных приложениях. Spring специально разработан для поддержки модульной архитектуры с разными конфигурациями для разных контекстов.
Базовое понимание
Kogda Spring приложение стартует, оно может загрузить несколько @Configuration классов, и каждый из них будет вносить свои бины в контейнер:
// Spring загружает все @Configuration классы и мержит их
@SpringBootApplication
public class IkeaBackendApplication {
public static void main(String[] args) {
SpringApplication.run(IkeaBackendApplication.class, args);
}
}
// Spring автоматически найдёт все @Configuration классы
// в пакете приложения и его подпакетах
Способ 1: Разделение конфигураций по функциональности
Это лучшая практика — разделять конфигурации по доменам:
// 1. Конфигурация БД
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource() {
return DataSourceBuilder.create()
.driverClassName("org.postgresql.Driver")
.url("jdbc:postgresql://localhost:5432/ikea")
.username("user")
.password("pass")
.build();
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
// 2. Конфигурация кеширования (Redis)
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
public RedisCacheManager cacheManager(LettuceConnectionFactory connectionFactory) {
return RedisCacheManager.create(connectionFactory);
}
}
// 3. Конфигурация асинхронной обработки
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(500);
executor.initialize();
return executor;
}
}
// 4. Конфигурация безопасности
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.build();
}
}
// 5. Конфигурация HTTP клиента
@Configuration
public class HttpClientConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public WebClient webClient() {
return WebClient.builder()
.baseUrl("https://external-api.com")
.build();
}
}
Все эти конфигурации Spring автоматически загрузит и создаст бины в едином контейнере.
Способ 2: Конфигурации для разных профилей (Profiles)
Это очень полезно для разных окружений:
// application.yml
spring:
profiles:
active: dev
---
spring:
config:
activate:
on-profile: dev
server:
port: 8080
database:
url: jdbc:postgresql://localhost:5432/ikea_dev
---
spring:
config:
activate:
on-profile: prod
server:
port: 8080
database:
url: jdbc:postgresql://prod-db.internal:5432/ikea
Теперь создаём разные конфигурации для разных профилей:
// Конфигурация для разработки
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
public DataSource devDataSource() {
return DataSourceBuilder.create()
.driverClassName("org.h2.Driver") // In-memory H2 для dev
.url("jdbc:h2:mem:testdb")
.username("sa")
.password("")
.build();
}
@Bean
public CacheManager devCacheManager() {
return new ConcurrentMapCacheManager("products", "users");
}
}
// Конфигурация для production
@Configuration
@Profile("prod")
public class ProdConfig {
@Bean
public DataSource prodDataSource(
@Value("${database.url}") String url,
@Value("${database.username}") String username,
@Value("${database.password}") String password) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(50); // Production: больше connections
return new HikariDataSource(config);
}
@Bean
public CacheManager prodCacheManager(LettuceConnectionFactory factory) {
return RedisCacheManager.create(factory); // Redis в production
}
}
// Конфигурация для тестирования
@Configuration
@Profile("test")
public class TestConfig {
@Bean
public DataSource testDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:test-data.sql")
.build();
}
@Bean
public UserRepository mockUserRepository() {
return Mockito.mock(UserRepository.class);
}
}
Запуск с разными профилями:
# Development
java -jar app.jar --spring.profiles.active=dev
# Production
java -jar app.jar --spring.profiles.active=prod
# Testing
java -jar app.jar --spring.profiles.active=test
# Несколько профилей
java -jar app.jar --spring.profiles.active=prod,metrics,security
Способ 3: Условные конфигурации (@ConditionalOnClass, @ConditionalOnProperty)
// Конфигурация загружается только если в classpath есть Redis
@Configuration
@ConditionalOnClass(RedisTemplate.class)
@EnableCaching
public class RedisConfigIfAvailable {
@Bean
public RedisTemplate<String, Object> redisTemplate(
LettuceConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
return template;
}
}
// Конфигурация загружается если включена в properties
@Configuration
@ConditionalOnProperty(
name = "features.analytics.enabled",
havingValue = "true"
)
public class AnalyticsConfig {
@Bean
public AnalyticsService analyticsService() {
return new GoogleAnalyticsService();
}
}
// Конфигурация с условием на класс
@Configuration
@ConditionalOnMissingBean(EmailService.class)
public class DefaultEmailConfig {
@Bean
public EmailService emailService() {
return new ConsoleEmailService(); // Mock для dev
}
}
Способ 4: Импорт других конфигураций (@Import)
// Главная конфигурация импортирует другие
@Configuration
@Import({
DatabaseConfig.class,
CacheConfig.class,
SecurityConfig.class,
AsyncConfig.class,
HttpClientConfig.class
})
public class MainApplicationConfig {
// Дополнительные бины
@Bean
public ApplicationEventPublisher eventPublisher(ApplicationContext context) {
return context;
}
}
@SpringBootApplication
public class IkeaApplication {
public static void main(String[] args) {
SpringApplication.run(IkeaApplication.class, args);
}
}
Способ 5: Модульная архитектура с отдельными контекстами
// Модуль "Товары"
@Configuration
public class ProductModuleConfig {
@Bean
public ProductService productService(ProductRepository repo) {
return new ProductService(repo);
}
@Bean
public ProductController productController(ProductService service) {
return new ProductController(service);
}
}
// Модуль "Заказы"
@Configuration
public class OrderModuleConfig {
@Bean
public OrderService orderService(
OrderRepository orderRepo,
ProductService productService) {
return new OrderService(orderRepo, productService);
}
@Bean
public OrderController orderController(OrderService service) {
return new OrderController(service);
}
}
// Главная конфигурация собирает модули
@Configuration
@Import({
ProductModuleConfig.class,
OrderModuleConfig.class
})
public class ApplicationModuleConfig {
}
Способ 6: Конфигурация для интеграционных тестов
// Основной контекст
@SpringBootApplication
public class IkeaApplication {}
// Отдельная конфигурация для тестов
@Configuration
public class TestDataConfig {
@Bean
@Profile("test")
public CommandLineRunner loadTestData(UserRepository userRepo) {
return args -> {
User testUser = new User("test@example.com", "Test User");
userRepo.save(testUser);
};
}
}
// Тест использует оба контекста
@SpringBootTest
@ActiveProfiles("test")
class UserServiceIntegrationTest {
@Autowired
private UserRepository userRepository;
@Test
void shouldLoadTestData() {
List<User> users = userRepository.findAll();
assertThat(users).isNotEmpty();
}
}
Лучшие практики
-
Разделяй по функциональности
DatabaseConfig.class CacheConfig.class SecurityConfig.class MQConfig.class ExternalApiConfig.class -
Используй профили для разных окружений
@Profile("dev") @Profile("prod") @Profile("test") -
Используй условные конфигурации
@ConditionalOnClass @ConditionalOnProperty @ConditionalOnBean -
Избегай циркулярных зависимостей между конфигурациями
// ❌ Плохо ConfigA → ConfigB → ConfigA // ✅ Хорошо ConfigA ← CommonConfig → ConfigB -
Импортируй явно для читаемости
@Import({Config1.class, Config2.class}) // Лучше чем полагаться на @ComponentScan
Итог
Несколько конфигураций Spring в одном проекте — это стандартная и рекомендуемая практика, которая обеспечивает:
- Модульность
- Разделение ответственности
- Гибкость для разных окружений
- Легкость тестирования
- Читаемость и поддерживаемость кода
В реальном production проекте обычно бывает 5-10 конфигураций, каждая отвечающая за свою область.