Context Dependency Injection – Are you doing it right?
Context Dependency Injection – Are you doing it right?
8. Juli 2014 Keine Kommentare zu Context Dependency Injection – Are you doing it right?CDI specification allows Field, Property (aka setter) and constructor injection. Like many developers out there, I also habitually use field injection. I must admit I never gave enough thought about it. But recently I saw a bean with more than 10 injection points and started to ponder over Dependency Injection.
The following blog post discusses why you should prefer constructor injection over field or setter injection. In the later part of the post, we will see how jQAssistant can be used to find beans with too many injection points or to enforce use of constructor injection.
public class DefectServiceImpl implements DefectService { // @Inject annotation on field @Inject private DefectRepository defectRepository; public DefectServiceImpl() { } ... }
PROS:
- Apparently intuitive to many developers
CONS:
- Hard to test
- Beans can silently grow fat with too many dependencies
public class DefectServiceImpl implements DefectService { private DefectRepository defectRepository; public DefectServiceImpl() { } // @Inject annotation on setter @Inject public void setDefectRepository(DefectRepository defectRepository) { this.defectRepository = defectRepository; } ... }
PROS:
- Dependencies can be set after construction allowing finer control over bean state. This might be handy for injecting non-mandatory collaborators.
CONS:
- Bean dependencies are not communicated clearly – no clear way to see mandatory and optional dependencies.
public class DefectServiceImpl implements DefectService { private DefectRepository defectRepository; // @Inject annotation on constructor @Inject public DefectServiceImpl(DefectRepository defectRepository) { this.defectRepository = defectRepository; } ... }
PROS:
- Ensures that when a bean instance is constructed; all mandatory collaborators are in place and bean is ready to use.
- If the number of dependencies start growing; then you get clumsy constructors which is a clear indicator of un-maintaible code. Listen to your bean; probably its saying „Refactor Me!“.
CONS:
- May be I’m bit biased by now for constructor injection; but I do not see any clear drawbacks.
Now that we have seen pros and cons of different approaches for Dependency Injection; next big challenge is to be consistent with our choices. jQAssistant to the rescue!
The current version of jQAssistant introduced CDI plugin with „Cdi:InjectionPoint“ concept. Following rule can be defined by using this concept to find out beans that are not using constructor injection.
<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0"> <constraint id="cdi:BeansMustUseConstructorInjection"> <requiresConcept refId="cdi:InjectionPoint" /> <description>All CDI beans must use constructor injection.</description> <cypher><![CDATA[ MATCH (a:Type)-[:DECLARES]->(member:Cdi:InjectionPoint) WHERE NOT member.name = "<init>" RETURN DISTINCT a.fqn AS InvalidBean ]]></cypher> </constraint> </jqa:jqassistant-rules>
If you are absolutely convinced that „Field injections are evil“; but you would like to keep it bit flexible by allowing constructor and setter injection. Following rule can be used to avoid usage of field injections.
<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0"> <constraint id="cdi:BeansMustNotUseFieldInjection"> <requiresConcept refId="cdi:InjectionPoint" /> <description>CDI beans shall not use field injection.</description> <cypher><![CDATA[ MATCH (a:Type)-[:DECLARES]->(member:Field:Cdi:InjectionPoint) RETURN DISTINCT a.fqn AS InvalidBean ]]></cypher> </constraint> </jqa:jqassistant-rules>
If you are already using field injection and not completely convinced yet about advantages of constructor injection? Take your time. But meanwhile you can always prevent the existing beans from getting cluttered up with too many dependencies.
Following query can be used to set a rule to report beans with more than 5 field injection points.
<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0"> <constraint id="cdi:BeansWithTooManyDependencies"> <requiresConcept refId="cdi:InjectionPoint" /> <description>CDI beans shall not have more than 5 field injection points.</description> <cypher><![CDATA[ MATCH (a:Type)-[:DECLARES]->(member:Field:Cdi:InjectionPoint) WITH DISTINCT a.fqn AS Bean, count(member.name) AS fieldInjectionCount WHERE fieldInjectionCount > 5 RETURN Bean AS RefactorMe, fieldInjectionCount ORDER BY fieldInjectionCount DESC ]]></cypher> </constraint> </jqa:jqassistant-rules>
Reference:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-dependency-resolution
http://spring.io/blog/2007/07/11/setter-injection-versus-constructor-injection-and-the-use-of-required
http://martinfowler.com/articles/injection.html
http://olivergierke.de/2013/11/why-field-injection-is-evil
Leave a comment