@@ -268,6 +268,92 @@ factory.interceptors = [WrappingInterceptor()]
268268
269269It produces the same thing.
270270
271+ </section >
272+
273+ <section id =" aop-pure-python" >
274+ <title >Coding AOP with Pure Python</title >
275+
276+ <para >There is a long history of Spring being based on XML. However, Spring Python offers an easy to use alternative: a
277+ <link linkend =" objects-config-object" >pure python decorator-based PythonConfig</link >. Imagine you had set up a simple
278+ context like this:</para >
279+
280+ <programlisting ><![CDATA[
281+ from springpython.config import *
282+ from springpython.context import *
283+
284+ class MovieBasedApplicationContext(PythonConfig):
285+ def __init__(self):
286+ super(MovieBasedApplicationContext, self).__init__()
287+
288+ @Object(scope.PROTOTYPE)
289+ def MovieLister(self):
290+ lister = MovieLister()
291+ lister.finder = self.MovieFinder()
292+ lister.description = self.SingletonString()
293+ self.logger.debug("Description = %s" % lister.description)
294+ return lister
295+
296+ @Object(scope.SINGLETON)
297+ def MovieFinder(self):
298+ return ColonMovieFinder(filename="support/movies1.txt")
299+
300+ @Object # scope.SINGLETON is the default
301+ def SingletonString(self):
302+ return StringHolder("There should only be one copy of this string")
303+ ]]> </programlisting >
304+
305+ <para >From an AOP perspective, it is easy to intercept <classname >MovieFinder</classname > and wrap it with some
306+ advice. Because you have already exposed it as an injection point with this pure-python IoC container, you just need
307+ to make this change:</para >
308+
309+ <programlisting ><![CDATA[
310+ from springpython.aop import *
311+ from springpython.config import *
312+ from springpython.context import *
313+
314+ class MovieBasedApplicationContext(PythonConfig):
315+ def __init__(self):
316+ super(MovieBasedApplicationContext, self).__init__()
317+
318+ @Object(scope.PROTOTYPE)
319+ def MovieLister(self):
320+ lister = MovieLister()
321+ lister.finder = self.MovieFinder()
322+ lister.description = self.SingletonString()
323+ self.logger.debug("Description = %s" % lister.description)
324+ return lister
325+
326+ # By renaming the original service to this...
327+ def targetMovieFinder(self):
328+ return ColonMovieFinder(filename="support/movies1.txt")
329+
330+ #...we can substitute a proxy that will wrap it with an interceptor
331+ @Object(scope.SINGLETON)
332+ def MovieFinder(self):
333+ return ProxyFactoryObject(target=self.targetMovieFinder(),
334+ interceptors=MyInterceptor())
335+
336+
337+ @Object # scope.SINGLETON is the default
338+ def SingletonString(self):
339+ return StringHolder("There should only be one copy of this string")
340+
341+ class MyInterceptor(MethodInterceptor):
342+ def invoke(self, invocation):
343+ results = "<Wrapped>" + invocation.proceed() + "</Wrapped>"
344+ return results
345+ ]]> </programlisting >
346+
347+ <para >Now, everything that was referring to the original <classname >ColonMovieFinder</classname > instance, is instead pointing
348+ to a wrapping interceptor. The caller and callee involved don't know anything about it, keeping your code isolated and clean.</para >
349+
350+ <note >
351+ <title >Shouldn't you decouple the interceptor from the IoC configuration?</title >
352+
353+ <para >It is usually good practice to split up configuration from actual business code. These two
354+ were put together in the same file for demonstration purposes.</para >
355+ </note >
356+
271357 </section >
272358
273359</chapter >
0 commit comments