A Quick Introduction to the Spring Python Framework, Page 2
Getting Started With Spring Python
In our example, we'll create a program that requires a logger. For whatever reason, we want to use a mock object, that can easily be replaced later. The first step is to define our Spring object, which will be injected at execution time. The original Java Spring uses "Beans", which is really just a POJO or plain old Java object. I suspect that plain old Python objects (POPOs!) probably won't catch on; the equivalent in Spring Python is simply Objects. I'll use the term Spring Object to avoid confusion. Listing 4 shows a Python class for our logger. Note, the file name is Lumber.
class Logger: def getDestination(self): return self.destination def write(self): print "writing to log:", self.msg
Listing 4. Our simple Spring Object,
Logger, in module
This Spring Object has two properties, Destination and
Msg, as well as a write method. Each property has a
Different from the Java implementation of Spring,
setter's aren't necessary. The Spring Objects
are defined in an external XML file, shown in Listing 5.
The XML format is very similar to the Java Spring
version. Each object, defined with the <object> tag,
has an id, or object name, its deriving class, and the
scope. Only two scopes are possible, prototype or singleton.
In listing 5 we define an object called logger which is of
Lumber.Logger. We use the property "destination"
with a value of
<?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="logger" class="Lumber.Logger" scope="prototype"> <property name="destination" value="/who/knows/where/loggit.txt"/> </object> </objects>
Listing 5. Our XML configuration file, applicationContext.xml, with property destination
Because we're using Python, we can dynamically add
Setter's in the Spring Object are
not necessary, although, if that's your style of programming
feel free to add those methods. Now that we have a Spring
Object, and the configuration for it, we can create the
sample app, where we'll inject the dependency,
from springpython.context import ApplicationContext from springpython.config import XMLConfig container = ApplicationContext(XMLConfig("applicationContext.xml")) logger = container.get_object("logger") print "all messages will be logged into ", logger.getDestination() try: print unboundVariableWatchMeBlowUp except: logger.msg = "A critical error has occurred of type xyz" logger.write()
Listing 6. The main program. A spring container is created
create our container, which will be used to inject the
logger defined in the XML configuration file. As you can see
from the first print message, logger is indeed the object
defined externally, and we can call it's method
getDestination(). In our sample application, we
log a message after an exception is raised. We add the
property msg, with a string value, and then call the method
In a real world application, after development of the
main program is finished, the simplistic
Lumber.Logger class can be replaced with a
complete, and sophisticated logging system. For example,
that will be able to write data over a network. The only
requirements in our sample application is that the new
logger have a
write() method (Think, design by contract).
I don't believe that DI should be used everywhere all the time, but I think there are advantages to be gained. If you didn't already know, there's a pretty viscous debate going on about whether DI makes any sense in dynamic languages like Python and Ruby. I'll probably get hate mail for writing this article.
Besides that minor point, if you can't wrap your head around the idea of inversion of control, try to look at it from implementation point of view. You have a Python class that will be instantiated by the Spring container. Define your class, create the XML config file with the initial values that you'd like, create a Spring container inside of the dependent component, get the object via the container. Now you can use this object like any other Python object. That's it, really.
The Spring Python home page
A great opinion piece on why, and when, you should use Spring Python
Martin Fowler's explanation on Inversion of Control and Dependency Injection
Another article by Martin Fowler on both DI and IoC
A Wikipedia article on DI, mentions some other Python frameworks