Container versus Context

You may have noticed in this chapter's example how we keep referring to the code that contains our object definitions as a container, and yet, the class used to create it is called ApplicationContext. This chapter will clarify the differences between a context and a container.

Spring Python has a base class called ObjectContainer, which is responsible for managing the object definitions, creating instances of objects based on these definitions, and is largely responsible for the functionality that we have looked at so far. Any instance of this class would be a container in object oriented terms. In fact, we could substitute that class in our previous example everywhere we see ApplicationContext, and it would act mostly the same.

ObjectContainer has a subclass called ApplicationContext. An instance of ApplicationContext is a context. However, from an OOP perspective, a context is a container. So generally referring to instances of either one as an IoC container is common practice.

An ApplicationContext has some differences in behavior from an ObjectContainer as well as extra features involving life cycle management and object management. These differences will first be shown in the following table of features and will be covered in more detail later.

Class

Features

ObjectContainer

  • Lazily instantiates objects on startup with option to override
  • Objects are scoped singleton by default

ApplicationContext

  • Eagerly instantiates objects on startup with option to override
  • Objects are scoped singleton by default
  • Supports property driven objects
  • Supports post processors
  • Supports context-aware objects

Lazy objects

Earlier I said that most of the behavior in this chapter's example would be the same if we had replaced ApplicationContext with ObjectContainer. One difference is that ObjectContainer doesn't instantiate any objects when the container is first created. Instead, it waits until the object is requested to actually create it. ApplicationContext automatically instantiates all objects immediately when the container is started.

Note

In the examples so far, this difference would have little effect, because right after creating a container, we request the principle object. This won't be the case with some of the examples later in this book.

It is possible to override this by setting lazy_init to True in the object's definition. This will delay object creation until first request.

class WikiTestAppConfig(WikiProductionAppConfig):
def __init__(self):
super(WikiTestAppConfig, self).__init__()
@Object(lazy_init=True)
def data_access(self):
return StubDataAccess()

One reason for using this is if we had some object that was only needed on certain occasions and could incur a lot of overhead when created. Setting lazy_init to True would defer its creation, making it behave as an on-demand service.

This override works for both ObjectContainer and ApplicationContext.

Scoped objects

Another key duty of the container is to also manage the scope of objects. This means at what time that objects are created, where the instances are stored, and how long before they are destroyed.

Spring Python currently supports two scopes: singleton and prototype. A singleton-scoped object is cached in the container until shutdown. A prototype-scoped object is never stored, thus requiring the object factory to create a new instance every time the object is requested from the container.

Note

When a singleton object includes a prototype object as one of its initializing properties, it is important to realize that the prototype isn't recreated every time the cached singleton is used.

The default policy for the container is to make everything singleton. The scope for each object can be individually overridden as shown below.

class WikiTestAppConfig(WikiProductionAppConfig):
def __init__(self):
super(WikiTestAppConfig, self).__init__()
@Object(scope=scope.PROTOTYPE)
def data_access(self):
return StubDataAccess()

Each request for data_access will yield a different instance. This happens whether the request is from outside the container, or from another container object injecting data_access.

This override works for both ObjectContainer and ApplicationContext.

Property driven objects

ApplicationContext will invoke after_properties_set() on any object that has this method, after it has been created and all container-defined properties have been set. Here are some examples of how this can be useful:

  • Including validation logic in class definitions. If some properties are optional and others are not (or certain combinations of properties need to be set), this is our opportunity to define it, and let the container validate that an object was defined correctly.
  • Starting up background services. For example, PyroServiceExporter launches a daemon thread in the background after all properties are set.

Post processor objects

ApplicationContext will search for post processors. These are classes that manipulate other objects in the container. They extend Spring Python's ObjectPostProcessor class.

class ObjectPostProcessor(object):
def post_process_before_initialization(self, obj, obj_name):
return obj
def post_process_after_initialization(self, obj, obj_name):
return obj

When the context starts up, all non-post processors are fed one-at-a-time to each post processor twice: before the initialization and after. By default, the object is passed through with no change. By coding a custom post processor, we can override either stage, and apply any changes we wish. This can include altering the object itself, or substituting a different object.

class RemoteService(ObjectPostProcessor):
def post_process_after_initialization(self, obj, obj_name):
if obj_name.endswith("Service"):
return PyroServiceExporter(service=obj,
service_name=obj_name,
service_port=9000)
else:
return obj

This post processor looks at an object's name, and if its name ends with Service, returns a proxy that exports the object as a Pyro service, using the object's name as part of the URL. Otherwise, it simply returns the object.

This example shows how we can easily develop a convention-over-configuration client-server mechanism, simply by using Spring Python's IoC container and some custom post processors.

Context aware objects

We can define classes that get a copy of the ApplicationContext injected into the attribute app_context by extending ApplicationContextAware.

Note

This is only recommended for objects that need to manipulate the context in some fashion. It is not a good practice to use this hook to simply fetch objects, because it ties our code directly to the container.

Instead, use the principles of Dependency Injection as shown earlier in this chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.111.116