Обновить

Комментарии 18

Конструктор инжектор без мутируюшие методов это ведь про иммутабельность.

И зачем тогда столько слов?

А, понял. Java так не умеет как C#, когда record или record class сразу дают primary constructor с init-only свойствами.

public record Service(IRepository Repo, IConfig Config);

DI работает из коробки, аннотации можно ставить на параметры. Для сервисов вполне годится, не только для DTO

C#

public record UserService(IRepository Repo, ILogger Logger);

// регистрация
services.AddScoped<IRepository, SqlRepository>();
services.AddScoped<ILogger, FileLogger>();
services.AddScoped<UserService>();

// контейнер сам вызовет new UserService(repo, logger)

Java

public class UserService {
    private final Repository repo;
    private final Logger logger;

    public UserService(Repository repo, Logger logger) {
        this.repo = repo;
        this.logger = logger;
    }
}

// регистрация через аннотации на классах
@Repository
public class SqlRepository implements Repository {}

@Component
public class FileLogger implements Logger {}

@Service
public class UserService { ... }

// или явно в конфиге
@Configuration
public class AppConfig {
    @Bean
    public UserService userService(Repository repo, Logger logger) {
        return new UserService(repo, logger);
    }
}

Rust

trait Repository {
    fn save(&self, data: &str);
}

trait Logger {
    fn log(&self, msg: &str);
}

struct SqlRepository;
impl Repository for SqlRepository {
    fn save(&self, data: &str) {}
}

struct FileLogger;
impl Logger for FileLogger {
    fn log(&self, msg: &str) {}
}

struct UserService<R: Repository, L: Logger> {
    repo: R,
    logger: L,
}

fn main() {
    let service = UserService {
        repo: SqlRepository,
        logger: FileLogger,
    };
}

В шарп самый компактный вариант, в раст самый понятный, джава с танцами

Статья, в общем-то, посвящена тому, что самый компактный вариант (внедрение через поле) отнюдь не является самым лучшим.

Что, так-то понятно. Только начинающие питонисты считают однострочники самым лучшим кодом

Столько слов - потому что LLM-ка столько нагенерила. Проблема высосана из пальца.

Можно всю статью свести к одной табличке

Способ инициализации | Защита от создания невалидные объекты | Вариативность при создании объекта |
Через поля | - | - |
Через методы | - | + |
Через конструкторы | + | + |

Можно все программирование свести к одной табличке. Но глубокого понимания причин того или иного решения это не даст. Этим скорее всего и отличаются кодеры от программистов.

Думал, этот холивар закончился еще во времена Spring 4. Но статья полезная, особенно аргумент про инварианты.
Еще стоит добавить про старт контекста: конструкторы дают жесткую гарантию в момент поднятия приложения. Field injection позволяет «размазать» инициализацию и получить ошибки в рантайме.

Так это и проходит красной нитью через весь текст)

"К моменту завершения конструктора все инварианты должны быть выполнены. Объект, вышедший из конструктора, обязан быть целостным (consistent) и готовым к работе."

Это всё замечательно, но Hibernate требует no arg конструктор.

Вы абсолютно правы, JPA-сущности (@Entity) требуют конструктор по умолчанию. Но это другой тип класса с другим жизненным циклом, управляемым Hibernate, а не Spring DI. Мы говорим о сервисах, компонентах и репозиториях (@Service@Component@Repository), где вся логика работы строится на зависимостях, и их целостность критична.

LLM generated комментарий

Наверно надо начинать материться в комментариях чтобы тебя не считали ллм) Теперь вся элементарная вежливость и знание знаков препинания и правил русского языка будет относить тебя к ним...

Набор очень постараться чтобы ввести '@Service' если не знаешь специфики форматирования

О, не получилось

Вы так настойчиво продвигаете тезис, что приватное поле инициализируется через reflection, а конструктор - через new, но я вот сильно не уверен в new. Возможны два варианта: или reflection-вызов конструктора, или runtime-кодогенерация, в которой будет new. Я думаю, что используется reflection (я бы так сделал), и это несложно проверить: поставить точку останова в конструкторе и посмотреть стектрейс, но мне безумно лень. Упоминание производительности сразу было смешным: какая блин производительность инжекта, у вас же не миллиарды бинов инжектятся. А имея в виду то, что я в начале сказал, разница в производительности будет даже в пользу reflection.

Но общее моё отношение к теме статьи - а не пофиг ли, как оно инжектится? Много сильных слов: легаси, зомби, невалидное состояние, ну-ну, а это всё точно является проблемой?

Это не проблема в глобальном смысле, пока ты не начинаешь бороться с чистотой и производительностью кода. Странно то, что многие, в том числе из перечисленных в конце статей, не до конца осознают из-за чего это все происходит.

В целом наверно Ваше мнение поддерживает большинство. Но я так не хочу)

Вообще как-будто это всё на поверхности лежит? Интересно какие аргументы в пользу field injection.

Ну на поверхности или нет, но очень часто вижу, что многие этого вовсе не знали)
Аргументов в пользу field injection кроме как удобства нет)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации