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

Как работает Spring с Bean, если использовать конфигурацию с помощью аннотаций?

2.0 Middle🔥 251 комментариев
#Spring Framework

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

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

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

Как Spring работает с Bean через аннотации

Обзор процесса

Когда ты используешь аннотации для конфигурации Bean, Spring выполняет следующие шаги:

  1. Component Scanning — сканирование пакетов на предмет аннотированных классов
  2. Bean Creation — создание экземпляров классов
  3. Dependency Injection — внедрение зависимостей
  4. Bean Initialization — инициализация Bean
  5. Bean Ready — Bean готов к использованию

Шаг 1: Включение Component Scanning

// Способ 1: Через @Configuration + @ComponentScan
@Configuration
@ComponentScan(basePackages = "com.example.app")
public class AppConfig {
    // Пусто — сканирование включено
}

// Способ 2: Spring Boot (автоматически)
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// @SpringBootApplication = @Configuration + @ComponentScan + @EnableAutoConfiguration

Шаг 2: Распознание Bean-классов

Spring сканирует пакеты и находит классы с аннотациями:

// Эти аннотации регистрируют класс как Bean
@Component                    // Базовая аннотация
public class MyComponent { }

@Service                      // Для бизнес-логики
public class UserService { }

@Repository                   // Для доступа к данным
public class UserRepository { }

@Controller                   // Для web-контроллеров
public class UserController { }

@RestController               // Для REST API
public class ApiController { }

@Configuration                // Для конфигурации
public class AppConfig { }

Все они — подтипы @Component, Spring их одинаково обрабатывает:

// Внутренне
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
    String value() default "";
}
// Аннотирована @Component

Шаг 3: Создание Bean экземпляра

@Service
public class UserService {
    public UserService() {
        System.out.println("UserService создан");
    }
}

@Repository
public class UserRepository {
    public UserRepository() {
        System.out.println("UserRepository создан");
    }
}

Внутренний процесс:

// Примерная реализация (внутри Spring)
public class BeanFactory {
    public Object createBean(Class<?> beanClass) throws Exception {
        // 1. Получить конструктор
        Constructor<?> constructor = beanClass.getDeclaredConstructor();
        
        // 2. Создать экземпляр через рефлексию
        Object instance = constructor.newInstance();
        
        // 3. Вернуть экземпляр
        return instance;
    }
}

Шаг 4: Dependency Injection (DI)

Spring анализирует конструкторы и поля, затем внедряет зависимости:

@Service
public class UserService {
    // Способ 1: Constructor Injection (рекомендуется)
    private final UserRepository repository;
    
    public UserService(UserRepository repository) {
        this.repository = repository;
    }
}

@Service
public class OrderService {
    // Способ 2: Setter Injection
    private UserService userService;
    
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

@Service
public class PaymentService {
    // Способ 3: Field Injection (не рекомендуется)
    @Autowired
    private UserService userService;
}

Внутренняя логика:

// Примерная реализация (упрощённо)
public class BeanFactory {
    private Map<String, Object> beans = new HashMap<>();
    
    public void registerBeans(String[] packageNames) {
        // Сканировать пакеты
        Set<Class<?>> annotatedClasses = findAnnotatedClasses(packageNames);
        
        // Создать Bean для каждого класса
        for (Class<?> clazz : annotatedClasses) {
            Object bean = createBean(clazz);
            beans.put(getBeanName(clazz), bean);
        }
    }
    
    public Object createBean(Class<?> clazz) {
        // Получить конструктор с наибольшим количеством параметров
        Constructor<?> constructor = selectConstructor(clazz);
        
        // Найти зависимости для параметров конструктора
        Object[] dependencies = resolveDependencies(constructor.getParameterTypes());
        
        // Создать экземпляр
        Object instance = constructor.newInstance(dependencies);
        
        // Обработать @Autowired на полях
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Autowired.class)) {
                Object dependency = beans.get(field.getType());
                field.setAccessible(true);
                field.set(instance, dependency);
            }
        }
        
        return instance;
    }
}

Шаг 5: Управление жизненным циклом

Spring поддерживает callback-методы для управления жизненным циклом Bean:

@Service
public class UserService implements InitializingBean, DisposableBean {
    
    // Способ 1: Через интерфейсы
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Bean инициализирован");
        // Здесь инициализировать ресурсы
    }
    
    @Override
    public void destroy() throws Exception {
        System.out.println("Bean уничтожается");
        // Здесь закрывать ресурсы
    }
}

@Service
public class PaymentService {
    // Способ 2: Через аннотации
    @PostConstruct
    public void init() {
        System.out.println("PaymentService инициализирован");
    }
    
    @PreDestroy
    public void cleanup() {
        System.out.println("PaymentService уничтожается");
    }
}

@Service
public class OrderService {
    // Способ 3: Через @Bean аннотацию в конфигурации
    @Bean(initMethod = "init", destroyMethod = "cleanup")
    public OrderService orderService() {
        return new OrderService();
    }
}

Порядок инициализации Bean

// 1. Создание
public UserService() {
    System.out.println("1. Конструктор");
}

// 2. Внедрение зависимостей
@Autowired
Public void setRepository(UserRepository repo) {
    System.out.println("2. Injection");
}

// 3. Инициализация
@PostConstruct
public void init() {
    System.out.println("3. Init");
}

// Порядок вывода: 1, 2, 3

Пример полного цикла

// 1. Конфигурация
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}

// 2. Repository
@Repository
public class UserRepository {
    public UserRepository() {
        System.out.println("1. UserRepository constructor");
    }
    
    @PostConstruct
    public void init() {
        System.out.println("3. UserRepository init");
    }
}

// 3. Service
@Service
public class UserService {
    private final UserRepository repository;
    
    public UserService(UserRepository repository) {
        this.repository = repository;
        System.out.println("2. UserService constructor");
    }
    
    @PostConstruct
    public void init() {
        System.out.println("4. UserService init");
    }
}

// 4. Controller
@RestController
public class UserController {
    private final UserService service;
    
    public UserController(UserService service) {
        this.service = service;
        System.out.println("5. UserController constructor");
    }
}

// Вывод при запуске:
// 1. UserRepository constructor
// 3. UserRepository init
// 2. UserService constructor
// 4. UserService init
// 5. UserController constructor

Bean Scope

Spring управляет жизненным циклом Bean в зависимости от scope:

@Service
@Scope("singleton")  // По умолчанию — один экземпляр на весь контекст
public class UserService {
}

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

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

@Service
@Scope("session")    // Один экземпляр на HTTP-сессию
public class SessionData {
}

Итог

При использовании аннотаций Spring:

  1. Сканирует классы с @Component, @Service, @Repository и другими аннотациями
  2. Создаёт экземпляры через рефлексию
  3. Разрешает зависимости и внедряет их
  4. Инициализирует Bean через @PostConstruct методы
  5. Управляет жизненным циклом согласно Scope
  6. Уничтожает при завершении контекста через @PreDestroy

Это всё происходит автоматически — разработчику нужно только аннотировать классы!