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

Как настроить Spring для создания нового экземпляра бина @Component класса, где он используется через @Autowired

2.2 Middle🔥 231 комментариев
#REST API и микросервисы#Spring Boot и Spring Data#Spring Framework

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

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

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

Ответ

По умолчанию Spring создаёт один экземпляр (singleton) каждого @Component класса и переиспользует его везде. Если нужен новый экземпляр каждый раз — нужно изменить scope на PROTOTYPE.

1. Аннотация @Scope("prototype")

Это основной способ — каждый @Autowired получит новый экземпляр:

@Component
@Scope("prototype")
public class UserService {
    private String sessionId = UUID.randomUUID().toString();
    
    public void process() {
        System.out.println("Processing with session: " + sessionId);
    }
}

@Component
public class OrderProcessor {
    @Autowired
    private UserService userService1;
    
    @Autowired
    private UserService userService2;
    
    public void process() {
        userService1.process();  // sessionId_1
        userService2.process();  // sessionId_2 (разные!)
    }
}

2. Использование ScopedProxyMode

Если нужен prototype внутри singleton бина, используйте proxyMode:

@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext {
    private String requestId;
    
    public RequestContext() {
        this.requestId = UUID.randomUUID().toString();
    }
    
    public String getRequestId() {
        return requestId;
    }
}

@Component
public class RequestHandler {
    @Autowired
    private RequestContext requestContext;  // новый для каждого использования
    
    public void handleRequest() {
        System.out.println("Request ID: " + requestContext.getRequestId());
    }
}

3. Использование ObjectFactory

Для более явного управления созданием новых экземпляров:

@Component
public class DataService {
    private String dataId = UUID.randomUUID().toString();
    
    public void loadData() {
        System.out.println("Loading data: " + dataId);
    }
}

@Component
public class DataProcessor {
    @Autowired
    private ObjectFactory<DataService> dataServiceFactory;
    
    public void process() {
        // Каждый вызов getObject() создаёт новый экземпляр
        DataService service1 = dataServiceFactory.getObject();
        DataService service2 = dataServiceFactory.getObject();
        
        service1.loadData();  // dataId_1
        service2.loadData();  // dataId_2
    }
}

4. Использование Provider (Javax injection)

import javax.inject.Provider;
import javax.inject.Singleton;

@Component
public class ReportService {
    private String reportId = UUID.randomUUID().toString();
    
    public void generateReport() {
        System.out.println("Report ID: " + reportId);
    }
}

@Component
public class ReportGenerator {
    @Autowired
    private Provider<ReportService> reportServiceProvider;
    
    public void generate() {
        ReportService service1 = reportServiceProvider.get();
        ReportService service2 = reportServiceProvider.get();
        
        service1.generateReport();  // reportId_1
        service2.generateReport();  // reportId_2
    }
}

5. Использование метода-фабрики @Bean

Если компонент создаётся в конфиге:

@Configuration
public class AppConfig {
    @Bean
    @Scope("prototype")
    public PaymentProcessor paymentProcessor() {
        return new PaymentProcessor(UUID.randomUUID().toString());
    }
}

public class PaymentProcessor {
    private String processorId;
    
    public PaymentProcessor(String processorId) {
        this.processorId = processorId;
    }
    
    public void process() {
        System.out.println("Processing with: " + processorId);
    }
}

@Component
public class OrderService {
    @Autowired
    private ObjectFactory<PaymentProcessor> processorFactory;
    
    public void processOrder() {
        PaymentProcessor processor = processorFactory.getObject();
        processor.process();
    }
}

6. Сравнение Scopes

// SINGLETON (по умолчанию) — один на всё приложение
@Component  // это SINGLETON
public class AppConfig {}

// PROTOTYPE — новый для каждого @Autowired
@Component
@Scope("prototype")
public class SessionData {}

// REQUEST — новый для каждого HTTP запроса (только web)
@Component
@Scope("request")
public class RequestData {}

// SESSION — новый для каждой HTTP сессии (только web)
@Component
@Scope("session")
public class UserSession {}

// APPLICATION — один на сервлет контекст (только web)
@Component
@Scope("application")
public class ApplicationData {}

7. Полный пример с prototype

@Component
@Scope("prototype")
public class Task {
    private String taskId;
    private LocalDateTime createdAt;
    
    public Task() {
        this.taskId = UUID.randomUUID().toString();
        this.createdAt = LocalDateTime.now();
    }
    
    public void execute() {
        System.out.println("Executing task: " + taskId + " at " + createdAt);
    }
}

@Component
public class TaskManager {
    @Autowired
    private ObjectFactory<Task> taskFactory;
    
    public void executeTasks() {
        // Каждый раз новый экземпляр Task
        Task task1 = taskFactory.getObject();
        Task task2 = taskFactory.getObject();
        
        task1.execute();
        task2.execute();
        
        // taskId и createdAt будут разными
    }
}

8. Проблема: Prototype в Singleton

При инъекции prototype в singleton требуется proxyMode:

// НЕПРАВИЛЬНО — singleton получит одного prototype
@Component
public class SingletonService {
    @Autowired
    private PrototypeService proto;  // всегда один и тот же экземпляр!
}

// ПРАВИЛЬНО — используйте ObjectFactory
@Component
public class SingletonService {
    @Autowired
    private ObjectFactory<PrototypeService> protoFactory;
    
    public void usePrototype() {
        PrototypeService proto1 = protoFactory.getObject();
        PrototypeService proto2 = protoFactory.getObject();
        // Это разные экземпляры!
    }
}

// ИЛИ используйте @Scope с proxyMode
@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PrototypeService {
}

@Component
public class SingletonService {
    @Autowired
    private PrototypeService proto;  // proxy будет создавать новые экземпляры
}

Когда использовать Prototype?

  1. Stateful объекты: каждый нуждается в своём состоянии
  2. Session-specific данные: разные для каждого запроса
  3. Временные объекты: создаются и выбрасываются
  4. Потокобезопасность: избежать синхронизации

Правило выбора

  • Singleton — по умолчанию, для stateless сервисов
  • Prototype — для stateful компонентов
  • ObjectFactory/Provider — для контролируемого создания

Основное правило: используйте @Scope("prototype") для класса, и Spring будет создавать новый экземпляр каждый раз при инъекции.

Как настроить Spring для создания нового экземпляра бина @Component класса, где он используется через @Autowired | PrepBro