Why is using the framework's features considered wrong

Viewed 120

I joined a company as a junior developer and I get assigned to do maintenance and fixes to various applications.

In every project that I had to work in a team I always heard complains about my code being too "framework coupled" and that I should write the code using plain Java rather than using the Spring Framework's features (heck, I even got yelled at for using @Autowired rather than Constructor Injection)

I am really frustrated by this thing and I want to know if I am in the wrong. Aren't the framework's features the main reason a programmer is going to use that framework?

2 Answers

Usually, the reasoning behind that way of thinking is that it would be easier to one fine day swap the Framework you use today to some other with the minimum required effort. You might argue that this isn't something very common or that you do very often, but still, even if the reason does not convince you it is generally a good practice to try to decouple your code as much as possible from the underlying Framework for multiple reasons (in addition to the one just mentioned you also have unit testing made easier).

In regards to the @Autowired topic, constructor injection is usually preferred for two main reasons:

  1. You clearly define the dependencies for your class to work properly;
  2. It is easier to unit test it because mocking its dependencies is way easier if you can "inject" them via the constructor.

Please do not get confused between two different concepts: "conding standards for particular project" and "best practices" - if you are a junior developer you just need to accept conding standards, if you are a team lead you should carefully evaluate whether the "best practices" are actually best. Spring Framework is not only DI-framework but it also provides a lot of other useful capabilities (externalized configs, post-processors, AOP, transaction management, converters, etc - a lot of them), if your team decided to not get coupled with Spring Framework it means they are missing a lot of features (or need to implement those features from scratch) for the sake of hypothetical possibility to switch DI-framework in the future.

As regards to the evilness of "field injection", the problem is evangelists of "constructor injection" completely leave out the fact that there are a lot of cases where it does not work at all, for example consider the following code:

@Component
@RequiredArgsConstructor
class A {
  private final B b;
}

@Component
@RequiredArgsConstructor
class B {
  private final A a;
}

DI-framework will fail to bootstrap those beans due to circular dependencies, and you have the following options to make it work:

  • do not use constructor injection
  • put "somewhere" @Lazy annotation (or create your own proxy in order to stay clear of Spring). Where to put it? Actually in both classes, because the behaviour depends on the root of dependency resolution: if we start with A then B needs to be @Lazy and vice versa. But the problem is this workaround just moves the issue from bootstrap phase to runtime
  • accept that you code smells and refactor it, but always keep in mind that bootstrap phase may fail any time, i.e. do the job that DI-framework is supposed to do
Related