← Назад к вопросам
Как бы написал часть Spring Framework, который бы создавал объекты из полей с @Bean
2.0 Middle🔥 251 комментариев
#Spring Boot и Spring Data#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Реализация @Bean-аннотации Spring
Этот вопрос демонстрирует глубокое понимание Spring. Я напишу упрощенную версию механизма, который сканирует классы, находит методы с @Bean и регистрирует их как бины в контексте.
Архитектура решения
1. Сканирование классов -> Find @Configuration classes
2. Анализ методов -> Find @Bean methods
3. Создание прокси -> Create Bean definitions
4. Управление зависимостями -> Resolve dependencies
5. Инстанцирование объектов -> Create instances
Шаг 1: Определение аннотаций
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Configuration {
String value() default "";
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
String name() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
Шаг 2: Определение Bean Definition
import java.lang.reflect.Method;
import java.util.List;
public class BeanDefinition {
private String beanName;
private Class<?> beanClass;
private Method factoryMethod; // Метод с @Bean
private Object configInstance; // Экземпляр класса с @Configuration
private List<String> dependencies; // Имена зависимостей
private Object instance; // Кэшированный экземпляр (singleton)
public BeanDefinition(
String beanName,
Class<?> beanClass,
Method factoryMethod,
Object configInstance) {
this.beanName = beanName;
this.beanClass = beanClass;
this.factoryMethod = factoryMethod;
this.configInstance = configInstance;
this.dependencies = new ArrayList<>();
}
public String getBeanName() { return beanName; }
public Class<?> getBeanClass() { return beanClass; }
public Method getFactoryMethod() { return factoryMethod; }
public Object getConfigInstance() { return configInstance; }
public List<String> getDependencies() { return dependencies; }
public void setInstance(Object instance) { this.instance = instance; }
public Object getInstance() { return instance; }
public boolean isSingleton() { return instance != null; }
}
Шаг 3: Spring Context (Контейнер бинов)
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
public class SimpleApplicationContext {
private Map<String, BeanDefinition> beanDefinitions = new HashMap<>();
private Map<String, Object> singletons = new HashMap<>();
private List<Object> configurationInstances = new ArrayList<>();
/**
* Инициализируем контекст: сканируем конфигурации и регистрируем бины
*/
public SimpleApplicationContext(Class<?>... configClasses) {
// Шаг 1: Регистрируем конфигурационные классы
for (Class<?> configClass : configClasses) {
registerConfigurationClass(configClass);
}
// Шаг 2: Создаем все singleton бины
initializeSingletons();
}
/**
* Регистрируем конфигурационный класс и его @Bean методы
*/
private void registerConfigurationClass(Class<?> configClass) {
// Проверяем, что класс помечен @Configuration
if (!configClass.isAnnotationPresent(Configuration.class)) {
throw new IllegalArgumentException(
configClass.getName() + " не помечен @Configuration");
}
try {
// Создаем экземпляр конфигурационного класса
Object configInstance = configClass.getDeclaredConstructor().newInstance();
configurationInstances.add(configInstance);
// Обрабатываем все методы класса
Method[] methods = configClass.getDeclaredMethods();
for (Method method : methods) {
// Ищем методы помеченные @Bean
if (method.isAnnotationPresent(Bean.class)) {
registerBeanMethod(method, configInstance);
}
}
} catch (Exception e) {
throw new RuntimeException(
"Ошибка создания конфигурации: " + configClass.getName(), e);
}
}
/**
* Регистрируем одну @Bean переменную
*/
private void registerBeanMethod(Method method, Object configInstance) {
Bean beanAnnotation = method.getAnnotation(Bean.class);
// Определяем имя бина (или используем имя метода)
String beanName = !beanAnnotation.name().isEmpty()
? beanAnnotation.name()
: method.getName();
// Возвращаемый тип метода — это тип бина
Class<?> beanClass = method.getReturnType();
// Создаем определение бина
BeanDefinition definition = new BeanDefinition(
beanName,
beanClass,
method,
configInstance
);
// Анализируем зависимости метода (его параметры)
Parameter[] parameters = method.getParameters();
for (Parameter param : parameters) {
// Параметры метода — это зависимости
// Их имена совпадают с именами других бинов
definition.getDependencies().add(param.getName());
}
// Регистрируем определение
beanDefinitions.put(beanName, definition);
System.out.println("Зарегистрирован бин: " + beanName +
" (" + beanClass.getSimpleName() + ")");
}
/**
* Инициализируем все singleton бины
*/
private void initializeSingletons() {
for (BeanDefinition definition : beanDefinitions.values()) {
Object instance = createBean(definition);
singletons.put(definition.getBeanName(), instance);
definition.setInstance(instance);
}
}
/**
* Создаем экземпляр бина, разрешая его зависимости
*/
private Object createBean(BeanDefinition definition) {
try {
Method factoryMethod = definition.getFactoryMethod();
// Получаем параметры метода
Parameter[] parameters = factoryMethod.getParameters();
Object[] args = new Object[parameters.length];
// Разрешаем зависимости
for (int i = 0; i < parameters.length; i++) {
Parameter param = parameters[i];
String paramName = param.getName();
Class<?> paramType = param.getType();
// Ищем бин с таким именем или типом
Object dependency = getBean(paramName, paramType);
if (dependency == null) {
throw new IllegalArgumentException(
"Не найдена зависимость: " + paramName +
" для бина: " + definition.getBeanName());
}
args[i] = dependency;
}
// Вызываем фабричный метод с разрешенными зависимостями
factoryMethod.setAccessible(true);
Object instance = factoryMethod.invoke(
definition.getConfigInstance(), args);
System.out.println("Создан бин: " + definition.getBeanName());
return instance;
} catch (Exception e) {
throw new RuntimeException(
"Ошибка создания бина: " + definition.getBeanName(), e);
}
}
/**
* Получаем бин по имени или типу
*/
public Object getBean(String name) {
return singletons.get(name);
}
public <T> T getBean(Class<T> type) {
for (Object bean : singletons.values()) {
if (type.isInstance(bean)) {
return (T) bean;
}
}
return null;
}
/**
* Вспомогательный метод для разрешения зависимостей
*/
private Object getBean(String name, Class<?> type) {
// Сначала ищем по имени
if (singletons.containsKey(name)) {
return singletons.get(name);
}
// Затем ищем по типу
for (Object bean : singletons.values()) {
if (type.isInstance(bean)) {
return bean;
}
}
return null;
}
/**
* Закрываем контекст и освобождаем ресурсы
*/
public void close() {
singletons.clear();
beanDefinitions.clear();
configurationInstances.clear();
}
}
Шаг 4: Пример использования
// Определяем конфигурацию
@Configuration
public class AppConfig {
@Bean
public UserRepository userRepository() {
return new UserRepository();
}
@Bean
public UserService userService(UserRepository userRepository) {
// Spring найдет userRepository в контексте и передаст сюда
return new UserService(userRepository);
}
@Bean(name = "primaryDataSource")
public DataSource dataSource() {
return new PostgresDataSource();
}
}
// Классы для примера
class UserRepository {
public User findById(Long id) {
return new User(id, "John");
}
}
class UserService {
private UserRepository repository;
public UserService(UserRepository repository) {
this.repository = repository;
}
public User getUser(Long id) {
return repository.findById(id);
}
}
class User {
Long id;
String name;
public User(Long id, String name) {
this.id = id;
this.name = name;
}
}
class DataSource {}
class PostgresDataSource extends DataSource {}
// Использование контекста
public class Main {
public static void main(String[] args) {
// Создаем контекст и регистрируем конфигурацию
SimpleApplicationContext context =
new SimpleApplicationContext(AppConfig.class);
// Получаем бин
UserService userService = context.getBean("userService", UserService.class);
User user = userService.getUser(1L);
System.out.println("User: " + user.name);
// Закрываем контекст
context.close();
}
}
// Вывод:
// Зарегистрирован бин: userRepository (UserRepository)
// Зарегистрирован бин: userService (UserService)
// Зарегистрирован бин: primaryDataSource (PostgresDataSource)
// Создан бин: userRepository
// Создан бин: userService
// Создан бин: primaryDataSource
// User: John
Шаг 5: Дополнительные возможности
Поддержка Lazy инициализации
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Lazy {
}
// В контексте:
private Object createBean(BeanDefinition definition) {
if (definition.getFactoryMethod().isAnnotationPresent(Lazy.class)) {
// Создаем бин только при первом запросе
return new LazyBeanProxy(definition);
}
// ... обычная инициализация
}
Поддержка Prototype бинов
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
String value();
// SINGLETON, PROTOTYPE, etc.
}
Обработка зависимостей через поля
private void injectFieldDependencies(Object instance) {
Class<?> clazz = instance.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Autowired.class)) {
Object dependency = getBean(field.getType());
field.setAccessible(true);
field.set(instance, dependency);
}
}
}
Ключевые принципы реализации
- Reflection API: Используем для анализа аннотаций и вызова методов
- Dependency Injection: Разрешаем зависимости перед инстанцированием
- Singleton Pattern: Кэшируем уже созданные бины
- Inversion of Control: Контекст управляет жизненным циклом объектов
- Lazy Initialization: Бины создаются при необходимости
Различия с реальным Spring
Реальный Spring значительно сложнее:
- Поддержка PostConstruct / PreDestroy callbacks
- Bean lifecycle hooks (InitializingBean, DisposableBean)
- Automatic type-based wiring
- Circular dependency detection
- Aspect-Oriented Programming (AOP)
- Conditional beans (@ConditionalOnProperty)
- Property placeholder resolution
- Event publishing
Но базовый механизм остается тем же: сканирование аннотаций -> создание определений -> разрешение зависимостей -> инстанцирование объектов.
Эта реализация демонстрирует фундаментальные концепции, на которых построен Spring Framework.