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

Будет ли создан один экземпляр бина, если два класса используют один и тот же бин?

1.0 Junior🔥 231 комментариев
#Spring Framework

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

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

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

Один экземпляр бина для двух классов в Spring?

Этот вопрос проверяет понимание Spring контейнера и жизненного цикла бинов. Ответ: ДА, по умолчанию будет создан ОДИН экземпляр (scope = singleton).

Пример

// Сервис
@Service
public class UserService {
    private int instanceCounter = 0;
    
    public UserService() {
        instanceCounter++;
        System.out.println("UserService создан. Номер экземпляра: " + instanceCounter);
    }
    
    public void processUser(String name) {
        System.out.println("Processing: " + name);
    }
}

// Первый класс использует сервис
@Component
public class OrderService {
    private final UserService userService;
    
    @Autowired
    public OrderService(UserService userService) {
        this.userService = userService;
        System.out.println("OrderService получил UserService");
    }
    
    public void createOrder(String userName) {
        userService.processUser(userName);
    }
}

// Второй класс тоже использует сервис
@Component
public class ReportService {
    private final UserService userService;
    
    @Autowired
    public ReportService(UserService userService) {
        this.userService = userService;
        System.out.println("ReportService получил UserService");
    }
    
    public void generateReport(String userName) {
        userService.processUser(userName);
    }
}

// Main
public class Application {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Application.class);
        
        OrderService orderService = context.getBean(OrderService.class);
        ReportService reportService = context.getBean(ReportService.class);
        
        orderService.createOrder("John");
        reportService.generateReport("John");
    }
}

// Вывод:
// UserService создан. Номер экземпляра: 1
// OrderService получил UserService
// ReportService получил UserService
// Processing: John
// Processing: John

// ВСЕ ИСПОЛЬЗУЮТ ОДИНАКОВЫЙ ЭКЗЕМПЛЯР USERSERVICE!

Доказательство: один экземпляр

@Service
public class UserService {
    private int counter = 0;
    
    public synchronized void increment() {
        counter++;
    }
    
    public int getCounter() {
        return counter;
    }
}

@Component
public class ServiceA {
    @Autowired
    private UserService userService;
    
    public void process() {
        userService.increment();
        System.out.println("ServiceA вызвал increment. Counter = " + userService.getCounter());
    }
}

@Component
public class ServiceB {
    @Autowired
    private UserService userService;
    
    public void process() {
        userService.increment();
        System.out.println("ServiceB вызвал increment. Counter = " + userService.getCounter());
    }
}

public class TestSingleton {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(TestSingleton.class);
        
        ServiceA serviceA = context.getBean(ServiceA.class);
        ServiceB serviceB = context.getBean(ServiceB.class);
        
        serviceA.process();  // Counter = 1
        serviceB.process();  // Counter = 2 (увеличился!)
        
        // Если бы были разные экземпляры UserService,
        // counter в ServiceB был бы 1, а не 2
    }
}

// Вывод:
// ServiceA вызвал increment. Counter = 1
// ServiceB вызвал increment. Counter = 2
// ✓ Это доказывает, что используется ОДНА копия UserService

ПОЧЕМУэто работает?

Spring использует Singleton паттерн по умолчанию

// Spring контейнер
public class DefaultListableBeanFactory implements BeanFactory {
    private Map<String, Object> singletons = new HashMap<>();
    
    @Override
    public Object getBean(String beanName) {
        // Проверяем, создан ли бин уже
        if (singletons.containsKey(beanName)) {
            return singletons.get(beanName);  // Возвращаем существующий
        }
        
        // Создаём новый
        Object bean = createBean(beanName);
        singletons.put(beanName, bean);  // Сохраняем
        return bean;
    }
}

Scope бинов в Spring

По умолчанию scope = singleton, но можно изменить:

1. Singleton (по умолчанию)

@Service
@Scope("singleton")  // или без аннотации
public class UserService {
    // Один экземпляр на всё приложение
}

// Все компоненты используют один и тот же объект
UserService service1 = context.getBean(UserService.class);
UserService service2 = context.getBean(UserService.class);
assert service1 == service2;  // true - один объект

2. Prototype (новый на каждый запрос)

@Service
@Scope("prototype")
public class UserService {
    // Новый экземпляр каждый раз
}

// Разные компоненты получают разные объекты
UserService service1 = context.getBean(UserService.class);
UserService service2 = context.getBean(UserService.class);
assert service1 != service2;  // true - разные объекты

3. Request (для веб-приложений)

@Service
@Scope("request")  // Один экземпляр на HTTP запрос
public class RequestService {
    // Новый экземпляр на каждый HTTP request
}

4. Session

@Service
@Scope("session")  // Один экземпляр на сессию
public class SessionService {
    // Новый экземпляр на каждую сессию пользователя
}

Практический пример: потенциальные проблемы

Проблема: Mutable state в singleton бине

@Service
public class NotThreadSafeService {
    private List<String> items = new ArrayList<>();  // ОПАСНО!
    
    public void addItem(String item) {
        items.add(item);  // Без синхронизации!
    }
    
    public List<String> getItems() {
        return items;
    }
}

// Проблема: Race condition!
OrderService и ReportService используют один экземпляр
Оба могут менять items одновременно
Может быть corruption данных

Решение 1: Синхронизация

@Service
public class ThreadSafeService {
    private final List<String> items = Collections.synchronizedList(new ArrayList<>());
    
    public void addItem(String item) {
        items.add(item);  // Безопасно
    }
}

Решение 2: Immutable поля

@Service
public class ImmutableService {
    private final List<String> items = new ArrayList<>();
    
    public void addItem(String item) {
        // items = new ArrayList<>(items);  // Неправильно
        items.add(item);  // items остаётся final, но содержимое может меняться
    }
}

Решение 3: Dependency на scope

@Service
@Scope("prototype")
public class StatefulService {
    private List<String> items = new ArrayList<>();  // OK
    
    public void addItem(String item) {
        items.add(item);  // Каждый использующий получит свой экземпляр
    }
}

Best Practices

1. Делай бины stateless (когда возможно)

// ✓ Хорошо - stateless
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public User getUser(Long id) {
        return userRepository.findById(id);
    }
}

// ✗ Плохо - stateful
@Service
public class OrderProcessor {
    private Order currentOrder;  // State!
    private User currentUser;     // State!
    
    public void processOrder() {
        // Race conditions!
    }
}

2. Если нужен state - используй prototype или request scope

@Service
@Scope("prototype")
public class OrderProcessor {
    private Order currentOrder;  // OK, каждый экземпляр свой
    private User currentUser;
}

3. Используй constructor injection

// ✓ Явно показывает зависимости
@Service
public class UserService {
    private final UserRepository userRepository;
    private final Logger logger;
    
    @Autowired
    public UserService(UserRepository userRepository, Logger logger) {
        this.userRepository = userRepository;
        this.logger = logger;
    }
}

// vs field injection (скрывает зависимости)
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;  // Неявно
}

Ответ резюме

ДА — если двум классам inject'ить один бин, они получат ОДИН экземпляр (по умолчанию scope=singleton).

Исключения:

  • Если бин имеет scope=prototype → будут разные экземпляры
  • Если бин имеет scope=request/session → для разных request/session
  • Если вручную создавать через new → разные объекты, но это не Spring бины

Моральный: Singleton бины должны быть stateless или thread-safe, иначе могут быть race conditions.

Будет ли создан один экземпляр бина, если два класса используют один и тот же бин? | PrepBro