Types of Inversion of Control

You may be wondering why there are two different types of IoC and why these types are split further into different implementations. There seems to be no clear answer to this question; certainly the different types provide a level of flexibility, but to us, it seems that IoC is more of a mixture of old and new ideas; the two different types of IoC represent this.

Dependency Lookup is a much more traditional approach and at first glance, it seems more familiar to Java programmers. Dependency Injection is a newer, less well-established approach that, although it appears counterintuitive at first, is actually much more flexible and usable than Dependency Lookup.

With Dependency Lookup–style IoC, a component must acquire a reference to a dependency, whereas with Dependency Injection, the dependencies are literally injected into the component by the IoC container. Dependency Lookup comes in two types: Dependency Pull and Contextualized Dependency Lookup (CDL). Dependency Injection also has two common flavors: Constructor Dependency Injection and Setter Dependency Injection.

Note 

For the discussions in this section, we are not concerned with how the fictional IoC container comes to know about all the different dependencies, just that at some point, it performs the actions described for each mechanism.

Dependency Pull

To a Java developer, Dependency Pull is the most familiar type of IoC. In Dependency Pull, dependencies are pulled from a registry as required. Anyone who has ever written code to access an EJB has used Dependency Pull. Spring also offers Dependency Pull as a mechanism for retrieving components the framework manages; you saw this in action in

Listing 4-1: Dependency Pull in Spring

image from book
    public static void main(String[] args) throws Exception {              // get the bean factory         BeanFactory factory = getBeanFactory();              MessageRenderer mr = (MessageRenderer) factory.getBean("renderer");         mr.render();     }
image from book

This kind of IoC is not only prevalent in J2EE-based applications, which make extensive use of JNDI lookups to obtain dependencies from a registry, but it is also pivotal to working with Spring in many environments.

Contextualized Dependency Lookup

Contextualized Dependency Lookup (CDL) is similar, in some respects, to Dependency Pull, but in CDL, lookup is performed against the container that is managing the resource, not from some central registry, and it is usually performed at some set point. CDL works by having the component implement an interface similar to that in Listing 4-2.

Listing 4-2: Component Interface for CDL

image from book
package com.apress.prospring.ch4;      public interface ManagedComponent {          public void performLookup(Container container); }
image from book

By implementing this interface, a component is signaling to the container that it wishes to obtain a dependency. When the container is ready to pass dependencies to a component, it calls performLookup() on each component in turn. The component can then look up its dependencies using the Container interface, as shown in Listing 4-3.

Listing 4-3: Obtaining Dependencies in CDL

image from book
package com.apress.prospring.ch4;      public class ContextualizedDependencyLookup implements ManagedComponent {          private Dependency dep;          public void performLookup(Container container) {         this.dep = (Dependency) container.getDependency("myDependency");     }      } 
image from book

Constructor Dependency Injection

Constructor Dependency Injection is Dependency Injection where a component's dependencies are provided to it in its constructor(s). The component declares a constructor or a set of constructors taking as arguments its dependencies, and the IoC container passes the dependencies to the component when it instantiates it, as shown in Listing 4-4.

Listing 4-4: Constructor Dependency Injection

image from book
package com.apress.prospring.ch4;      public class ConstructorInjection {          private Dependency dep;          public ConstructorInjection(Dependency dep) {         this.dep = dep;     } }
image from book

Setter Dependency Injection

In Setter Dependency Injection, the IoC container injects a component's dependencies into the component via JavaBean-style setter methods. A component's setters expose the set of the dependencies the IoC container can manage. Listing 4-5 shows a typical Setter Dependency Injection–based component.

Listing 4-5: Setter Dependency Injection

image from book
package com.apress.prospring.ch4;      public class SetterInjection {          private Dependency dep;          public void setMyDependency(Dependency dep) {         this.dep = dep;     } }
image from book

Within the container, the dependency requirement exposed by the setMyDependency() method is referred to by the JavaBeans-style name, myDependency. In practice, setter injection is the most widely used injection mechanism, and it is one of the simplest IoC mechanisms to implement.

Injection vs. Lookup

Choosing which style of IoC to use—Injection or Lookup—is not usually a difficult decision. In many cases, the type of IoC you use is mandated by the container you are using. For instance, if you are using EJB 2.0, then you must use Lookup-style IoC to obtain the EJB from the J2EE container. In Spring, aside from initial bean lookups, your components and their dependencies are always wired together using Injection-style IoC.

Note 

When you are using Spring, you can access EJB resources without needing to perform an explicit lookup. Spring can act as an adapter between Lookup- and Injection-style IoC systems, thus allowing you to manage all resources using Injection.

The real question is this: Given the choice, which method should you use, Injection or Lookup? The answer to this is most definitely Injection. If you look at the code in Listings 4-4 and 4-5, you can clearly see that using Injection has zero impact on your components' code. The Dependency Pull code, on the other hand, must actively obtain a reference to the registry and interact with it to obtain the dependencies, and using CDL requires your classes to implement a specific interface and look up all dependencies manually. When you are using Injection, the most your classes have to do is allow dependencies to be injected using either constructors or setters.

Using Injection, you are free to use your classes completely decoupled from the IoC container supplying dependent objects with their collaborators manually, whereas with Lookup, your classes are always dependent on classes and interfaces defined by the container. Another drawback with Lookup is that it becomes very difficult to test your classes in isolation from the container. Using Injection, testing your components is trivial, because you can simply provide the dependencies yourself using the appropriate constructor or setter.

Note 

For a more complete discussion of testing using Dependency Injection and Spring, refer to Appendix A.

Lookup-based solutions are, by necessity, more complex than Injection-based ones. Although complexity is nothing to be afraid of, we question the validity of adding unneeded complexity to a process as core to your application as dependency management.

All of these reasons aside, the biggest reason to choose Injection over Lookup is that it makes your life easier. You write substantially less code when you are using Injection, and the code that you do write is simple and can, in general, be automated by a good IDE. You will notice that all of the code in the Injection samples is passive, in that it doesn't actively try to accomplish a task; the most exciting thing you see in Injection code is objects getting stored in a field—not much can go wrong there! Passive code is much simpler to maintain than active code, because there is very little that can go wrong. Consider the following code taken from Listing 4-3:

    public void performLookup(Container container) {         this.dep = (Dependency) container.getDependency("myDependency");     } 

In this code, plenty could go wrong: the dependency key could change, the container instance could be null, or the returned dependency might be the incorrect type. We refer to this code as having a lot of moving parts because plenty of things can break. Using Lookup might decouple the components of your application, but it adds complexity in the additional code required to couple these components back together in order to perform any useful tasks.

Setter Injection vs. Constructor Injection

Now that we have established which method of IoC is preferable, we still need to choose whether to use setter injection or constructor injection. Constructor injection is particularly useful when you absolutely must have an instance of the dependency class before your component is used. Many containers, Spring included, provide a mechanism for ensuring that all dependencies are defined when you use setter injection, but by using constructor injection you assert the requirement for the dependency in a container-agnostic manner.

Setter injection is useful in a variety of different cases. If the component is exposing its dependencies to the container but is happy to provide its own defaults, then setter injection is usually the best way to accomplish this. Another benefit of setter injection is that it allows dependencies to be declared on an interface, although this is not as useful as you might first think. Consider a typical business interface with one business method, defineMeaningOfLife(). If, in addition to this method, you define a setter for injection such as setEncylopedia(), then you are mandating that all implementations must use or at least be aware of the encyclopedia dependency. You do not need to define this setter at all—any decent IoC container, Spring included, can work with the component in terms of the business interface but still provide the dependencies of the implementing class. An example of this may clarify this matter slightly. Consider the business interface in Listing 4-6.

Listing 4-6: The Oracle Interface

image from book
package com.apress.prospring.ch4;      public interface Oracle {          public String defineMeaningOfLife(); }
image from book

Notice that the business interface does not define any setters for dependency injection. This interface could be implemented as shown in Listing 4-7.

Listing 4-7: Implementing the Oracle Interface

image from book
package com.apress.prospring.ch4;      public class BookwormOracle implements Oracle {          private Encyclopedia enc;          public void setEncyclopedia(Encyclopedia enc) {         this.enc = enc;     }          public String defineMeaningOfLife() {         return "Encyclopedias are a waste of money - use the Internet";     }      }
image from book

As you can see, the BookwormOracle class not only implements the Oracle interface but also defines the setter for Dependency Injection. Spring is more than comfortable dealing with a structure like this—there is absolutely no need to define the dependencies on the business interface. The ability to use interfaces to define dependencies is an often-touted benefit of setter injection, but in actuality, you should strive to keep setters used solely for injection out of your business interfaces. Unless you are absolutely sure that all implementations of a particular business interface require a particular dependency, let each implementation class define its own dependencies and keep the business interface for business methods.

Although you shouldn't always place setters for dependencies in a business interface, placing setters and getters for configuration parameters in the business interface is a good idea and makes setter injection a valuable tool. We consider configuration parameters to be a special case for dependencies. Certainly your components depend on the configuration data, but configuration data is significantly different from the types of dependency you have seen so far. We will discuss the differences shortly, but for now, consider the business interface shown in Listing 4-8.

Listing 4-8: The NewsletterSender Interface

image from book
package com.apress.prospring.ch4;      public interface NewsletterSender {          public void setSmtpServer(String smtpServer);     public String getSmtpServer();          public void setFromAddress(String fromAddress);     public String getFromAddress();          public void send(); }
image from book

The NewsletterSender interface is implemented by classes that send a set of newsletters via e-mail. The send() method is the only business method, but notice that we have defined two JavaBean properties on the interface. Why are we doing this, when we just said that you shouldn't define dependencies in the business interface? The reason is that these values, the SMTP server address and the address the e-mails are sent from, are not dependencies in the practical sense; rather, they are configuration details that affect how all implementations of the NewsletterSender interface function. Spring's Dependency Injection capabilities form the ideal solution to the external configuration of application components, not for dependency provision but as a mechanism for externalizing component configuration settings. The question here then is: What is the difference between a configuration parameter and any other kind of dependency? In most cases, you can clearly see whether a dependency should be classed as a configuration parameter, but if you are not sure, look for the following three characteristics that point to a configuration parameter:

  1. Configuration parameters are passive. In the NewsletterSender example shown in Listing 4-8, the SMTP server parameter is an example of a passive dependency. Passive dependencies are not used directly to perform an action; instead, they are used internally or by another dependency to perform their actions. In the MessageRenderer example from Chapter 2, the MessageProvider dependency was not passive—it performed a function that was necessary for the MessageRenderer to complete its task.

  2. Configuration parameters are usually information, not other components. By this we mean that a configuration parameter is usually some piece of information that a component needs to complete its work. Clearly the SMTP server is a piece of information required by the NewsletterSender, but the MessageProvider is really another component that the MessageRenderer needs to function correctly.

  3. Configuration parameters are usually simple values or collections of simple values. This is really a by-product of points 1 and 2, but configuration parameters are usually simple values. In Java this means they are a primitive (or the corresponding wrapper class) or a String or collections of these values. Simple values are generally passive. This means you can't do much with a String other than manipulate the data it represents; and you almost always use these values for information purposes—for example, an int value that represents the port number that a network socket should listen on, or a String that represents the SMTP server through which an e-mail program should send messages.

When considering whether to define configuration options in the business interface, also consider whether the configuration parameter is applicable to all implementations of the business interface or just one. For instance, in the case of implementations of NewsletterSender, it is obvious that all implementations need to know which SMTP server to use when sending e-mails. However, we would probably choose to leave the configuration option that flags whether to send secure e-mail off the business interface, because not all e-mail APIs are capable of this and it is correct to assume that many implementations will not take security into consideration at all.

Note 

Recall that in Chapter 2, we chose to define the dependencies in the business purposes. This was for illustration purposes and should not be treated in any way as best practice.

Setter injection also allows you to swap dependencies for a different implementation on the fly without creating a new instance of the parent component. Currently Spring doesn't support this feature, but as soon as Spring is JMX aware, this feature will present itself. Perhaps the biggest benefit of setter injection is that it is the least intrusive of the Injection mechanisms.

If you are defining constructors for injection on a class that would otherwise just have the default constructor, then you are affecting all code that uses that class in a non-IoC environment. Extra setters that are defined on a class for IoC purposes do not affect the ability of other classes to interact with it.

In general, setter-based injection is the best choice, because it has the least effect on your code's usability in non-IoC settings. Constructor injection is a good choice when you want to ensure that dependencies are being passed to a component, but bear in mind that many containers provide their own mechanism for doing this with setter injection. Most of the code in the sample application uses setter injection, although there are a few examples of constructor injection.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net