PDA

View Full Version : How to custom define pointcut attributes


steinard
10-06-2006, 12:50 PM
Hi!

This is the first day I look at AOP, and I've started by looking at the AOPQuickStart example. I realised pretty soon that the example is not what I would like, as I would like to annotate which methods that should be intercepted. Therefore I have modiefied the AOPQuickStart Example to create my own attribute and my own advice implementing IMethodInterceptor. I use:

Attribute[] attributes = (Attribute[]) invocation.Method.GetCustomAttributes(typeof(Attri bute), false);

to find out if a method is annotated with the specific attribute in my advice class. I wonder if there is a more elegant way of doing interception, as all methods executed on the class are now evaluated by this around-advice method, and where:

object returnValue = invocation.Proceed();

is simply called if specified attrbute(s) do not match. This is not so important as I can live with this...

Another more important thing, I wonder if there is a convenient way of adding instances to a proxyfactory, so e.g. that instances returned by nHibernate may be registered with a proxyfactory like class. Or do I have to manually manage one proxyfactory per instance to be intercepted?

Thanks for any help,
Steinar.

Bruno Baia
10-06-2006, 07:27 PM
Hi Steinar,

The example AopQuickStart is intentionally simplistic.
There is Aop Guide (http://www.springframework.net/doc/reference/html/aop-quickstart.html) based on this example to go deeper with Spring.AOP.
You should be interested by the chapter "Using Attributes to define Pointcuts (http://www.springframework.net/doc/reference/html/aop-quickstart.html#aop-quickstart-going-deeper-attribute-pointcuts)", but unfortunately it's empty :o ...

So here is an example bases on AopQuickStart example :


namespace Spring.Examples.AopQuickStart
{
public class ConsoleLoggingAroundAttribute : Attribute
{
}
}


<object id="consoleLoggingAroundAdvisor" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdv isor, Spring.Aop">
<property name="advice">
<object type="Spring.Examples.AopQuickStart.ConsoleLoggingAround Advice" />
</property>
<property name="attribute" value="Spring.Examples.AopQuickStart.ConsoleLoggingAround Attribute"/>
</object>

<object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject">
<property name="target">
<object id="myServiceObjectTarget" type="Spring.Examples.AopQuickStart.ServiceCommand" />
</property>
<property name="interceptorNames">
<list>
<value>consoleLoggingAroundAdvisor</value>
</list>
</property>
</object>


[ConsoleLoggingAround]
public void Execute()
{
Console.Out.WriteLine(
"Service implementation : Execute()...");
}



Btw, there is another example in the main documentation :
Static pointcuts (http://www.springframework.net/doc/reference/html/aop.html#aop-static-pointcuts)


Thanks to give Spring.NET a try,
Bruno

Bruno Baia
10-06-2006, 07:35 PM
Another more important thing, I wonder if there is a convenient way of adding instances to a proxyfactory, so e.g. that instances returned by nHibernate may be registered with a proxyfactory like class. Or do I have to manually manage one proxyfactory per instance to be intercepted?


You should take a look to the AutoProxy facility (http://www.springframework.net/doc/reference/html/aop.html#aop-autoproxy).
An example of this is the transaction management in Spring.Data.


-Bruno

steinard
10-09-2006, 10:42 AM
Great!

I only removed some parsing from my advice class (invoke method) and edited the xml specification you gave me and I was able to run my own attribute and advice implementation, and it worked out of the box, only intercepting the annotated method!

Thanks so much, now I'll look at autoprxying...
Steinar.

steinard
10-09-2006, 12:47 PM
Hi!

I have now read chapter 9 ( http://www.springframework.net/doc/reference/html/aop.html#aop-autoproxy )
concerning autoproxying. But I could not apply the description there to my modified QuickStart example.

I want to bind all created instances of a certain type to a proxyfactory, so methods can be intercepted. Example:

// First object
ICommand c1 = new ServiceCommand();
c1.execute(); // not intercepted
c1.execute2(); // intercepted because of annotation

// Second object with same behaviour....
ICommand c2 = new ServiceCommand();
c2.execute(); // not intercepted
c2.execute2(); // intercepted because of annotation

If it is not possible to make all instanciated objects of type ICommand automatically bound by DefaultAdvisorAutoProxyCreator, then I would like to register them, like:

proxyFactory.addTarget(c1);
proxyFactory.addTarget(c2);

But I see no way of doing this. Here is my spring object definitions:

<object id="myAdvisor" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdv isor, Spring.Aop">
<property name="advice">
<object type="Spring.Examples.AopQuickStart.MyAdvice" />
</property>
<property name="attribute" value="Spring.Examples.AopQuickStart.MyAttribute"/>
</object>



<object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoP roxyCreator">

<!--property name="target">
<object id="myServiceObjectTarget" type="Spring.Examples.AopQuickStart.ServiceCommand" />
</property-->

<property name="interceptorNames">
<list>
<value>myAdvisor</value>
</list>
</property>
</object>


By replacing type="Spring.Aop.Framework.ProxyFactoryObject" with type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoP roxyCreator" I had to comment out the target property, but now no interception occurs.

I guess I am missing something elementary here,
thanks,
Steinar.

Bruno Baia
10-10-2006, 05:59 AM
Hi,

'DefaultAdvisorAutoProxyCreator' will use all advisors defined by default, so you don't need to specify them again in interceptorsNames.

Here is an example, that will proxy all your business objects defined in the container :

<!--Business objects-->
<object id="myServiceCommand" type="Spring.Examples.AopQuickStart.ServiceCommand" />
<object id="myAnotherServiceCommand" type="Spring.Examples.AopQuickStart.AnotherServiceComman d" />
<object id="myAnotherClass" type="Spring.Examples.AopQuickStart.AnotherClass" />

<!--Advisors-->
<object id="consoleLoggingAroundAdvisor" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdv isor, Spring.Aop">
<property name="advice">
<object type="Spring.Examples.AopQuickStart.ConsoleLoggingAround Advice" />
</property>
<property name="attribute" value="Spring.Examples.AopQuickStart.ConsoleLoggingAround Attribute"/>
</object>

<!--AutoProxy definition-->
<object type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoP roxyCreator, Spring.Aop"/>




But you can go deeper if you need to.
Here is an example with a custom AutoProxyCreator that will proxy only ICommand implementation :

public class CommandAutoProxyCreator : AbstractAutoProxyCreator
{
/// <summary>
/// Identify as object to proxy if the object is a <see cref="ICommand"/> instance.
/// </summary>
protected override object[] GetAdvicesAndAdvisorsForObject(object obj, string name, ITargetSource customTargetSource)
{
if (obj is ICommand)
{
return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
}
return DO_NOT_PROXY;
}
}


<!-- Business objects -->
<object id="myServiceCommand" type="Spring.Examples.AopQuickStart.ServiceCommand" />
<object id="myAnotherServiceCommand" type="Spring.Examples.AopQuickStart.AnotherServiceComman d" />
<object id="myAnotherClass" type="Spring.Examples.AopQuickStart.AnotherClass" />

<!-- Advisors -->
<object id="consoleLoggingAroundAdvisor" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdv isor, Spring.Aop">
<property name="advice">
<object type="Spring.Examples.AopQuickStart.ConsoleLoggingAround Advice" />
</property>
<property name="attribute" value="Spring.Examples.AopQuickStart.ConsoleLoggingAround Attribute"/>
</object>

<!-- AutoProxy definition -->
<object type="Spring.Examples.AopQuickStart.CommandAutoProxyCrea tor">
<property name="InterceptorNames">
<list>
<value>consoleLoggingAroundAdvisor</value>
</list>
</property>
</object>

You can make CommandAutoProxyCreator more generic providing a 'Type' property with the type that the object needs to implement to be proxied, etc...
CommandAutoProxyCreator overrides "AbstractAutoProxyCreator" ant not "AbstractAdvisorAutoProxyCreator", so you need to specify which Advisors to apply using InterceptorNames property.

The AnotherServiceCommand is another ICommand implementation.

The AnotherClass doesn't implement ICommand but has a ConsoleLoggingAround Attribute applied on a method :

public class AnotherClass
{
[ConsoleLoggingAround]
public virtual void DoIt() // Needs to be virtual to be proxied (no interface here)
{
Console.Out.WriteLine(
"AnotherClass implementation : Execute()...");
}
}


Using business objects :

IApplicationContext ctx = ContextRegistry.GetContext();
ICommand serviceCommand = (ICommand)ctx["myServiceCommand"];
ICommand anotherServiceCommand = (ICommand)ctx["myAnotherServiceCommand"];
AnotherClass anotherClass = (AnotherClass)ctx["myAnotherClass"];

serviceCommand.Execute();
serviceCommand.DoExecute();

anotherServiceCommand.Execute();
anotherServiceCommand.DoExecute();

anotherClass.DoIt();




Hope this helps,
Bruno

steinard
10-10-2006, 09:04 AM
Hi!

Thank you so much for your help. A small change in my configuration file made things work again. But I am not yet satisfied with how a target is bound to the proxy, as I feel that I am back again to squar one (actually, I never left it). As far as I can see, the binding happens only if I create an instance through the context registry (which uses the xml-configuration).

If I programmatically choose to instanciate an object of ServiceCommander, then that object will not be bound to the autoproxy. I would love to have this functionality automated, as NHibernate will return several types of classes that should be intercepted and bound to a proxy. There I will never be able to call a context registry and have xml-weaving in the background do the binding.

So basically, I want an interception functionality like this:

// Create AOP proxy using Spring.NET IoC container.
IApplicationContext ctx = ContextRegistry.GetContext();
ICommand command = (ICommand) ctx["myServiceObject"];

command.Execute(); // not intercepted
command.Execute2(); // will be intercepted

ICommand com2 = new ServiceCommand();
com2.Execute2(); // should also be intercepted, but is not...

So is it possible to make a configuration such that all instances of a type is automatically bound to proxies or do I have to manage the proxies myself along with the instances? I would not do this below:

public ICommand LoadCommand(Type type, string id) {
// Create AOP proxy programmatically without Spring.NET IoC container.
ICommand com = (ICommand)session.CreateCriteria(type).Add(
Expression.Like("ServiceCommand", id)).UniqueResult();

ProxyFactory factory = new ProxyFactory(com);
factory.AddAdvice(new MyAdvice());
return (ICommand) factory.GetProxy();
}

Thanks again for all help you provide on this issue,
Steinar.

Bruno Baia
10-12-2006, 09:40 PM
Hi,

you can create an advice that will do this, and apply it to your DAO object :

public object Invoke(IMethodInvocation invocation)
{
object returnValue = invocation.Proceed();

ProxyFactory factory = new ProxyFactory(returnValue);
factory.AddAdvice(new MyAdvice()); // Can be configured in your advice
return factory.GetProxy();
}



I got a question, is you domain object implementing an interface ?
generally NHibernate returns domain objects ?


Cheers,
Bruno

steinard
10-16-2006, 09:06 AM
Hi again!

My persistence module returns domain objects that all implements special and more general interfaces. The most general interface simply indicates that the domain object may be persisted by nHibernate. NHibernate does not know whether it returns a customer or a product, it simply tries to return an object based on the criteria and the type specified. Reflection lookups will do the rest of the work before nHibernate criteria queries are executed (Because interfaces reside in other modules then the actual implementations, the implementations will almost never be directly accessed or referenced, creating a more loosely coupled application. For this to work, reflection must do it's magic).

The instanciated object will be delivered back to a manager that exposes the type's interface elsewhere. Actually, you never care about the exact implementation of the interface. Typically you will simply ask the manager layer for a type that implements ICustomer with ID=100 and you will never know if it is a customer of type VIP or regular (if someone really needs to know this then there are several ways to find out...).

Anyway, this works great, and by placing the implementation of nHibernate in a core liberary all projects can utilise nHibernated persistence right out of the box (with only a couple of lines with setup code). Right now I'm trying to figure out how I conveniently may register an interceptable object (to a proxy) instanciated and returned by nHibernate, if I succeed, this will also introduce AOP to the organisation.

Thanks for any help so far, right know I'm walking along the ways outlined in my previous posting just to see if this is a way I would like to go.

Cheers,
Steinar.

steinard
10-16-2006, 09:13 AM
generally NHibernate returns domain objects ?

Oh, it was just an examble to illustrate how an interceptable domain object would work in my problem. If you replace ICommand with IProductCategory it might make more sense....

Cheers,
Steinar.

steinard
10-18-2006, 09:58 AM
I ended up using your apporach by intercepting key methods in my dao hibernate layer to automatically register domain objects returned by nhibernate to a proxy. I had some small problems with reflection (as the proxy is a runtime-generated subclass of the expected type), but that was easy to solve.

Now I will start testing the usefulness (which I believe will be very useful) and also the time penalities introduced, and if this will have any serious impact on some heavy calculations (and frequent underlying DB-calls).

Thank you very much for pointing me in the right direction!
Cheers,
Steinar.