20

I am interested in direct field dependency injection. Traditionally, Spring supports both constructor injection (supplying arguments to constructors) and setter-based injection (calling setters on a call).

However, Spring is also capable of direct field injection (setting member fields of an object without a setter method), as evidenced by annotating fields with @Autowired. Autowiring is limited to just "beans", so primitive values cannot be injected (although this can somewhat be circumvented by creating beans of class "java.lang.String" - this works, but has the normal caveats of autowiring.) In addition to this, Spring supports @Value to directly set values to member fields from properties etc.

Yet, Spring does not allow properties to be directly set to member fields (without autowiring).

My question is: why?

It is obviously capable of doing so, so why doesn't it? Are there any big negative side-effects that prevent this? Or is the capability somehow limited so that only autowiring makes sense? Does it need some bigger hacks than calling setters?

Note that I do not wish to discuss the relative merits of having setters and getters in general, just the reasons why Spring has made this choice.

Nakedible
  • 4,067
  • 7
  • 34
  • 40
  • 1
    I've always wondered the same things and assumed the reason was related to "making clean code" and the prevention of compiler issuing warnings around this-value-was-never-set-and-you-are-trying-to-use-it. – Andrew White Mar 25 '11 at 13:38

3 Answers3

10

The @Autowired annotation uses reflection to make private fields accessible (see this related question). I can see three things why it isn't used in Spring configuration files.

  1. Since configuration happens by bean properties (getters and setters) you can't really tell - in the likely case that both exist - if you want to e.g. call setValue or set the member value.
  2. It breaks encapsulation. Your Spring configuration has no reason to know about private member variables. With an annotation that's ok since it is already right there in the source code.
  3. Security concerns. A more restrictive security manager might not allow making private fields accessible via reflection.
Community
  • 1
  • 1
Daff
  • 43,734
  • 9
  • 106
  • 120
  • For case 1, I'd say it is trivial to prefer the setter and only go look for a field in case the setter does not exist at all. For case 2, I don't see the point if Spring is allowed to call private setters. If Spring can't call private setters, then I don't know why the same access restrictions couldn't be applied to member variables. And, I'd be fine with an annotation as well - just not autowired. For case 3, a more restrictive security manager might not allow making private setters accessible via reflection either? I don't see why the access restrictions have to be different. – Nakedible Mar 25 '11 at 13:56
  • Private setter injection is not a good idea either. I emphasize on point 2: If I access private members or methods doesn't matter it still breaks encapsulation and goes against the principles of both OOP and dependency injection. There will also still be the same security issues. – Daff Mar 25 '11 at 14:06
  • 3
    If the setter method is *public*, why should the member field not be *public* as well? Or, if the setter is *private*, how does setting a *private* member field have any more security implications? – Nakedible Mar 25 '11 at 14:08
  • http://en.wikipedia.org/wiki/Encapsulation_%28object-oriented_programming%29 : "Hiding the internals of the object protects its integrity by preventing users from setting the internal data of the component into an invalid or inconsistent state." That's why the field shouldn't be public. Now again I really consider private setter methods a bad practice and would only use them in exceptionally rare cases. – Daff Mar 25 '11 at 14:14
  • But using a public setter to achieve the exact same this hurts integrity just as much as setting the member variable! Why would there be a difference? Obviously *if* you have additional checks in your setter method, those make a difference but for the big majority of cases people do not have any additional checks - if you need the checks, *then* you can use a setter as the setter should be favoured over direct member setting. – Nakedible Mar 25 '11 at 14:16
  • 1
    This is part of the JavaBean specification: http://en.wikipedia.org/wiki/JavaBean "The class properties *must* be accessible using get, set, is" So if you make your members public without accessors you don't follow the specification and therefore don't have a JavaBean. What happens if you want to add an additional check but you are already accessing the field directly all over your code? – Daff Mar 25 '11 at 14:19
  • This degrades in to the discussion about the general merits of setters/getters. In the case of Spring, they *explicitly* mention that they support managing also other classes besides the ones that strictly adhere to JavaBeans. Also, if the variable is *only* set by Spring via dependency injection, then no code needs to be changed when adding a setter - Spring will notice it automatically and nothing needs to change. – Nakedible Mar 25 '11 at 14:24
  • I just found it easier to follow the standards and OOP best practices instead of adjusting your classes to the dependency injection framework being used. I understand your point and I think that having to use getters and setters in Java the way they are implemented is a language restriction anyway. – Daff Mar 25 '11 at 14:36
  • I'll give a reason why spring should support this: there are otherwise good libraries that have configuration classes with all public members (and no constructor args, no setters or getters). If spring does not support the feature described by OP, then I have to create wrappers just so that I can use spring. Probably you don't believe this is the case with big libraries so I will just point out: http://api.mongodb.org/java/2.6/com/mongodb/MongoOptions.html – Kevin Nov 03 '11 at 20:15
  • I'm pretty sure there is a BeanWrapper (http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/beans/BeanWrapper.html) implementation for this use case. This question was more about accessing private fields with the DI container. – Daff Nov 03 '11 at 22:38
10

I think I found the answer myself. I went over to the Spring source code and saw how the features were actually implemented. This is what I found:

Setting properties via XML is probably the oldest part of Spring, and it relies very heavily on "java.beans" classes for introspection, property enumeration, etc. And quite obviously, those do not support field introspection at all. On top of this is the type conversion machinery which determines how the property value can be converted to a suitable value for the property in question. There are no neatly separable pieces here.

All the @Autowired etc. stuff is implemented in a BeanPostProcessor, which has it's own type matching mechanic, which has nothing to do with the type conversion. That is also why it only injects beans. Same thing pretty much for @Value, it is just something that is resolved on the spot there and has nothing to do with properties.

So, adding field injection support, for properties in particular, is not a trivial engineering effort as the parts of the code that do one or the other are pretty much completely separate.

This doesn't exactly answer "Why?", but I think this is a more compelling explanation as to why Spring hasn't added direct field dependency injection than the other explanations I've heard. Unless they have something fundamental against it (which I doubt, considering that they want to allow configuration of existing third party classes, not just JavaBeans), then it's just a matter of engineering effort to get the functionality in.

Nakedible
  • 4,067
  • 7
  • 34
  • 40
1

It does support this through JSR-250 @Resource annotation (in Spring 3.0+)

I personally prefer this to setter injection, and have mixed feelings with this when considering constructor injection. Here are the considerations that I have thought about FWIW:

1) Constructor injection is a nice way to self-document your bean dependencies (pro) but creates a lot of DRY violations: (a) private field, (b) constructor argument, (c) constructor code to set the field from the parameter, (d) the additional code in the bean configuration (either in @Configuration classes or in xml). That's a LOT of DRY violations just for the sake of some encapsulation purity, which I don't even care about. This is barely a violation of encapsulation -- but it does create a large dependency on some injection container that respects JSR-250 annotations (see next)

2) Creates a dependency on JSR-250 compliant container. I have mixed feelings about this. When I first heard about @Resource I wrote it off saying it will make my system harder to test. However, I ended up using spring in my tests anyways. I can still use mock beans like I would anyways so it was never really a problem. And the question is outside of testing, when would you actually want to reuse this. In my mind if you're designing the system to take advantage of a container, then embrace it and use it. Are the dry violations actually worth the flexibility of not running inside of a container? At least with JSR-250 annotations you could run in any JEE6 environment and get injection like you want it.

3) Can create some not so great debugging scenarios if you instantiate one outside of the container: i.e. you will get null pointer exceptions instead of something nice. This is a trade-off. I personally think its not horrible -- I mean if you get a NPE on a line with a @Resource then you quickly realize it wasn't injected.

steve_ash
  • 1,184
  • 1
  • 10
  • 16