Читать книгу Beginning Spring - Höller Jürgen - Страница 12

1
POJO Programming Model, Lightweight Containers, and Inversion of Control
DEPENDENCY INJECTION

Оглавление

The fundamental principle of dependency injection is that application objects should not be responsible for looking up the resources or collaborators on which they depend. Instead, an IoC container should handle object creation and dependency injection, resulting in the externalization of resource lookup from application code to the container.

Dependency injection has several benefits to the overall system. First of all, lookup logic is completely removed from application code, and dependencies can be injected into the target component in a pluggable manner. Components don't know the location or class of their dependencies. Therefore, unit testing of such components becomes very easy because there is no environmental dependency like the JNDI context, and dependent components can easily be mocked and wired up to the component in the test case. Configuration of the application for different environments also becomes very easy and achievable without code modification because no concrete class dependencies exist within components. There is no dependence on the container API. Code can be moved from one container to another, and it should still work without any modification in the codebase. There is no requirement to implement any special interfaces at all. Written classes are just plain Java objects, and it is not necessary to deploy those components to make them run.

Two dependency injection methods can be used. One is constructor injection, and the other is setter injection. A good container should be able to support both at the same time, and should allow mixing them.

Setter Injection

The setter methods are invoked immediately after the object is instantiated by the container. The injection occurs during the component creation or initialization phase, which is performed much earlier in the process than handling business method calls. Thus, there are no threading issues related with calling those setter methods. Setter methods are part of the JavaBean specification, so that they allow the outside world to change collaborators and property values of components. Those JavaBean properties are also used to externalize simple properties such as int or boolean values. This simplifies the code and makes it reusable in a variety of environments.

The most important advantage of setter injection is that it allows re-configurability of the component after its creation. The component's dependencies can be changed at run time. Many existing classes can already be used with standard JavaBean-style programming. In other words, they offer getter and setter methods to access their properties. For example, Jakarta Commons DBCP DataSource provides a commonly used DataSource implementation, and it can be managed via its JavaBean properties within the container. It's possible to use the standard JavaBeans property-editor mechanism for type conversions whenever necessary. For example, a String value given in configuration can easily be converted into a necessary typed value, or a location can be resolved into a resource instance, and so on. If there is a corresponding getter for each setter, it becomes possible to obtain the current state of the component and save it to restore for a later time. If the component has default values for some or all of its properties, it can be configured more easily using setter injection. You can still optionally provide some dependencies of it as well.

The biggest disadvantage of setter injection is that not all necessary dependencies may be injected before use, which leaves the component in a partially configured state. In some cases, the order of invocation of setter methods might be important, and this is not expressed in the component's contract. Containers provide mechanisms to detect and prevent such inconsistencies in component states during their creation phase.

Constructor Injection

With constructor injection, beans express their dependencies via constructor arguments. In this method, dependencies are injected during component creation. The same thread safety applies for constructor injection as well. You can also inject simple properties such as int or boolean values as constructor arguments.

The biggest advantage of constructor injection is that each managed component in the container is guaranteed to be in a consistent state and ready to use after it is created. Another good point is that the amount of code written with constructor injection will be slightly less compared to the code written when setter injection is used.

The biggest disadvantage of constructor injection is that it won't be possible to reconfigure components after their creation unless they provide a setter for those properties given as constructor arguments. Having several overloaded constructors for different configuration options might be confusing or even unavailable most of the time. Concrete inheritance can also be problematic unless you are careful about overriding all of the constructors in the superclass.

Setter or Constructor Injection

Both methods have advantages as well as disadvantages, and it is not possible to use only one method for any application. You might have classes especially written by third parties that don't have constructors that accept suitable arguments for your configuration case. Therefore, you might first create a component with an available constructor that accepts arguments close to your needs, and then inject other dependencies with setter methods. If the components need to be reconfigurable at run time, having setters for their specific properties will be mandatory in that case. IoC containers are expected to allow developers to mix the two types of dependency injection methods for the same component within the application configuration.

Beginning Spring

Подняться наверх