Laden...

[Artikel] Aspektorientierte Programmierung (mit Rapier-Loom.NET)

Erstellt von dN!3L vor 17 Jahren Letzter Beitrag vor 16 Jahren 63.464 Views
dN!3L Themenstarter:in
2.891 Beiträge seit 2004
vor 17 Jahren
[Artikel] Aspektorientierte Programmierung (mit Rapier-Loom.NET)

Dieser Artikel basiert auf einer Ausarbeitung von mir im Seminar "aspektorientiertes Programmieren" (AOP). Da hatte ich mir das Thema "AOP auf der .NET-Plattform" angeln können, dazu noch das mit dem vernünftigen AOP-Tool (nämlich Rapier-Loom.NET), im Gegensatz zu meinem Partner, der Aspect.NET behandeln "durfte".

Worum soll es also gehen?
Zunächst gibt's eine kleine Einführung in AOP. Als nächstes geht's gleich los mit Rapier-Loom.NET. Da wird Konzept erläutert, wie Aspekte realisiert werden können. Anschließend werden dann konkrete Beispiele für die Implementierung von Aspekten gegeben. Dazu werden die Syntax von Aspektdeklarationen und die programmatischen Möglichkeiten innerhalb eines Aspektes näher beleuchtet. Zum Schluss gibt's dann noch einen Einblick in die Funktionsweise des Webers und ein kleines Fazit. Und natürlich Referenzen.

Beispielcode
Im Anhang befindet sich Beispielcode mit mehreren verschiedenen Aspekten. Da die Praxisbeispiele auf dem angehängten Projekt beruhen, sind auch mehrere Aspekte unten erklärt. Die Aspekte werden in der Klasse "Factory" (de-)aktiviert.

Version
Die Ausarbeitung basiert auf der Version "Rapier-Loom.NET 1.5 für .NET 2.0".

dN!3L Themenstarter:in
2.891 Beiträge seit 2004
vor 17 Jahren
1. Einführung in AOP

Original von [14]

Ein Entwickler entwirft ein System aus einer Menge von Anforderungen. Man kann diese in Anforderungen auf Modulebene (Geschäftslogik) und Anforderungen auf Systemebene klassifizieren. Die Anforderungen auf Systemebene sind meist orthogonal zueinander, überschneiden sich aber mit Anforderungen auf der Modulebene. Zum Beispiel erfordert die Geschäftslogik an verschiedenen Stellen eine Authentifizierung des Nutzers, um Operationen ausführen zu dürfen. Betrachtet man nun die Implementierung dieses Systems, so kann man sagen, dass die Anforderungen auf Modulebene die funktionalen Eigenschaften des Systems wieder spiegeln. Auf der anderen Seite sind die Anforderungen auf Systemebene durch die modulübergreifenden Eigenschaften oder auch nicht funktionalen Eigenschaften des Systems reflektiert. Gängige Programmiermethoden erlauben es aber nur, die Anforderungen in einer Dimension zu implementieren. In dieser Dimension wird die Modulebene implementiert. Alles was übrig bleibt (Systemebene), wird zusätzlich in dieser Dimension verteilt. Mit anderen Worten, der Anforderungsraum ist n-dimensional wohingegen der Implementierungsraum nur eindimensional ist. Typische Beispiele für solche modulübergreifenden Eigenschaften sind:
Fehlertoleranz,

Sicherheit,

Logging,

Tracing,

Synchronisation,

Testen und

Fehlerbehandlung.

Folgende Symptome lassen sich identifizieren, wenn man versucht, die oben beschriebenen Probleme mit gängigen Programmiermethoden zu implementieren:

Fehlende funktionale Kapselung. Module in einem Softwaresystem müssen oft verschiedenen Anforderungen genügen. Als Resultat müssen sich die Entwickler gleichzeitig Gedanken um die Implementierung der Geschäftslogik, und z.B. der Sicherheit und der Fehlertoleranz machen. Dadurch entsteht Quellcode, der zwar allen Anforderungen gerecht wird, wo die einzelnen Anforderungen aber nicht mehr als Einheit erkannt werden können. Der Quellcode ist durchsetzt (im Englischen code tangling).

Verstreuter Code. Da sich nicht funktionale Eigenschaften über mehrere Module verteilen, ist deren Implementierung auch über mehrere Module verteilt (im Englischen code scattering).

Verborgene Eigenschaften. Die Eigenschaften der Systemebene (nicht funktionale Eigenschaften) spiegeln sich nicht im Interface der Implementierung wieder.

Code tangling und code scattering beeinflussen das Softwaredesign und die Softwareentwicklung auf verschiedenste Weise. Hier sind einige Schlussfolgerungen, die daraus gezogen werden können:

Schlechte Wartbarkeit. Da der Code für eine Eigenschaft in einem Modul an verschiedenen Stellen implementiert ist (code tangling), ist es für den Entwickler schwer, diesen als Einheit zu erfassen und entsprechend zu warten. Auf der anderen Seite, wenn der Code über mehrere Module verteilt ist (code scattering), müssen Änderungen parallel an allen Modulen erfolgen.

Kaum Wiederverwendungsmöglichkeit. Code, der verteilt ist, lässt sich schlecht für andere Anforderungen wiederverwenden, da er keine eigene Einheit darstellt.

Schlechte Produktivität. Könnte man jede Eigenschaft einzeln betrachten und als Einheit implementieren, so entsteht kein zusätzlicher Aufwand durch Probleme, die aus der Verteilung auf und in die einzelnen Module resultieren.

Schlechte Erweiterbarkeit. Ein Softwaresystem wird mit den Erkenntnissen von heute implementiert. Sollten jedoch in Zukunft weitere Eigenschaften hinzukommen, gestaltet es sich sehr schwierig, diese zusätzlich zu implementieren.

In der vorangegangenen Diskussion wurde klar, dass es durchaus von Nutzen ist, modulübergreifende Eigenschaften als eigenständige Einheiten zu betrachten und zu implementieren. Dies wird in der Literatur häufig als separation of concerns bezeichnet. Aspektorientierte Programmierung ist eine Methode hierfür und stellt einen Weg dar, um die aufgezeigten Probleme zu lösen. AOP bietet die Möglichkeit, modulübergreifende Eigenschaften als eigenständige Einheit (so genannte Aspekte) zu implementieren und aus diesen Implementierungen das Gesamtsystem zu formen. Erst ein Aspektweber fügt funktionale Eigenschaften und Aspekte zusammen.

Bei der Objektorientierten Programmierung sind die meisten nicht funktionalen Eigenschaften eines Softwaresystems über mehrere Methoden oder Klassen im Programmcode verteilt und lassen sich nur schwer getrennt von den funktionalen Eigenschaften kapseln. Dies lässt sich mit den folgenden Begriffen beschreiben:
Überschneidende Belange sind die über mehrere Module im Programmcode verteilten nicht funktionalen Belange an ein Softwaresystem (im Englischen crosscutting concerns).

Verstreuter Code ist die über mehrere Module im Programmcode verteilte Implementierung der nicht funktionalen Eigenschaften (im Englischen scattered code).

Verworrener Code ist die Implementierung von einem funktionalen und einem oder mehreren nicht funktionalen Belangen an ein Softwaresystem in einem Modul (im Englischen tangled code).

Mit Hilfe der Aspektorientierten Programmierung lassen sich die verteilten nicht funktionalen Belange an ein Softwaresystem in Aspekten kapseln. Dadurch wird verstreuter und verworrener Code vermieden. Aspekte kapseln also die nicht funktionalen Eigenschaften an ein Softwaresystem. Zu einem Aspekt gehört die Definition der Verwebungspunkte und der Aspektcode.

Ein Verwebungspunkt (im Englischen join point) ist ein Ereignis, dass bei der Ausführung eines Softwareprogramms eintritt. Dabei kann es sich z.B. um einen Methodenaufruf, das Fangen einer Exception oder die Zuweisung eines Wertes handeln. Ein Methodenaufruf zwischen zwei Softwarekomponenten besitzt sechs mögliche Verwebungspunkte:
vor einem Methodenaufruf,

anstelle eines Methodenaufrufes,

nach einem Methodenaufruf,

vor dem Eintritt in eine Methode,

anstelle einer Methode und

nach der Beendigung einer Methode.

Die Definition der Verwebungspunkte finden in der Regel im Aspektcode statt. Dadurch muss der funktionale Programmcode nicht verändert werden. Verwebungspunkte kennzeichnen die Stellen im Programmcode an denen Aspektcode vom Aspektweber eingewoben werden soll. Kommt ein Programm bei seiner Ausführung an einen definierten Verwebungspunkt, dann wird an dieser Stelle der Aspektcode ausgeführt.

Aspektweber verweben den funktionalen Programmcode oder Bytecode mit dem Aspektcode an den definierten Verwebungspunkten. Es gibt vier verschiedene Arten von Aspektwebern:
Quellcode-Weber verweben den funktionalen Programmcode mit dem Aspektcode vor der übersetzung des Programmcodes (im Englischen source code weaver).

Bytecode-Weber verweben den Aspektcode mit dem compilierten funktionalen Programmcode (im Englischen byte code weaver).

Ladezeit-Weber verweben den funktionalen Bytecode eines Programmes mit dem Aspektcode zur Ladezeit (im Englischen link time weaver).

Laufzeit-Weber verweben den Aspektcode mit dem geladenen funktionalen Bytecode dynamisch zur Laufzeit (im Englischen run time weaver).

Die Quellcode- und die Bytecode-Weber werden in dem Oberbegriff statische Aspektweber zusammengefasst. Die Ladezeit- und Laufzeit-Weber werden auch dynamische Aspektweber genannt.

Die Objektorientierte Programmierung ermöglicht die Modularisierung von funktionalen Belangen an ein Softwaresystem in Klassen. Die Aspektorientierte Programmierung ermöglicht zusätzlich die Modularisierung von nicht-funktionalen Belangen in Aspekten. Mit Hilfe von AOP ist also eine mehrdimensionale Modularisierung möglich. Durch die Modularisierung von nicht-funktionalen Eigenschaften soll der strukturelle Aufbau eines Softwaresystem übersichtlicher werden, wodurch die Wartbarkeit und Wiederverwendbarkeit von Programmcode verbessert werden soll. Außerdem ist es möglich funktionalen und nicht funktionalen Programmcode separat voneinander zu entwerfen, zu implementieren und zu testen.

Das Standardbeispiel für AOP ist Logging. Loggingfunktionalität ist in allen möglichen Modulen eingebaut (also die Aufrufe der Log()-Funktion). Das hat eigentlich gar nichts mit der eigentlichen Funktionalität dieses Objekts zu tun. Mit AOP kann man sich die ganzen verstreuten Aufrufe sparen und deklariert stattdessen zentral in einem Aspekt, wie und wann geloggt werden soll.

Ohne AOP


// hier steht Funktionalität vom Logging drin
public class NonAopClassOne
{
	public void One()
	{
		log("beginne mit One");
		...
		log("One beendet");
	}

	public void Two()
	{
		log("beginne mit Two");
		...
		log("Two beendet");
	}
}

// hier steht auch Funktionalität vom Logging drin
public class NonAopClassTwo
{
	public void Three()
	{
		log("beginne mit Three");
		...
		log("Three beendet");
	}

	public void Four()
	{
		log("beginne mit Four");
		...
		log("Four beendet");
	}
}

mit AOP

// hier steht keinerlei Funktionalität vom Logging drin
public class AopClassOne
{
	public void One()
	{ ... }

	public void Two()
	{ ... }
}

// hier ebenfalls nicht
public class AopClassTwo
{
	public void Three()
	{ ... }

	public void Four()
	{ ... }
}


// einzig und allein hier steht Funktionalität vom Logging drin
public class LogAspect : Aspect
{
	// vor dem Aufrufen jeder Methode wird das hier ausgeführt
	[Loom.ConnectionPoint.IncludeAll]
	[Loom.Call(Invoke.Before)]
	public void LogStart
	{
		log("beginne mit "+Context.MethodName);	
	}

	// nachdem eine Methode aufgerufen wurde, wird das hier ausgeführt
	[Loom.ConnectionPoint.IncludeAll]
	[Loom.Call(Invoke.After)]
	public void LogEnd
	{
		log(Context.MethodName+" beendet");	
	}
}
dN!3L Themenstarter:in
2.891 Beiträge seit 2004
vor 17 Jahren
2. Rapier-Loom.NET

Das Loom.NET-Projekt am Lehrstuhl „Betriebssysteme und Middleware“ am Hasso Plattner Institut an der Universität Potsdam beschäftigt sich ebenfalls mit der Unterstützung von aspektorientierter Programmierung im .NET-Framework. Unter der Leitung von Wolfgang Schult wurde bereits mit der Betaversion des .NET-Frameworks 1.0 damit begonnen, den statischen Weber Loom.NET zu implementieren. Mit diesem ist es möglich, in ein .NET-Kompilat Aspekte einzuweben. Loom.NET ist jedoch bereits veraltet und dessen Nachfolge wurde von Rapier-Loom.NET* , einem dynamischen Weber, angetreten. Dieser ist aktuell in Version 1.51 verfügbar; eine vollständige Unterstützung von .NET 2.0 Generics steht kurz vor der Fertigstellung.

* „rapier loom“: (engl.) ein sehr einfacher und vielseitiger Webstuhl mit durchschnittlicher Geschwindkeit [16]

dN!3L Themenstarter:in
2.891 Beiträge seit 2004
vor 17 Jahren
2.1 Funktionsweise

Wie bereits beschrieben, handelt es sich bei RapierLoom.NET um einen dynamischen Weber. Das bedeutet, dass Verwebungsentscheidungen erst zur Laufzeit (beim Instanziieren) getroffen werden können. Es ist also möglich, erst während der Programmausführung zu bestimmen, welche Aspekte eingewoben werden sollen, beziehungsweise ob überhaupt Aspekte eingewoben werden sollen. Realisiert wird dies alles durch das Factory- und das Proxy/Decorator-Konzept (siehe 2.4).
Bei der Instanziierung einer Zielklasse (Typ, in den Aspekte eingewoben werden sollen) werden die einzuwebenden Aspekte (bzw. „Aspektklassen“) also explizit angegeben. Als nächstes verwebt der Weber Ziel- und Aspektklassen an definierten Punkten und man erhält ein Objekt der Verwebungsklasse.

Abb. 6. [9] Eine Zielklasse wird mit mehreren Aspektklassen verwoben. Es entsteht eine Verwebungsklasse, in der die querschneidenden Belange aus den Aspektklassen an definierten Verwebungspunkten eingewoben sind.

In den Aspektklassen befinden sich so genannte Aspektmethoden*. Diese können mit bestimmten Methoden in der Zielklasse verwoben werden und definieren Verbindungspunkte, die angeben, an welchen Zielklassen, wo dort und wie sie mit der Zielklassenmethode verwoben werden. Zielklassen müssen dabei entweder virtuell sein oder in einem Interface deklariert sein. Dabei ist anzumerken, dass wegen des Proxy-Konzeptes nur Methoden verwoben werden können. Eigenschaften** werden beim Kompilieren jedoch in Methoden umgesetzt, sodass man auch auf Objektzugriffe Aspekte anwenden kann – sofern diese über Eigenschaften realisiert sind.
Ziel- und Aspektklassen müssen dabei nicht als Quelltext vorliegen. Es ist ebenso möglich, Typen aus bereits kompilierten Assemblies als Ziel- bzw. Aspektklasse zu benutzen. Somit ist man auch nicht an eine bestimmte Programmiersprache gebunden, man kann jede beliebige .NET-Sprache benutzen. Durch das Factory-Konzept und die Verwendung von Attributen*** zur Deklaration von Verwebungspunkten sind auch keine Spracherweiterung oder Benutzung eines speziellen Compilers nötig.

*entsprechen „Advice“ in AspectJ
*gemeint ist hier das spezielle Konzept für Getter und Setter in .NET
*
* Attribute bezeichnen (auch im Folgenden) nicht Klassenattribute, sondern Annotationen von .NET-Klassen vom Typ System.Attribute

dN!3L Themenstarter:in
2.891 Beiträge seit 2004
vor 17 Jahren
2.2 Aspektdeklaration

Um einen Aspekt zu deklarieren, reicht es, eine Spezialisierung des Typs Loom.Aspect zu erstellen. Damit hat man bereits eine Aspektklasse erstellt, die man einweben kann – wenn sie auch so noch nichts bewirken kann.

ConnectionPoints. Damit nämlich Aspektmethoden eingewoben werden, muss man Verbindungspunkte, so genannte ConnectionPoints*, deklarieren. Dies sind Attribute – Aspektmethoden können (und müssen) also annotiert werden. Die Annotation funktioniert nicht nur mit Methoden, sondern die Attribute können auch an die get- und set-Deklaration einer Eigenschaft gebunden werden.

Beispiel 1. Eine Aspektklasse.

public class AspectClass : Loom.Aspect
{
   [Loom.ConnectionPoint.IncludeAll]
   [Loom.Call(Loom.Invoke.After)]
   public void matchAll(object[] args)
   {}
}

Diese Beispiel-Aspektmethode wird nach (Invoke.After) jedem (IncludeAll) Methodenaufruf (Loom.Call) in der Zielklasse aufgerufen. Man erkennt, dass es zwei Arten von ConnectionPoints gibt, mit denen man folgende Fragestellungen lösen kann:

Wann wird die Aspektmethode aufgerufen? Zum einen gibt es die Möglichkeit, Methodenaufrufe zu betrachten – dazu dient das Attribut Loom.Call – zum anderen kann man Konstruktoraufrufe – mit Loom.Create – betrachten. Zudem muss man bei beiden Möglichkeiten angeben, wie die Aspektmethode eingewoben werden soll – ob vor, anstatt oder nach dem Zielklassenmethodenaufruf/-konstruktoraufruf. Für Methodenaufrufe gibt es außerdem die Möglichkeiten, auf das Auslösen einer Ausnahme zu reagieren oder nach der Ausführung den Rückgabewert bearbeiten zu können.

Tabelle 1. Wie werden Aspektmethoden eingewoben

Invoke.Before            vor Zielmethodenaufruf/-konstruktoraufruf
Invoke.After             nach Zielmethodenaufruf/-konstruktoraufruf
Invoke.Instead	         anstatt Zielmethodenaufruf/-konstruktoraufruf
Invoke.AfterReturning    nach Zielmethodenaufruf mit Möglichkeit, den Rückgabewert zu bearbeiten
Invoke.AfterThrowing     wenn während Zielmethodenaufruf eine Ausnahme auftritt

An welcher Stelle wird Aspektmethode eingewoben: Hier deklariert man, an welche Zielklassenmethoden die Aspektmethode angewoben wird. Der Ausgangsfall ist, dass mit keiner Zielklasse verwoben wird. Um die Methoden einzugrenzen, an die angewoben wird, gibt es mehrere Möglichkeiten, die man alle beliebig kombinieren kann:1.Wenn die Aspektmethode den gleichen Namen, die gleiche Signatur und den gleichen Rückgabetyp wie eine Methode in der Zielklasse hat, wird sie automatisch mit dieser verwoben (falls keine der folgenden Möglichkeiten angewendet wurde). 1.Mit dem Attribut IncludeAll wird die Aspektmethode mit allen Zielklassenmethoden verwoben, die den gleichen Rückgabewert und die gleiche Signatur haben. 1.Mit dem Attribut Include/Exclude + Methodenname wird die Aspektmethode (nicht) mit Zielklassenmethoden verwoben, die den gleichen Rückgabewert und die gleiche Signatur haben und deren Methodennamen dem angegebenen entspricht. 1.Mit dem Attribut Include/Exclude + Typ wird nur/nicht in Zielklassen gewoben, die vom angegebenen Typ sind. 1.Mit dem Attribut IncludeIfAttributeIsDefined/ ExcludeIdAttribu-teIsDefined + Attributtyp wird die Aspektmethode (nicht) mit Zielklassenmethoden verwoben, die den gleichen Rückgabewert und die gleiche Signatur haben und die ein Attribut vom angegebenen Typ besitzen. 1.Mit dem Attribut DeclaredOnly wird die Aspektmethode nur mit der Zielklassenmethode verwoben, wenn sie in der Zielklasse deklariert wurde (nicht in einer Basisklasse der Zielklasse).

Als Wildcards ist als Rückgabetyp object und für Parameter object[] möglich (falls das Attribut Loom.ConnectionPoint.PreserveType vorhanden ist, werden diese nicht als Wildcard interpretiert), sowie ‚*’ und ‚?’ in Methodennamen (bei Include/Exclude).

Aspektkontext. Innerhalb einer Aspektmethode hat man Zugriff auf das Objekt Context. Bei einem Instead-Aufruf wird dieses automatisch befüllt, ansonsten kann man dies mit dem Attribut ContextRequired veranlassen. Mittels dieses Objekts bekommt man unter anderem Zugriff auf die Zielklasse oder den Zielmethodennamen, in dessen Kontext die Aspektmethode zur Laufzeit aktiv ist und man kann die Zielklassenmethode, an die angewoben wurde, aufrufen (wichtig zum Beispiel beim Instead).

Beispiel 2. Wenn ein Kunde nach einer Abbuchung im Negativen steht, wird eine Ausnahme ausgelöst. Dazu wird die Aspektmethode nur in Zielklassen vom Typ IClient eingewoben, und zwar nach dem Methodenaufruf von CreditOperation (welche Parameter CreditOperation entgegennimmt, ist dabei egal). Da man Zugriff auf das IClient-Objekt benötigt, um den Kontostand abzufragen, muss das Context-Objekt befüllt werden.

[Loom.Call(Loom.Invoke.After)]
[Loom.ConnectionPoint.ContextRequired]
[Loom.ConnectionPoint.Include(typeof(IClient))]
[Loom.ConnectionPoint.Include("CreditOperation")]
public void CreditOverflowAssertion(object[] args)
{
   IClient client = Context.Instance as IClient;
   if (client!=null && client.Account<0)
      throw new Exception("Account exceeded!");
}

Beispiel 3. Bei Änderungen an einem Objekt wird dieses dem Transaktionsmanager gemeldet. Dazu wird vor jedem Methodenaufruf (Rückgabetyp und Parameter beliebig) das betroffene Objekt ermittelt und dem Transaktionsmanger gemeldet. Dabei wird davon ausgegangen, dass Änderungen an einem Objekt nur durch Methodenaufrufe und Änderung von Eigenschaften hervorgerufen werden.

[Loom.ConnectionPoint.IncludeAll]
[Loom.ConnectionPoint.ContextRequired]
[Loom.Call(Loom.Invoke.Before)]
public object DetectChanges(object[] args)
{
   if (!isDetectingChanges)
   {
      isDetectingChanges = true;
      TransactionManager.RegisterObject(Context.Instance); 
      isDetectingChanges = false;
   }
   return null;
}
private bool isDetectingChanges = false;

Beispiel 4. Falls während der Ausführung der Methode StartOrderProcessing ein Fehler auftritt, wird der ursprüngliche Zustand wiederhergestellt. Die Aspektmethode heißt so wie die Zielklassenmethode und es wurden keine anderen ConnectionPoint-Attribute definiert. Die Parameter der Zielklassenmethode können beliebig sein. Die Aspektmethode wird anstatt der Zielklassenmethode aufgerufen, startet eine Transaktion und versucht, die ersetzte Zielklassenmethode aufzurufen (aufgrund des Instead-Aufrufs ist das Context-Objekt bereits befüllt) und die Transaktion abzuschließen. Gelingt dies nicht, wird ein Rollback durchgeführt.

[Loom.Call(Loom.Invoke.Instead)]
public void StartOrderProcessing(object[] args)
{
   TransactionManager.ITransaction transaction = TransactionManager.CreateTransaction();
   try
   {
      transaction.Start();
      Context.Invoke(args);
      transaction.Commit();
   }
   catch
   {
      transaction.Rollback();
   }
}

Introductions. Rapier-Loom.NET bietet die Möglichkeit, zusätzliche Interfaces einzuweben. Dazu muss die Aspektklasse mit dem Attribut Loom.Introduces + Typ annotiert werden und die Aspektklasse muss das angegebene Interface implementieren.

Beispiel 5. Das Interface ICloneable wird eingewoben.

[Loom.Introduces(typeof(ICloneable))]
public class AspectClass : Loom.Aspect, ICloneable 
{
   public object Clone() {…}
}

* entsprechen „Pointcuts“ in AspectJ

dN!3L Themenstarter:in
2.891 Beiträge seit 2004
vor 17 Jahren
2.3 Weben zur Laufzeit - Der Weber

Nachdem man nun Aspekte deklariert hat, müssen diese auch irgendwann eingewoben werden. Wie bereits beschrieben ist Rapier-Loom.NET ein dynamischer Weber. Man kann also zur Laufzeit bzw. zur Instanziierungszeit entscheiden, welche Aspekte eingewoben werden sollen. Um nun eine Zielklasse mit Aspektklassen zu verweben, muss man (anstelle des new-Schlüsselwortes) den Weber als Factory benutzen. Dafür steht das statische Objekt Loom.Weaver bereit. Mit der Methode Weaver.CreateInstance kann nun eine Verwebungsklasse erstellt werden.

Beispiel 6. (Vgl. normale Instanziierung mit new); Nutzung des Webers als Factory (.NET 2.0 Generics); Nutzung des Webers als Factory (.NET 1.x-kompatibel)

IClient client1 = new Client("John",100);
IClient client2 = Weaver.Create<Client>("John",100);
IClient client3 = (IClient)Weaver.CreateInstance(typeof(Client),new object[] { "John",100 });

Welche Aspekte werden nun aber eingewoben? Eine Möglichkeit ist, die Zielklasse oder die gesamte Assembly direkt mit der Aspektklasse zu annotieren (eine Aspektklasse ist eine Spezialisierung des Typs System.Attribute). Ist dies der Fall, wird jeder so angegebene Aspekt eingewoben und eine Instanziierung mit dem Weber kann wie in Beispiel 6 erfolgen.

Beispiel 7. Annotierung einer Zielklasse mit einem Aspekt. Der angegebene Aspekt wird vom Weber automatisch eingewoben.

[AspectClass]
public class TargetClass
{…}

Eine andere Möglichkeit, die Aspekte einzuweben bzw. weitere Aspekte einzuweben, ist die Angabe dieser beim Aufrufen des Webers. Die entsprechenden Webermethoden weisen eine Vielzahl von Überladungen auf:

Beispiel 8. Einweben explizit angegebener Aspekte mit Weber als Factory (.NET 2.0)

Aspect aspect1 = new TimeMeasureAspect();
Aspect aspect2 = new TransactionalAspect();
Aspect aspect3 = new CreditAbilityAspect();
Aspect[] allAspects = new Aspect[] { aspect1,aspect2,aspect3 };
IClient client1 = Weaver.Create<Client>(aspect1,new object[] { "John",100 });
IClient client2 = Weaver.Create<Client>(allAspects,new object[] { "John",100 });

Zu beachten ist, dass nach Instanziierung die eingewobenen Aspekte für die gesamte Lebenszeit des erstellten Objekts fest eingewoben und aktiv sind. Es gibt also keine Möglichkeit, später zusätzliche Aspekte einzuweben oder zu entfernen. Es gibt lediglich die Option, einen eingewobenen Aspekt mit einem anderen zu ersetzen, was aber wiederum Beschränkungen unterliegt (siehe 5).

dN!3L Themenstarter:in
2.891 Beiträge seit 2004
vor 17 Jahren
2.4 Funktionsweise des Webers

Wie anfangs schon angedeutet, realisiert Rapier-Loom.NET zum dynamischen Verweben von Aspekten das Proxy- bzw. Decorator-Konzept. Das heißt, dass für jedes Objekt ein Wrapper-Objekt bzw. Proxy implementiert wird. Dieser Wrapper ist für die Ausführung des Aspektcodes verantwortlich. Das Wrapper-Objekt wird vor das zu verwebende Objekt platziert und alle Aufrufe an bzw. Referenzen auf das Objekt werden über den Proxy umgeleitet [14].

Abb. 7. Der Weber erstellt automatisch ein Proxy-Objekt für die Zielklasse, das an den Verwebungspunkten den entsprechenden Aspektcode des eingewobenen Aspekts aufruft.

Dabei wird für jeden einzelnen einzuwebenden Aspekt ein separater Proxy dynamisch zur Instanziierungszeit vom Weber generiert – wodurch übrigens im Gegensatz zu manch anderen Ansätzen Introductions leicht zu verwirklichen sind. So lässt sich auch leicht erklären, warum Zielmethoden entweder virtuell oder in einem Interface deklariert sein müssen – damit eben ein Proxy erstellt werden kann. Für die automatische Codegenerierung wird dazu die Klasse TypeBuilder aus dem .NET-Framework benutzt, welcher einen Satz von Routinen zum Definieren von Klassen, Hinzufügen von Methoden und Feldern und zum Erstellen der Klasse in der Laufzeit bereitstellt [15].
Der Weber geht nun pro Aspekt/Proxy wie folgt vor: Als erstes werden die Attribute der Aspektklasse, deren Methoden und deren Eigenschaften analysiert und ermittelt, was wo eingewoben werden muss. Anschließend wird für das Proxy-Objekt das Interface IAspectInfo implementiert, woran man später zum Beispiel eine Verwebungsklasse als solche erkennen und die eingewobenen Aspekte ermitteln kann. Als nächstes werden die für die Realisierung der Aspektverwebung nötigen Methoden, Eigenschaften, Introductions und Konstruktoren definiert und schließlich eine Instanz des so erstellten Typs erzeugt.
Zur Performanz im Gegensatz zur normalen Instanziierung ohne Weber und Benutzung von Nicht-Verwebungsklassen ist zu sagen, dass eine Instanziierung durch Benutzung des Webers natürlich länger dauert, da jedes Mal eine oder mehrere Proxyklassen (je Aspekt einen Proxy) erstellt werden müssen (was aber durch Zwischenspeicherung des für den Proxy generierten Codes optimiert wird), sowie Methoden mit verwobenen before- und after-Anweisungen geringfügig länger dauern (da sie durch den Proxy geleitet werden) und Anweisungen, bei denen ein Kontext erzeugt werden muss (zum Beispiel bei instead), selbstverständlich noch etwas mehr Zeit benötigen.

dN!3L Themenstarter:in
2.891 Beiträge seit 2004
vor 17 Jahren
3. Fazit

Rapier-Loom.NET bietet eine sehr ausgereifte und einfache Möglichkeit, Aspektorientierung in eigenen Projekten zu nutzen. Es werden keine zusätzlichen Spracherweiterung eingeführt, Aspekte werden in normalen Klassen deklariert und Verwebungspunkte durch Attribute markiert. Statt wie in anderen Realisierungen muss kein zusätzlicher Compiler benutzt werden, mit Rapier-Loom.NET stattdessen braucht man nur eine Assembly zu referenzieren und kann (und muss) den darin enthaltenen Weber als Factory benutzen. Dabei können auch externe Typen als Zielklasse verwendet werden, da das Proxy-Konzept implementiert wird.
Das Proxy-Konzept führt jedoch auch zu kleineren Einschränkungen, da man zum Einen nicht mehr das new-Schlüsselwort benutzen kann, sondern den Weber als Factory benutzen muss. Dies kann bei Unachtsamkeit dazu führen, dass kein Verweben stattfindet, es macht aber auch gerade die Mächtigkeit eines dynamischen Webers aus. Zum Anderen wird man durch den Sachverhalt, dass Zielklassenmethoden entweder virtuell oder in einem Interface deklariert sein müssen, dahingehend etwas eingeschränkt, dass man Aspekte nicht auf alle vorhandenen Methoden anwenden kann – was zum Beispiel für Performanz-Messungen wichtig wäre. Außerdem ist es nicht möglich, Aspekte für Feldzugriffe zu definieren. Dies sorgt jedoch auch dafür, dass durch Aspektorientierung die Kapselung der betroffenen Module nicht zerstört wird, denn Aspekte sind nur auf die von außen sichtbaren Methoden und nur auf Getter und Setter anwendbar.

dN!3L Themenstarter:in
2.891 Beiträge seit 2004
vor 17 Jahren
4. Referenzen
  1. "Aspektorientierte Programmierung auf der .NET-Plattform"; Frank Feinbube, Daniel Richter; Seminar aspektorientierte Programmierung (Hasso Plattner Institut); 09.02.2007
  2. „Aspektorientiertes Programmieren“; Wolfgang Schult; 20.02.2003
  3. „Dynamic Aspect-Weaving with .NET“; Wolfgang Schult, Andreas Polze; 12.09.2002
  4. RapierLoom.NET-Homepage; http://www.dcl.hpi.uni-potsdam.de/research/loom/rapier_loom.htm; 16.01.2007
  5. Quelltext von RapierLoom.NET v1.2
  6. „AspectJ 5 – Programmierung mit AJDT“; Robert Wierschke; Seminar aspektorientierte Programmierung (Hasso Plattner Institut); 12.2006
  7. „Aspektorientierte Programmierung - Überblick über Techniken und Werkzeuge“; Janin Jeske, Bastian Brehmer, Falko Menge, Stefan Hüttenrauch, Christian Adam, Benjamin Schüler, Wolfgang Schult, Andreas Rasche, Andreas Polze; Technische Berichte Nr. 14 des Hasso-Plattner-Instituts; 06.2006
  8. Visual Studio 2005 Dokumentation
  9. „Modern looms“; Britannica Concise Encyclopedia; http://secure.britannica.com/ebc/article-60775; 07.02.2007
dN!3L Themenstarter:in
2.891 Beiträge seit 2004
vor 17 Jahren

UPDATE: Einführung in AOP erweitert

830 Beiträge seit 2005
vor 17 Jahren

Hallo dN!3L,

vielen Dank für diesen Artikel.

Hat mir den Einstieg in das Thema echt erleichtert.
Schade nur, dass die Doku von Loom echt lahm ist. Die neue Version 2.0 ist noch so gut wie überhaupt nicht Dokumentiert. Aus diesem Grund habe ich AspectSharp benutzt. Ist zwar, glaube ich, nicht ganz so mächtig wie Loom, dafür aber sehr einfach einzusetzen.

Also, Danke nochmals.

Gruss
Friedel

Ohne Ziel ist auch der Weg egal.

2.921 Beiträge seit 2005
vor 17 Jahren

Du schreibst:

z.B.


[Loom.ConnectionPoint.IncludeAll]
[Loom.Call(Invoke.Before)]

über eine Methode in einer Aspekt-Klasse, dies heißt doch in diesem Falle, dass die Aspekt-Methode vor dem Aufruf einer jeden Methode ausgeführt wird, ist das richtig?

Erzeugt Loom dazu EIN globales Objekt, um dies zu erreichen oder eins pro Klasse, wo dies passiert? Werde ich versuchen morgen oder demnächst vielleicht auch selbst in Erfahrung zu bringen.

Geht auch der umgekehrte Weg? z.B. Ich schreibe z.B. ein Attribut über eine nicht Aspekt-Klasse und dann wird die dort beschriebene Methode bei bestimmten gegebenen Bedingungen ausgeführt?
ist das dann die
sogenannt "Statische Verwebung"?

Wie würde quasi ein Around ausgeführt werden, in java wird bei diesem Begriff auf die Advices hingewiesen, also gibt es auch Advices in Loom?

Was geht nicht, bzw. wo sind die Grenzen? Denn ich denke von diesem Mechanismus erwartet man sich doch einiges.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dN!3L Themenstarter:in
2.891 Beiträge seit 2004
vor 17 Jahren

Original von dr4g0n76

  
[Loom.ConnectionPoint.IncludeAll]  
[Loom.Call(Invoke.Before)]  
  

über eine Methode in einer Aspekt-Klasse, dies heißt doch in diesem Falle, dass die Aspekt-Methode vor dem Aufruf einer jeden Methode ausgeführt wird, ist das richtig? In diesem Fall schon. Das InvokeBefore zeigt an, dass die Aspektmethode VOR der Zielmethode ausgeführt werden soll, das IncludeAll, dass mit ALLEN Methoden in der Zielklasse verwoben wird (aber aufpassen: Parameter und Rückgabetypen müssen passen [siehe auch "Wildcards"]).

Erzeugt Loom dazu EIN globales Objekt, um dies zu erreichen oder eins pro Klasse, wo dies passiert? Werde ich versuchen morgen oder demnächst vielleicht auch selbst in Erfahrung zu bringen. Hm, was für ein Objekt meinst du? Du kannst einen einzigen Aspekt erstellen und diesen in alle anderen Klassen einweben. Da wird dann logischerweise auch immer nur der eine Aspekt verwendet bzw. dessen Aspektmethoden aufgerufen.
Aber ich denke eher, du meinst den Proxy (Abschnitt "Funktionsweise des Webers"). Es wird pro Zielklasse für jeden Aspekt ein Proxy erstellt. Im Anhang ist dazu ein kleines Programm, das via Reflection anzeigt, wie die Verwebungsklasse aussieht. Darin sieht man, dass für jeden eingewobenen Aspekt eine Spezialisierung erstellt wird. Jede dieser Spezialsierungen ist übrigens vom Typ AspectInfo, darin kann man z.B. auf die verwobene Aspektklasse zugreifen oder sich alle verwobenen Aspekte anzeigen lassen.

Geht auch der umgekehrte Weg? z.B. Ich schreibe z.B. ein Attribut über eine nicht Aspekt-Klasse und dann wird die dort beschriebene Methode bei bestimmten gegebenen Bedingungen ausgeführt?
ist das dann die sogenannt "Statische Verwebung"?

Ob die Verwebung statisch oder dynamisch ist, hängt vom Zeitpunkt der Verwebung ab. Statische Weber weben zur Compilezeit oder dann auf den Bytecode (was ja dein Ansatz ist, soweit ich gesehen habe); Dynamische Weber weben zur Laufzeit (macht RapierLoom).
Ansonsten ist die Antwort mit RapierLoom: Nein. Falls du auf dem Bytecode "webst" (du ignorierst das Aspekt-Konzept?) und je nach den Bedingungen, die du meinst, könnte man das als statisches Weben bezeichnen.

Wie würde quasi ein Around ausgeführt werden, in java wird bei diesem Begriff auf die Advices hingewiesen, also gibt es auch Advices in Loom?
Was geht nicht, bzw. wo sind die Grenzen? Denn ich denke von diesem Mechanismus erwartet man sich doch einiges.

Also nochmal im Bezug auf AspectJ (Java): In den Aspektklassen befinden sich so genannte Aspektmethoden, diese entsprechen „Advice“ in AspectJ. ConnectionPoints entsprechen "Pointcuts". In der neuesten RapierLoom-Version wurde auch umbenannt und dort heißt es dann auch "Advice" und "Pointcut" (bei Gelegenheit werd ich mich mal genauer mit der neuen Version beschäftigen).

Was geht nicht, bzw. wo sind die Grenzen? Denn ich denke von diesem Mechanismus erwartet man sich doch einiges. Hm, welcher Mechanismus?

Gruß
dN!3L

EDIT: Doch glatt den Anhang vergessen (und erst nach 2h dran gedacht)...

S
8.746 Beiträge seit 2005
vor 17 Jahren

Eine Beschränkung dürfte doch sein, dass sealed classes nicht durch Aspekte anzureichern sind. Dann könnte ich mir auch Probleme durch Inlining vorstellen, wird vermutlich via MethodImpl rausgedreht.

dN!3L Themenstarter:in
2.891 Beiträge seit 2004
vor 17 Jahren

Original von svenson
Eine Beschränkung dürfte doch sein, dass sealed classes nicht durch Aspekte anzureichern sind.

Genau, durch das Proxy-Konzept. Wie schon beschrieben kann man ja auch nur Aspekte auf Zeugs anwenden, das virtual (und somit public) oder in einem Interface deklariert wird.
Aber man kann das ja auch so sehen: Wenn ich eine sealed-Klasse habe, möchte ich nicht, dass mir jemand darin was ändert. Also ist es ja irgendwie auch logisch, dass man keine Aspekte darauf anwenden kann, die Klasse schließlich "versiegelt". Ebenso verhält es sich z.B. mit Feldern. Darauf kann ich keine Aspekte anwenden, weil eben Feldzugriffe schön sauber über Eingenschaften/Properties zu realisieren hat (worauf man dann wieder Aspekte anwenden kann).

Dann könnte ich mir auch Probleme durch Inlining vorstellen, wird vermutlich via MethodImpl rausgedreht. Was meinst du damit?

Gruß
dN!3L

T
512 Beiträge seit 2006
vor 17 Jahren

Ich hab mich jetzt auch mal mit AOP beschäftigt, und mir gefällt die Idee recht gut.

Ich finde nur leider nichts über eine Lizens von Rapier Loom.NET. Weiß jemand, ob man es in komerziellen Projekten einsetzen kann?

e.f.q.

Aus Falschem folgt Beliebiges

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo Traumzauberbaum,

Can I use Rapier-Loom.Net in commercial/private projects?

The current version of Rapier-Loom.Net is free for academic and private use. If you want to use Rapier-Loom.Net in a commercial environment, please contact us directly at
>
.

Aus der FAQ der 1.51 Doku

herbivore

W
1 Beiträge seit 2007
vor 17 Jahren

Von der Version 2.0 steht jetzt ein RC1 unter www.rapier-loom.net zur Verfügung, mit vollständiger Doku und Beispielen.

630 Beiträge seit 2007
vor 16 Jahren

Danke für diesen umfangreichen Artikel! Ist die Annahme richtig das Frameworks wie Loom oder Aspect# nur eine Übergangslösung darstellen bis die Programmierprachen AOP "nativ" unterstützen?


aspect ALogging
{...}

class MyClass :ALogging 
{...}

Währe sowas in Zukunft denkbar?
Gibt es schon konkrete Informationen seitens Microsoft?

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo tscherno,

denkbar ist eine native Unterstützung auf jeden Fall, aber ich denke, sie wird nicht so bald kommen.

Neulich hatte dr4g0n76 in einer privaten Diskussion folgendes geschrieben. Ich hoffe und denke, dass er nichts dagegen hat, wenn ich das mal hier zitiere. Auch wenn der Text selbst etwas aus dem Zusammenhang gerissen ist, denke ich, dass die Links darin ganz interessant sind.

Ich denke, ob neue Sprachkonstrukte eingeführt werden sollten oder nicht, ist ein Diskussionsthema für sich.

Vielleicht ist auch dieser Ansatz ganz falsch und der Compiler von Microsoft bietet schon eine Möglichkeit so etwas einfacher zu machen, ohne den nachfolgenden Disassemblierungs-/Reassemblierungsprozess.

Interessante vielleicht weiterführende Links, um zu sehen, was Microsoft vorhat:


>


>

Interessant hört sich für mich an, was bei ILX ausgesagt wird. Wenn ich auch noch nicht sicher bin, ob ich dort alle Zusammenhänge richtig verstanden habe.


>


>

herbivore

2.921 Beiträge seit 2005
vor 16 Jahren

Zitat:

_Original von Dr4g0n76_Ich hoffe und denke, dass er nichts dagegen hat, wenn ich das mal hier zitiere

@Herbivore:
Natürlich habe ich nichts dagegen. Ich finde es sogar sehr gut, dass Du dies an dieser Stelle hier erwähnst.

Ich hoffe auch immer noch, dass eine native Unterstützung von AOP bzw. Enhancern vielleicht bei der nächsten Framework Version oder Sprachspezifikation von C# angestrebt wird.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

I
1.739 Beiträge seit 2005
vor 16 Jahren

@tscherno: Das Framework Loom kenne ich nicht.
Könntest du bitte eine Kurzbeschreibung(sehr kurz ist ok) und einen Link liefern.
Loom kenn ich nur als Spiel...(Superteil übrigens(damals)), und Google war wohl gerade gegen meine Anfrage(Trash first).

Danke im Vorraus.

Ikaros

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo ikaros,

Das Framework Loom kenne ich nicht. Könntest du bitte eine Kurzbeschreibung(sehr kurz ist ok) und einen Link liefern.

der ganze Artikel handelt (direkt oder indirekt) von Loom-Framework. Du musst, sorry, also nur von Anfang an lesen. Auch an einem Link auf das Framwork (im der Literaturliste am Ende des eigentlichen Artikels) mangelt es nicht.

herbivore

I
1.739 Beiträge seit 2005
vor 16 Jahren

Danke für den Hinweis.

Meine Schuld(las nur den Rest).
War meine Faulheit(zu spät gemerkt), 2fachen Dank(Hinweis und Anstoss)

ikaros
(der jetzt den gesamten Thread liest und die Links ganz oben benutzt)

und die Störung entschuldigt...