Скачать книгу

fine-grained model, which is also richer in terms of behavioral aspects.

      LIGHTWEIGHT CONTAINERS AND INVERSION OF CONTROL (IOC)

      Despite all the difficulties and disadvantages of the old EJB programming model, there were still some attractive points in the platform that caused many people to develop enterprise Java applications and deploy them into J2EE application servers. It was very important that several middleware services crucial for applications to work were readily provided by the J2EE environment, and developers were able to utilize them in their applications. For example, the following actions are independent from business logic, and it's important that they are provided by a J2EE platform:

      • Handling database connections outside the application codebase

      • Enabling pooling capabilities, if necessary

      • Performing transaction management with declarative means

      • Working with a ready-to-use transaction management infrastructure

      • Creating and wiring of components in the application

      • Applying security constraints on the system

      • Dealing with thread and scheduling issues

      Lightweight Containers

      Some people were developing their applications without using EJBs while still leveraging many of those middleware features mentioned earlier. On the other hand, they usually perceived that they had to deploy their application to a full-featured J2EE application server only so that they could leverage those middleware services. This was quite a wrong opinion at the time. It is technically possible to develop an enterprise application without using a container at all. In that case, however, you need to handle the creating and wiring of components and implement required middleware services yourself. These tasks will definitely distract you from dealing solely with business requirements of the system, and delay the completion time of it.

      Therefore, in practice it is much better to have an environment by which all those components will be created and wired and those required middleware services will be provided. Such an environment is called a container. The Java EE platform provides several such containers, each specialized with services required by a particular layer in the application. For example, the Servlet container creates and manages components of the web layer of an application, such as Servlets, JSPs, Filters, and so on. The EJB container, on the other hand, focuses on the business layer of the application and manages the EJB components of it. Similar to the Java EE platform, the Spring Container is also a container in which components of an application are created, wired with each other, and the middleware services are provided in a lightweight manner.

      When we talk about containers, it is expected that any container should be capable of providing several basic services to components managed in its environment. According to the seminal book Expert One-on-One J2EE Development Without EJB by Rod Johnson and Jürgen Höller (Wrox, 2004), those expected services can be listed as follows:

      • Life-cycle management

      • Dependency resolution

      • Component lookup

      • Application configuration

      In addition to those features, it will be very useful if the container is able to provide following middleware services:

      • Transaction management

      • Security

      • Thread management

      • Object and resource pooling

      • Remote access for components

      • Management of components through a JMX-like API

      • Extendibility and customizability of container

      A lightweight container includes all of these features, but doesn't require application code to depend on its own API. That is, it doesn't have invasive character, its startup time is very fast, it doesn't need to be deployed into a full-featured Java EE application server to be able to provide those services, and deploying components into it is a trivial process. The Spring Application Framework is one of the most prominent lightweight containers in the enterprise world.

      Inversion of Control (IoC)

      One of the most important benefits containers that provide with components they manage is pluggable architecture. Components implement some interfaces, and they also access services provided by other components they need through similar interfaces. They never know concrete implementation classes of their services. Therefore, it becomes very easy to replace any component in the system with a different implementation. The job of a container is to create those components and their dependent services and wire them together.

      Dependent components are never instantiated using a new operator within component classes. They are injected into the component by the container instance at run time. Hence, control of dependencies is moved out of components to the container. This pattern, therefore, is called Inversion of Control, or IoC for short. IoC is an important concept in frameworks generally, and is best understood through the Hollywood principle of “Don't call us; we'll call you.”

      IoC is one of the fundamental features that is expected to be provided by any container. It has basically two forms: dependency lookup and dependency injection.

      In dependency lookup, the container provides callback methods to the components it manages, and the components interact with the container and acquire their dependencies explicitly within those callback methods. In such a scenario, there is usually a lookup context that is used to access dependent components and other resources managed by the container.

      In dependency injection, components are provided with suitable constructors or setter methods so that the container can inject dependent components. There is hardly ever an explicit lookup performed within components. Most of the time dependencies are injected during creation of components through those methods.

The method used during the early years of J2EE corresponds to dependency lookup. The lookup context mentioned earlier was also called the JNDI context in this environment. EJB components and other resources such as JDBC DataSource and JMS ConnectionFactory were accessed through that JNDI context. Figure 1.3 depicts explicit interaction of various parts with the JNDI repository in the J2EE platform via JNDI API.

FIGURE 1.3

      With the advent of the Spring Application Framework and other lightweight IoC frameworks, the dependency injection method has become popular. In this scenario, how components are instantiated and what dependent components they need are defined using a container's own configuration mechanism. It is the job of the container to process this configuration information to instantiate necessary components and wire up their dependencies at run time. During the evolution process of J2EE toward Java EE, explicit dependency lookup using JNDI has been transformed into the implicit dependency injection method. Today, when IoC is mentioned, it is usually understood as dependency injection among developers.

      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.

Скачать книгу