Chapter 2. The IoC container

Inversion Of Control (IoC), also known as dependency injection is more of an architectural concept than a simple coding pattern.

The idea is to decouple classes that depend on each other from inheriting other dependencies, and instead link them only at the interfacing level. This requires some sort of 3rd party software module to instantiate the concrete objects and "inject" them into the class that needs to call them.

In Spring, there are certain classes whose instances form the backbone of your application and that are managed by the Spring IoC container. While Spring Java calls them beans, Spring Python and Spring for .NET call them objects. An object is simply a class instance that was instantiated, assembled and otherwise managed by a Spring IoC container instead of directly by your code; other than that, there is nothing special about a object. It is in all other respects one of probably many objects in your application. These objects, and the dependencies between them, are reflected in the configuration meta-data used by a container.

The following diagram demonstrates a key Spring concept: building useful services on top of simple objects, configured through a container's set of blueprints, provides powerful services that are easier to maintain.

This chapter provides the basics of Spring Python's IoC container by using examples with explanations. If you are familiar with Spring Java, then you may notice many similarities. Also, this document points out key differences. It shows how to define the objects, read them into a container, and then fetch the objects into your code.

2.1. Container

A container is an object you create in your code that receives the definitions for objects from various sources. Your code goes to the container to request the object, and the container then does everything it needs to create an instance of that.

Depending on the scope of the object definition, the container may create a new instance right there on the spot, or it may fetch a reference to a singleton instance that was created previously. If this is the first time a singleton-scoped object is requested, is created, stored, and then returned to you. For a prototype-scoped object, EVERY TIME you request an object, a new instance is created and NOT stored in the singleton cache.

Containers depend on various object factories to do the heavy lifting of construction, and then itself will set any additional properties. There is also the possibility of additional behavior outside of object creation, which can be defined by extending the ObjectContainer class.

The reason it is called a container is the idea that you are going to a central place to get your top level object. While it is also possible to get all your other objects, the core concept of dependency injection is that below your top-most object, all the other dependencies have been injected and thus not require container access. That is what we mean when we say most of your code does NOT have to be Spring Python-aware.

[Note]Present vs. Future Object Containers

Pay special note that there is no fixed requirement that a container actually be in a certain location. While the current solution is memory based, meaning your objects will be lost when your application shuts down, there is always the possibility of implementing some type of distributed, persistent object container. For example, it is within the realm of possibilities to implement a container that utilizes a back-end database to "contain" things or utilizes some distributed memory cache spread between nodes.

2.1.1. ObjectContainer vs. ApplicationContext

The name of the container is ObjectContainer. Its job is to pull in object meta-data from various sources, and then call on related object factories to create the objects. In fact, this container is capable of receiving object definitions from multiple sources, each of differing types such as XML, python code, and other future formats.

The following block of code shows an example of creating an object container, and then pulling an object out of the container.

from springpython.context import ApplicationContext
from springpython.config import XMLConfig

container = ApplicationContext(XMLConfig("app-context.xml"))
service = container.get_object("sampleService")

The first thing you may notice is the fact that ApplicationContext was used instead of ObjectContainer. ApplicationContext is a subclass of ObjectContainer, and is typically used because it also performs additional pre- and post-creational logic.

For example, any object that implements ApplicationContextAware will have an additional app_context attribute added, populated with a copy of the ApplicationContext. If your object's class extends ObjectPostProcessor and defines a post_process_after_initialization, the ApplicationContext will run that method against every instance of that object.

2.1.2. Scope of Objects / Lazy Initialization

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, how long before they are destroyed, and whether or not to create them when the container is first started up.

Currently, two scopes are supported: SINGLETON and PROTOTYPE. A singleton-scoped object is cached in the container until application 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.

The default policy for the container is to make everything SINGLETON and also eagerly fetch all objects when the container is first created. The scope for each object can be individually overriden. Also, the initialization of each object can be shifted to "lazy", whereby the object is not created until the first time it is fetched or referenced by another object.

2.2. Configuration

Spring Python support different formats for defining objects. This project first began using the format defined by PyContainer, a now inactive project. The structure has been captured into an XSD spec. This format is primarily to support legacy apps that have already been built with Spring Python from its inception. There is no current priority to extend this format any further. Any new schema developments will be happening with XMLConfig

In the spirit of Spring JavaConfig and a blog posting by Rod Johnson, another format has been defined. By extending PythonConfig and using the @Object python decorator, objects may be defined with pure python code in a centralized class.

Due to limitations in the format of PyContainer, another schema has been developed called XMLConfig that more closely models the original Spring Java version. It has support for referenced objects in many more places than PyContainer could handle, inner objects as well, various collections (lists, sets, frozen sets, tuples, dictionaries, and java-style props), and values.

Spring Python is ultimately about choice, which is why developers may extend the Config class to define their own object definition scanner. By plugging an instance of their scanner into ApplicationContext, definitions can result in instantiated objects.

You may be wondering, amidst all these choices, which one to pick? Here are some suggestions based on your current solution space:

  • New projects are encouraged to pick either XMLConfig or PythonConfig based on your preference for XML vs. pure python coding.

  • Projects migrating from Spring Java can use SpringJavaConfig to ease transition, with a long term goal of migrating to XMLConfig, and perhaps finally PythonConfig.

  • Apps already developed with Spring Python can use PyContainerConfig to keep running, but it is highly suggested you work towards XMLConfig.

  • Projects currently using XMLConfig should be pretty easy to migrate to PythonConfig, since it is basically a one-to-one translation. The pure python configuration may turn out much more compact, especially if you are using lists, sets, dictionaries, and props.

2.2.1. XMLConfig - Spring Python's native XML format

XMLConfig is a class that scans object definitions stored in the new XML format defined for Spring Python. It looks very similar to Spring Java's 2.5 XSD spec, with some small changes.

The following is a simple definition of objects. Later sections will show other options you have for wiring things together.

<?xml version="1.0" encoding="UTF-8"?>
<objects xmlns="http://www.springframework.org/springpython/schema/objects"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/springpython/schema/objects
       		http://springpython.webfactional.com/schema/context/spring-python-context-1.0.xsd">
       		
	<object id="MovieLister" class="springpythontest.support.testSupportClasses.MovieLister" scope="prototype">
		<property name="finder" ref="MovieFinder"/>
		<property name="description"><ref object="SingletonString"/></property> 
	</object>
	
	<object id="MovieFinder" class="springpythontest.support.testSupportClasses.ColonMovieFinder" scope="singleton">
		<property name="filename"><value>support/movies1.txt</value></property>
	</object>
	
	<object id="SingletonString" class="springpythontest.support.testSupportClasses.StringHolder">
		<property name="str" value="There should only be one copy of this string"></property>
	</object>
</objects>

The definitions stored in this file are fed to an XMLConfig instance which scans it, and then sends the meta-data to the ApplicationContext. Then, when the application code requests an object named MovieLister from the container, the container utilizes an object factory to create the object and return it.

from springpython.context import ApplicationContext
from springpython.config import XMLConfig

container = ApplicationContext(XMLConfig("app-context.xml"))
service = container.get_object("MovieLister")

2.2.1.1. Referenced Objects

A referenced object is where an object is needed, but instead of providing the definition right there, there is, instead, a name, referring to another object definition.

Object definitions can refer to other objects in many places including: properties, constructor arguments, and objects embedded inside various collections. This is the way to break things down into smaller pieces. It also allows you more efficiently use memory and guarantee different objects are linked to the same backend object.

The following fragment, pulled from the earlier example, shows two different properties referencing other objects. It demonstrates the two ways to refer to another object.

<object id="MovieLister" class="springpythontest.support.testSupportClasses.MovieLister" scope="prototype">
	<property name="finder" ref="MovieFinder"/>
	<property name="description"><ref object="SingletonString"/></property> 
</object>

This means that instead of defining the object meant to be injected into the description property right there, the container must look elsewhere amongst its collection of object definitions for an object named SingletonString.

[Note]Referenced objects don't have to be in same configuration

When a referenced object is encountered, finding its definition is referred back to the container. This means ANY of the input sources provided to the container can hold this definition, REGARDLESS of format.

[Note]Spring Python ONLY supports global references

While Spring Java has different levels of reference like parent, local, and global, Spring Python only supports global at this time.

In the following subsections, other types of object definitions are given. Each will also include information about embedding reference objects.

2.2.1.2. Inner Objects

Inner objects are objects defined inside another structure, and not at the root level of the XML. The following shows an alternative configuration of a MovieLister where the finder uses a named inner object.

<object id="MovieLister3" class="springpythontest.support.testSupportClasses.MovieLister">
	<property name="finder">
		<object id="named" class="springpythontest.support.testSupportClasses.ColonMovieFinder">
			<property name="filename"><value>support/movies1.txt</value></property>
		</object>
	</property>
	<property name="description"><ref object="SingletonString"/></property>
</object>

The ColonMovieFinder is indeed an inner object because it was defined inside the MovieLister3 object. Objects defined at the top level have a container-level name that matches their id value. In this case, asking the container for a copy of MovieLister3 will yield the top level object. However, named objects develop a path-like name based on where they are located. In this case, the inner ColonMovieFinder object will have a container-level name of MovieLister3.finder.named.

Typically, neither your code nor other object definitions will have any need to reference MovieLister3.finder.named, but there may be cases where you need this. The id attribute of ColonMovieFinder can be left out (it is optional for inner objects) like this:

<object id="MovieLister2" class="springpythontest.support.testSupportClasses.MovieLister">
	<property name="finder">
		<object class="springpythontest.support.testSupportClasses.ColonMovieFinder">
			<property name="filename"><value>support/movies1.txt</value></property>
		</object>
	</property>
	<property name="description"><ref object="SingletonString"/></property>
</object>

That is slightly more compact, and usually alright because you usually wouldn't access this object from anywhere. However, if you must, the name in this case is MovieLister2.finder.<anonymous> indicating an anonymous object.

It is important to realize that inner objects have all the same privileges as top-level objects, meaning that they can also utilize reference objects, collections, and inner objects themselves.

2.2.1.3. Collections

Spring Java supports many types of collections, including lists, sets, frozen sets, maps, tuples, and java-style properties. Spring Python supports these as well. The following configuration shows usage of dict, list, props, set, frozenset, and tuple.

<object id="ValueHolder" class="springpythontest.support.testSupportClasses.ValueHolder">
	<constructor-arg><ref object="SingletonString"/></constructor-arg>
	<property name="some_dict">
		<dict>
			<entry><key><value>Hello</value></key><value>World</value></entry>
			<entry><key><value>Spring</value></key><value>Python</value></entry>
			<entry><key><value>holder</value></key><ref object="SingletonString"/></entry>
			<entry><key><value>another copy</value></key><ref object="SingletonString"/></entry>
		</dict>
	</property>
	<property name="some_list">
		<list>
			<value>Hello, world!</value>
			<ref object="SingletonString"/>
			<value>Spring Python</value>
		</list>
	</property>
	<property name="some_props">
		<props>
			<prop key="administrator">administrator@example.org</prop>
			<prop key="support">support@example.org</prop>
			<prop key="development">development@example.org</prop>
		</props>
	</property>
	<property name="some_set">
		<set>
			<value>Hello, world!</value>
			<ref object="SingletonString"/>
			<value>Spring Python</value>
		</set>
	</property>
	<property name="some_frozen_set">
		<frozenset>
			<value>Hello, world!</value>
			<ref object="SingletonString"/>
			<value>Spring Python</value>
		</frozenset>
	</property>
	<property name="some_tuple">
		<tuple>
			<value>Hello, world!</value>
			<ref object="SingletonString"/>
			<value>Spring Python</value>
		</tuple>
	</property>        
</object>
  • some_dict is a python dictionary with four entries.

  • some_list is a python list with three entries.

  • some_props is also a python dictionary, containing three values.

  • some_set is an instance of python's mutable set.

  • some_frozen_set is an instance of python's frozen set.

  • some_tuple is a python tuple with three values.

[Note]Java uses maps, Python uses dictionaries

While java calls key-based structures maps, python calls them dictionaries. For this reason, the code fragment shows a "dict" entry, which is one-to-one with Spring Java's "map" definition.

Java also has a Property class. Spring Python translates this into a python dictionary, making it more like an alternative to the configuring mechanism of dict.

2.2.1.4. Constructors

Python functions can have both positional and named arguments. Positional arguments get assembled into a tuple, and named arguments are assembled into a dictionary, before being passed to a function call. Spring Python takes advantage of that option when it comes to constructor calls. The following block of configuration data shows defining positional constructors.

<object id="AnotherSingletonString" class="springpythontest.support.testSupportClasses.StringHolder">
	<constructor-arg value="attributed value"/>
</object>

<object id="AThirdSingletonString" class="springpythontest.support.testSupportClasses.StringHolder">
	<constructor-arg><value>elemental value</value></constructor-arg>
</object>

Spring Python will read these and then feed them to the class constructor in the same order as shown here.

The following code configuration shows named constructor arguments. Spring Python converts these into keyword arguments, meaning it doesn't matter what order they are defined.

<object id="MultiValueHolder" class="springpythontest.support.testSupportClasses.MultiValueHolder">
	<constructor-arg name="a"><value>alt a</value></constructor-arg>
	<constructor-arg name="b"><value>alt b</value></constructor-arg>
</object>

<object id="MultiValueHolder2" class="springpythontest.support.testSupportClasses.MultiValueHolder">
	<constructor-arg name="c"><value>alt c</value></constructor-arg>
	<constructor-arg name="b"><value>alt b</value></constructor-arg>
</object>

This was copied from the code's test suite, where a test case is used to prove that order doesn't matter. It is important to note that positional constructor arguments are fed before named constructors, and that overriding a the same constructor parameter both by position and by name is not allowed by Python, and will in turn, generate a run-time error.

It is also valuable to know that you can mix this up and use both.

2.2.1.5. Values

For those of you that used Spring Python before XMLConfig, you may have noticed that expressing values isn't as succinct as the old format. A good example of the old PyContainer format would be:

<component id="user_details_service" class="springpython.security.userdetails.InMemoryUserDetailsService">
	<property name="user_dict">
		{
			"basichiblueuser"  : ("password1", ["ROLE_BASIC", "ASSIGNED_BLUE",   "LEVEL_HI"], True),
			"basichiorangeuser": ("password2", ["ROLE_BASIC", "ASSIGNED_ORANGE", "LEVEL_HI"], True),
			"otherhiblueuser"  : ("password3", ["ROLE_OTHER", "ASSIGNED_BLUE",   "LEVEL_HI"], True),
			"otherhiorangeuser": ("password4", ["ROLE_OTHER", "ASSIGNED_ORANGE", "LEVEL_HI"], True),
			"basicloblueuser"  : ("password5", ["ROLE_BASIC", "ASSIGNED_BLUE",   "LEVEL_LO"], True),
			"basicloorangeuser": ("password6", ["ROLE_BASIC", "ASSIGNED_ORANGE", "LEVEL_LO"], True),
			"otherloblueuser"  : ("password7", ["ROLE_OTHER", "ASSIGNED_BLUE",   "LEVEL_LO"], True),
			"otherloorangeuser": ("password8", ["ROLE_OTHER", "ASSIGNED_ORANGE", "LEVEL_LO"], True)
		}
	</property>
</component>
[Note]Why do I see components and not objects?

In the beginning, PyContainer was used and it tagged the managed instances as components. After replacing PyContainer with a more sophisticated IoC container, the instances are now referred to as objects, however, to maintain this legacy format, you will see component tags inside PyContainerConfig-based definitions.

While this is very succinct for expressing definitions using as much python as possible, that format makes it very hard to embed referenced objects and inner objects, since PyContainerConfig uses python's eval method to convert the material.

The following configuration block shows how to configure the same thing for XMLConfig.

<object id="user_details_service" class="springpython.security.userdetails.InMemoryUserDetailsService">
	<property name="user_dict">
		<dict>
			<entry>
				<key><value>basichiblueuser</value></key>
				<value><tuple>
					<value>password1</value>
					<list><value>ROLE_BASIC</value><value>ASSIGNED_BLUE</value><value>LEVEL_HI</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>basichiorangeuser</value></key>
				<value><tuple>
					<value>password2</value>
					<list><value>ROLE_BASIC</value><value>ASSIGNED_ORANGE</value><value>LEVEL_HI</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>otherhiblueuser</value></key>
				<value><tuple>
					<value>password3</value>
					<list><value>ROLE_OTHER</value><value>ASSIGNED_BLUE</value><value>LEVEL_HI</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>otherhiorangeuser</value></key>
				<value><tuple>
					<value>password4</value>
					<list><value>ROLE_OTHER</value><value>ASSIGNED_ORANGE</value><value>LEVEL_HI</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>basicloblueuser</value></key>
				<value><tuple>
					<value>password5</value>
					<list><value>ROLE_BASIC</value><value>ASSIGNED_BLUE</value><value>LEVEL_LO</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>basicloorangeuser</value></key>
				<value><tuple>
					<value>password6</value>
					<list><value>ROLE_BASIC</value><value>ASSIGNED_ORANGE</value><value>LEVEL_LO</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>otherloblueuser</value></key>
				<value><tuple>
					<value>password7</value>
					<list><value>ROLE_OTHER</value><value>ASSIGNED_BLUE</value><value>LEVEL_LO</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>otherloorangeuser</value></key>
				<value><tuple>
					<value>password8</value>
					<list><value>ROLE_OTHER</value><value>ASSIGNED_ORANGE</value><value>LEVEL_LO</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
		</dict>
	</property>
</object>

Of course this is more verbose than the previous block. However, it opens the door to having a much higher level of detail:

<object id="user_details_service2" class="springpython.security.userdetails.InMemoryUserDetailsService">
	<property name="user_dict">
		<list>
			<value>Hello, world!</value>
			<dict>
				<entry>
					<key><value>yes</value></key>
					<value>This is working</value>
				</entry>
				<entry>
					<key><value>no</value></key>
					<value>Maybe it's not?</value>
				</entry>
			</dict>
			<tuple>
				<value>Hello, from Spring Python!</value>
				<value>Spring Python</value>
				<dict>
					<entry>
						<key><value>yes</value></key>
						<value>This is working</value>
					</entry>
					<entry>
						<key><value>no</value></key>
						<value>Maybe it's not?</value>
					</entry>
				</dict>
				<list>
					<value>This is a list element inside a tuple.</value>
					<value>And so is this :)</value>
				</list>
			</tuple>
			<set>
				<value>1</value>
				<value>2</value>
				<value>1</value>
			</set>
			<frozenset>
				<value>a</value>
				<value>b</value>
				<value>a</value>
			</frozenset>
		</list>
	</property>
</object>

2.2.2. PythonConfig and @Object - decorator-driven configuration

By defining a class that extends PythonConfig and using the @Object decorator, you can wire your application using pure python code.

from springpython.config import PythonConfig
from springpython.config import Object
from springpython.context import scope

class MovieBasedApplicationContext(PythonConfig):
    def __init__(self):
        super(MovieBasedApplicationContext, self).__init__()
        
    @Object(scope.PROTOTYPE)
    def MovieLister(self):
        lister = MovieLister()
        lister.finder = self.MovieFinder()
        lister.description = self.SingletonString()
        self.logger.debug("Description = %s" % lister.description)
        return lister
    
    @Object(scope.SINGLETON)
    def MovieFinder(self):
        return ColonMovieFinder(filename="support/movies1.txt")
    
    @Object    # scope.SINGLETON is the default
    def SingletonString(self):
        return StringHolder("There should only be one copy of this string")
    
    def NotExposed(self):
        pass

As part of this example, the method NotExposed is also shown. This indicates that using get_object won't fetch that method, since it isn't considered an object.

By using pure python, you don't have to deal with any XML. If you look closely, you will notice that the container code below is only different in the line actually creating the container. Everything else is the same.

from springpython.context import ApplicationContext

container = ApplicationContext(MovieBasedApplicationContext())
service = container.get_object("MovieLister")

2.2.3. PyContainerConfig - Spring Python's original XML format

PyContainerConfig is a class that scans object definitions stored in the format defined by PyContainer, which was the original XML format used by Spring Python to define objects.

An important thing to note is that PyContainer used the term component, while Spring Python uses object. In order to support this legacy format, component will show up in PyContainerConfig-based configurations.

[Note]PyContainer's format is deprecated

PyContainer's format and the original parser was useful for getting this project started. However, it has shown its age by not being easy to revise nor extend. So this format is being retired. This parser is solely provided to help sustain existing Spring Python apps until they can migrate to the new XMLConfig format.

<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://www.springframework.org/springpython/schema/pycontainer-components"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/springpython/schema/pycontainer-components
       		http://springpython.webfactional.com/schema/context/spring-python-pycontainer-context-1.0.xsd">

    <component id="MovieLister" class="springpythontest.support.testSupportClasses.MovieLister" scope="prototype">
	    <property name="finder" local="MovieFinder"/>
	    <property name="description" local="SingletonString"/>
	</component>

	<component id="MovieFinder" class="springpythontest.support.testSupportClasses.ColonMovieFinder" scope="singleton">
		<property name="filename">"support/movies1.txt"</property>
	</component>
    
    <component id="SingletonString" class="springpythontest.support.testSupportClasses.StringHolder">
    	<property name="str">"There should only be one copy of this string"</property>
    </component>
</components>

The definitions stored in this file are fed in to a PyContainerConfig which scans it, and then sends the meta-data to the ApplicationContext. Then, when the application code requests an object named "MovieLister" from the container, the container utilizes an object factory to create an object and return it.

from springpython.context import ApplicationContext
from springpython.config import PyContainerConfig

container = ApplicationContext(PyContainerConfig("app-context.xml"))
service = container.get_object("MovieLister")

2.2.4. SpringJavaConfig

The SpringJavaConfig is a class that scans object definitions stored in the format defined by the Spring Framework's original java version. This makes it even easier to migrate parts of an existing Spring Java application onto the Python platform.

[Note]This is about configuring python objects NOT java objects

It is important to point out that this has nothing to do with configuring java-backed beans from Spring Python, or somehow injecting java-backed beans magically into a python object. This is PURELY for configuring python-backed objects using a format that was originally designed for pure java beans.

When ideas like "converting java to python" are mentioned, it is meant that re-writing certain parts of your app in python would require a similar IoC configuration, however, for the java and python parts to integrate, you must utilize interoperable solutions like web service or other remoting technologies.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

	<bean id="MovieLister" class="springpythontest.support.testSupportClasses.MovieLister" scope="prototype">
		<property name="finder" ref="MovieFinder"/>
		<property name="description"><ref bean="SingletonString"/></property>
	</bean>
	
	<bean id="MovieFinder" class="springpythontest.support.testSupportClasses.ColonMovieFinder" scope="singleton">
		<property name="filename"><value>support/movies1.txt</value></property>
	</bean>
	
	<bean id="SingletonString" class="springpythontest.support.testSupportClasses.StringHolder">
		<property name="str" value="There should only be one copy of this string"></property>
	</bean>
</beans>

The definitions stored in this file are fed in to a SpringJavaConfig which scans it, and then sends the meta-data to the ApplicationContext. Then, when the application code requests an object named "MovieLister" from the container, the container utilizes an object factory to create an object and return it.

from springpython.context import ApplicationContext
from springpython.config import SpringJavaConfig

container = ApplicationContext(SpringJavaConfig("app-context.xml"))
service = container.get_object("MovieLister")

Again, the only difference in your code is using SpringJavaConfig instead of PyContainerConfig on one line. Everything is the same, since it is all inside the ApplicationContext.

[Note]What parts of Spring Java configuration are supported?

It is important to note that only spring-beans-2.5 has been tested at this point in time. It is possible that older versions of the XSD spec may also work.

Spring Java's other names spaces, like tx and aop, probably DON'T work. They haven't been tested, and there is no special code that will utilize their feature set.

How much of Spring Java will be supported? That is an open question, best discussed on Spring Python's community forum. Basically, this is meant to ease current Java developers into Spring Python and/or provide a means to split up objects to support porting parts of your application into Python. There isn't any current intention of providing full blown support.

2.2.5. Mixing Configuration Modes

Spring Python also supports providing object definitions from multiple sources, and allowing them to reference each other. This section shows the same app context, but split between two different sources.

First, the XML file containing the key object that gets pulled:

<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://www.springframework.org/springpython/schema/pycontainer-components"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/springpython/schema/pycontainer-components
       		http://springpython.webfactional.com/schema/context/spring-python-pycontainer-context-1.0.xsd">

    <component id="MovieLister" class="springpythontest.support.testSupportClasses.MovieLister" scope="prototype">
	    <property name="finder" local="MovieFinder"/>
	    <property name="description" local="SingletonString"/>
	</component>

    <component id="SingletonString" class="springpythontest.support.testSupportClasses.StringHolder">
    	<property name="str">"There should only be one copy of this string"</property>
    </component>
</components>

Notice that MovieLister is referencing MovieFinder, however that object is NOT defined in this location. The definition is found elsewhere:

class MixedApplicationContext(PythonConfig):
    def __init__(self):
        super(MixedApplicationContext, self).__init__()
        
    @Object(scope.SINGLETON)
    def MovieFinder(self):
        return ColonMovieFinder(filename="support/movies1.txt")
[Note]Object ref must match function name

In this situation, an XML-based object is referencing python code by the name MovieFinder. It is of paramount importance that the python function have the same name as the referenced string.

With some simple code, this is all brought together when the container is created.

from springpython.context import ApplicationContext
from springpython.config import PyContainerConfig

container = ApplicationContext([MixedApplicationContext(),
                                PyContainerConfig("mixed-app-context.xml")])
movieLister = container.get_object("MovieLister")

In this case, the XML-based object definition signals the container to look elsewhere for a copy of the MovieFinder object, and it succeeds by finding it in MixedApplicationContext.

It is possible to switch things around, but it requires a slight change.

class MixedApplicationContext2(PythonConfig):
    def __init__(self):
        super(MixedApplicationContext2, self).__init__()
        
    @Object(scope.PROTOTYPE)
    def MovieLister(self):
        lister = MovieLister()
        lister.finder = self.app_context.get_object("MovieFinder")  # <-- only line that is different
        lister.description = self.SingletonString()
        self.logger.debug("Description = %s" % lister.description)
        return lister
    
    @Object    # scope.SINGLETON is the default
    def SingletonString(self):
        return StringHolder("There should only be one copy of this string")
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://www.springframework.org/springpython/schema/pycontainer-components"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/springpython/schema/pycontainer-components
       		http://springpython.webfactional.com/schema/context/spring-python-pycontainer-context-1.0.xsd">

	<component id="MovieFinder" class="springpythontest.support.testSupportClasses.ColonMovieFinder" scope="singleton">
		<property name="filename">"support/movies1.txt"</property>
	</component>

</components>

An XML-based object definition can refer to a @Object by name, however, the python code has to change its direct function call to a container lookup, otherwise it will fail.

[Note]PythonConfig is ApplicationContextAware

In order to perform a get_object, the configuration needs a handle on the surrounding container. The base class PythonConfig provides this, so that you can easily look for any object (local or not) by using self.app_context.get_object("name")

2.3. Object Factories

Spring Python offers two types of factories, ReflectiveObjectFactory and PythonObjectFactory. These classes should rarely be used directly by the developer. They are instead used by the different types of configuration scanners.

2.4. Testable Code

One key value of using the IoC container is the how you can isolate parts of your code for better testing. Imagine you had the following configuration:

from springpython.config import *
from springpython.context import *

class MovieBasedApplicationContext(PythonConfig):
    def __init__(self):
        super(MovieBasedApplicationContext, self).__init__()
        
    @Object(scope.PROTOTYPE)
    def MovieLister(self):
        lister = MovieLister()
        lister.finder = self.MovieFinder()
        lister.description = self.SingletonString()
        self.logger.debug("Description = %s" % lister.description)
        return lister
    
    @Object(scope.SINGLETON)
    def MovieFinder(self):
        return ColonMovieFinder(filename="support/movies1.txt")
    
    @Object    # scope.SINGLETON is the default
    def SingletonString(self):
        return StringHolder("There should only be one copy of this string")

To inject a test double for MovieFinder, your test code would only have to extend the class and override the MovieFinder method, and replace it with your stub or mock object. Now you have a nicely isolated instance of MovieLister.

class MyTestableAppContext(MovieBasedApplicationContext):
    def __init__(self):
        super(MyTestableAppContext, self).__init__()
        
    @Object
    def MovieFinder(self):
        return MovieFinderStub()