← Назад к вопросам

Как инициализировать сервис в Spring

1.2 Junior🔥 191 комментариев
#Spring Boot и Spring Data#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Как инициализировать сервис в Spring

Основной способ: @Component и Constructor Injection

@Service  // или @Component
public class UserService {
    private final UserRepository repository;
    private final EmailService emailService;
    
    // Constructor Injection — Spring автоматически вызовет конструктор
    public UserService(UserRepository repository, EmailService emailService) {
        this.repository = repository;
        this.emailService = emailService;
    }
    
    public User createUser(String name, String email) {
        User user = new User(name, email);
        repository.save(user);
        emailService.sendWelcome(email);
        return user;
    }
}

Инициализация с @PostConstruct

После внедрения зависимостей

@Service
public class CacheService {
    private final UserRepository repository;
    private Map<Long, User> cache;
    
    public CacheService(UserRepository repository) {
        this.repository = repository;
    }
    
    @PostConstruct
    public void init() {
        // Вызывается после конструктора и внедрения всех зависимостей
        this.cache = new ConcurrentHashMap<>();
        loadCacheFromDatabase();
        System.out.println("Кеш инициализирован");
    }
    
    private void loadCacheFromDatabase() {
        List<User> users = repository.findAll();
        users.forEach(u -> cache.put(u.getId(), u));
    }
}

Применение в реальных случаях

@Service
public class SchedulerService {
    private ScheduledExecutorService executor;
    
    public SchedulerService() {
        // Не создаём здесь, может быть null при тестировании
    }
    
    @PostConstruct
    public void startScheduler() {
        executor = Executors.newScheduledThreadPool(4);
        executor.scheduleAtFixedRate(this::runTask, 0, 5, TimeUnit.SECONDS);
    }
    
    @PreDestroy
    public void stopScheduler() {
        executor.shutdown();
    }
    
    private void runTask() {
        System.out.println("Задача выполняется");
    }
}

Использование InitializingBean (старый способ)

@Service
public class LegacyService implements InitializingBean {
    
    @Override
    public void afterPropertiesSet() throws Exception {
        // Вызывается после внедрения всех зависимостей
        System.out.println("Сервис инициализирован");
    }
}

Минусы: Spring-специфичный код, менее читаемо

Инициализация через ApplicationContextAware

@Service
public class ContextAwareService implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.applicationContext = context;
    }
    
    @PostConstruct
    public void init() {
        // Теперь можно использовать applicationContext
        UserService userService = applicationContext.getBean(UserService.class);
    }
}

Инициализация с @Configuration

Factory method

@Configuration
public class AppConfig {
    
    @Bean
    public UserService userService(UserRepository repository) {
        // Spring вызовет этот метод и создаст бин
        return new UserService(repository);
    }
    
    @Bean
    public CacheService cacheService(UserService userService) {
        // Зависимость передаётся как параметр
        return new CacheService(userService);
    }
}

С инициализацией

@Configuration
public class DataSourceConfig {
    
    @Bean
    public DataSource dataSource() {
        return DataSourceBuilder.create()
            .driverClassName("org.postgresql.Driver")
            .url("jdbc:postgresql://localhost:5432/mydb")
            .username("user")
            .password("password")
            .build();
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

Инициализация с зависимостями от свойств

@Service
public class ApiClientService {
    private String apiUrl;
    private int timeout;
    private RestTemplate restTemplate;
    
    public ApiClientService(
        @Value("${api.url}") String apiUrl,
        @Value("${api.timeout:5000}") int timeout,
        RestTemplate restTemplate) {
        this.apiUrl = apiUrl;
        this.timeout = timeout;
        this.restTemplate = restTemplate;
    }
    
    @PostConstruct
    public void init() {
        System.out.println("API Client инициализирован для: " + apiUrl);
    }
}

Инициализация с @Profile

@Service
public class DataService {
    
    @Profile("dev")
    @PostConstruct
    public void initDev() {
        System.out.println("Инициализация для DEV профиля");
    }
    
    @Profile("prod")
    @PostConstruct
    public void initProd() {
        System.out.println("Инициализация для PROD профиля");
    }
}

Инициализация с EnvironmentAware

@Service
public class EnvService implements EnvironmentAware {
    private Environment environment;
    
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
    
    @PostConstruct
    public void init() {
        String dbUrl = environment.getProperty("spring.datasource.url");
        System.out.println("DB: " + dbUrl);
    }
}

Инициализация с очередью выполнения (@Order)

@Component
@Order(1)
public class FirstService {
    @PostConstruct
    public void init() {
        System.out.println("FirstService инициализирован");
    }
}

@Component
@Order(2)
public class SecondService {
    @PostConstruct
    public void init() {
        System.out.println("SecondService инициализирован");
    }
}

Инициализация с ObjectProvider (опциональные зависимости)

@Service
public class ServiceWithOptionalDeps {
    private final UserRepository repository;
    private final Optional<CacheService> cacheService;
    
    public ServiceWithOptionalDeps(
        UserRepository repository,
        ObjectProvider<CacheService> cacheServiceProvider) {
        this.repository = repository;
        this.cacheService = cacheServiceProvider.getIfAvailable();
    }
    
    public User getUser(Long id) {
        if (cacheService.isPresent()) {
            return cacheService.get().getFromCache(id);
        }
        return repository.findById(id).orElse(null);
    }
}

Очистка при завершении: @PreDestroy

@Service
public class ConnectionPoolService {
    private HikariDataSource dataSource;
    
    @PostConstruct
    public void init() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:postgresql://localhost/db");
        config.setMaximumPoolSize(10);
        this.dataSource = new HikariDataSource(config);
        System.out.println("Пул соединений создан");
    }
    
    @PreDestroy
    public void cleanup() {
        if (dataSource != null) {
            dataSource.close();
            System.out.println("Пул соединений закрыт");
        }
    }
}

Вся инициализация в тесте

@SpringBootTest
class UserServiceTest {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private UserRepository repository;
    
    @BeforeEach
    public void setUp() {
        // @PostConstruct уже вызван, сервис готов
    }
    
    @Test
    public void testUserCreation() {
        User user = userService.createUser("John", "john@mail.com");
        assertNotNull(user);
    }
}

Best Practices

  1. Constructor Injection — предпочитай для всегда нужных зависимостей
  2. @PostConstruct — для инициализации после внедрения
  3. @PreDestroy — для очистки ресурсов
  4. Избегай логики в конструкторе — оставь инициализацию для @PostConstruct
  5. Не создавай потоки в конструкторе — делай в @PostConstruct
  6. Тестируй инициализацию — убедись, что @PostConstruct работает
  7. Используй @Profile для разной инициализации в разных окружениях