Chapter 3. Aspect Oriented Programming

Aspect oriented programming (AOP) is a horizontal
programming paradigm, where some type of behavior is applied to several classes
that don't share the same vertical, object-oriented inheritance. In AOP, programmers
implement these cross cutting concerns by writing an
aspect then applying it conditionally based on a join
point. This is referred to as applying advice. This
section shows how to use the AOP module of Spring Python.

3.1. External dependencies

Spring Python's AOP itself doesn't require any special external
libraries to work however the IoC configuration format of your choice,
unless you use PythonConfig, will likely need some. Refer
to the IoC documentation for more details.

3.2. Interceptors

Spring Python implements AOP advice using proxies and
method interceptors. NOTE: Interceptors only apply to method
calls. Any request for attributes are passed directly to the target without AOP
intervention.

Here is a sample service. Our goal is to wrap the results with "wrapped" tags,
without modifying the service's code.

invoke(self, invocation) is a dispatching method defined
abstractly in the MethodInterceptor base class. invocation
holds the target method name, any input arguments, and also the callable
target function. In this case, we aren't interested in the method name or the arguments.
So we call the actual function using invocation.proceed(), and than
catch its results. Then we can manipulate these results, and return them back to the caller.

In order to apply this advice to a service, a stand-in proxy
must be created and given to the client. One way to create this is by creating a
ProxyFactory. The factory is used to identify the target service
that is being intercepted. It is used to create the dynamic proxy object to give back to
the client.

You can use the Spring Python APIs to directly create this proxied service.

If you notice, the original Spring Python "service" object has been renamed as "targetService", and there is, instead,
another object called "serviceFactory" which is a Spring AOP ProxyFactory. It points to the target service and also has an
interceptor plugged in. In this case, the interceptor is defined as an inner object, not having a name of its own,
indicating it is not meant to be referenced outside the IoC container. When you get a hold of this, you can request a proxy.

Notice how I didn't have to edit the original service at all? I didn't even
have to introduce Spring Python into that module. Thanks to the power of Python's
dynamic nature, Spring Python AOP gives you the power to wrap your own source code
as well as other 3rd party modules.

3.3. Proxy Factory Objects

The earlier usage of a ProxyFactory is useful, but often
times we only need the factory to create one proxy. There is a shortcut called
ProxyFactoryObject.

In this case, the ProxyFactoryObject acts as both a proxy
and a factory. As a proxy, it behaves just like the target service would, and it also provides
the ability to wrap the service with aspects. It saved us a step of coding, but more importantly,
the ProxyFactoryObject took on the persona of being our service
right from the beginning.

To be more pythonic, Spring Python also allows you to initialize everything at once.

RegexpMethodPointAdvisor is a powerful object in
Spring Python that acts as pointcut, a join point,
and a method interceptor. It fetches the fullpath of the target's
module, class, and method name, and then checks to see if it matches any of the patterns
in the patterns list using Python's regular expression module.

By plugging this into a ProxyFactoryObject as an interceptor, it evaluates
whether to route method calls through the advice, or directly to the target service.

3.5. Interceptor Chain

You may have noticed by now that the WrappingInterceptor
is being specified inside a Python list. That is because you can apply more than one piece of advice.
When an interceptor calls invocation.proceed(), it is actually
calling the next interceptor in the chain, until it gets to the end. Then it calls
the actual target service. When the target method returns back, everything backtracks
back out the set of nested interceptors.

Spring Python is coded to intelligently detect if you are assigning a single
interceptor to the interceptors property, or a list. A single
interceptor gets converted into a list of one. So, you can do either of the following:

3.6. Coding AOP with Pure Python

There is a long history of Spring being based on XML. However, Spring Python offers an easy to use alternative: a
pure python decorator-based PythonConfig. Imagine you had set up a simple
context like this:

From an AOP perspective, it is easy to intercept MovieFinder and wrap it with some
advice. Because you have already exposed it as an injection point with this pure-python IoC container, you just need
to make this change:

Now, everything that was referring to the original ColonMovieFinder instance, is instead pointing
to a wrapping interceptor. The caller and callee involved don't know anything about it, keeping your code isolated and clean.

Shouldn't you decouple the interceptor from the IoC configuration?

It is usually good practice to split up configuration from actual business code. These two
were put together in the same file for demonstration purposes.