Aspektorienterad programmering med Spring AOP över Grails framework

Förord

Aspektorienterad programmering (AOP) är ett nytt koncept och programmeringsparadigm. Det tillåter i princip möjligheten att lägga till funktionalitet till moduler och klasser utan att ändra i koden. Det möjliggörs genom att konfigurera Pointcut, som anger vilka bitar av koden som ska bli executed efter att ha skickat en viss händelse över en metod eller klass.

Mål

Vi strävar efter och antar att utvecklaren, med hjälp av denna artikel, kommer att kunna använda aspektorienterad programmering i Grails-projekt.

Requirements

Grails 3. *

Dependencies:

Spring Aspects 5. *

AspectJ Weaver 1. *

1- Aspektorienterad programmering approach

Oftast innebär våra mjukvarulösningar komplex logik och i detta fall är det särskilt viktigt att hålla en ren och förståelig kod. AOP som tillvägagångssätt hjälper till och möjliggör ett enkelt sätt för modularisering. I grund och botten så tillåter AOP att lägga till funktionalitet till moduler och klasser utan att ändra koden.

2- Bean setting med Spring

Det första steget är att konfigurera Spring AOP, i Spring resource file (conf / spring / resources.groovy). Vi behöver också ange vilken bean som ska fungera som aspect, i vårt fall kommer det att vara Trace bean. Den kan konfigureras på flera sätt, till exempel med annotation @Aspect in din Bean declaration.

I det här fallet måste vi injecta log dependencies som redan finns för Grails, så vi behöver inte skapa några instances själva.

// Aspect-Oriented

xmlns aop:"http://www.springframework.org/schema/aop"

trace(Trace){

    log = log

}

aop{

    config("proxy-target-class": true) {

        aspect(id: "traceService", ref: "trace")

    }

}

3- Implementing logging functionality

Några av de viktigaste begreppen vi behöver ha koll på är: Pointcut, Advice och JoinPoint. En Pointcut är huvudsakligen ett sätt att matcha en method call med en funktionalitet som vi lägger till ett nytt beteende, det behöver ange expression och signature. För att utföra det nya beteendet så behöver vi ange några advice och associera den med en Pointcut.

Advice är declared med annotations som exempelvis @Before och @After och anger när koden ska verkställas. Advice kommer att ta som parameter en JoinPoint, som innehåller information om det aktuella anropet, då menar jag info så som parametervärden, called metod osv.

Testprogrammet är relaterat till dokumenthanteringen. De viktigaste funktionerna är uppladdning, konvertering till HTML och att lagra dokument från olika format. Koden som vi visar ger prov på att vi för track actions över dokumenten.

Här hittar du Aspect bean kallad Trace:

@Aspect

class Trace {

    def log


    @Pointcut("execution(* my.company.myApp.document.DocumentService.add(..))")

    void createDocument(){}



    @Pointcut("execution(* my.company.myApp.document.DocumentService.update(..)) && args(document, user,..)")

    void updateDocument(Document document, User user){}



    @Pointcut("execution(* my.company.myApp.repository.RepositoryService.add*(..))")

    void storeDocument(){}



    @Before("createDocument()")

    void create(JoinPoint jp){

        Document document = jp.args[0] as Document

        User user = jp.args[1] as User

        log.info "The document: ${document?.name} has been created by user: ${user?.username}"

    }



    @Before("updateDocument(document, user)")

    void update(Document document, User user){

        log.info "The document: ${document?.name} has been modified by user: ${user?.username}"

    }

    

    @Before("storeDocument()")

    void storeDocument(JoinPoint jp){

        log.info "Called ${jp.signature.name} with ${jp.args} on ${jp.target}: Storing a new document"

    }
}

Jag har tagit upp tre Pointcut sentences, Jag kommer att förklara en del nedan.

Uppdatering av dokument:

@Pointcut("execution(* my.company.myApp.document.DocumentService.update(..)) && args(document, user,..)")
  
  void updateDocument(Document document, User user){}

I det här fallet anger vi vilken metod vi vill spåra. Vi kan ange arguments i Pointcut declaration, det funkar de flesta gånger.

Vår applikation innehåller method med texten:

void update(Document document, User user){

    Version version = new Version(state: State.UPDATED, user: user, date: new Date())

    document.addToVersions(version)

    document.save()   

}

Sedan efter declaring vår Pointcut så måste vi genomföra den metod som ska utföra required behavior. Vi vill lägga till nytt beteende exakt innan metoden exekveras och sedan spåra vilken användare som har uppdaterat dokumentet.

@Before("updateDocument(document, user)")

    void update(Document document, User user){
      
        log.info "The document: ${document?.name} has been modified by user: ${user?.username}"
    
    }

Lagring av dokument:

I det här nya fallet kommer vi att använda samma endpoint för att fånga ett annat method call:

@Pointcut("execution(* my.company.myApp.repository.RepositoryService.add*(..))")
    
    void storeDocument(){}

I vårt system har vi en Repository-klass som ansvarar för att arbeta med vårt files repository, i princip har vi två slags repository, HTML och Source. Vi vill spåra när ett dokument lagras i det.

def addHtmlDocument(String id, Format format, String fromPath){

    String repositoryPath = grailsApplication.config.dataStore.repository.html

    repository = new HtmlRepository(

            path: repositoryPath,

            converter: new DocumentFormat(format: Format.HTML),    

            builder: new AntBuilder())

    repository.addDocument(id, format, fromPath)

}


def addSourceDocument(String id, Format format, String fromPath){

    String repositoryPath = grailsApplication.config.dataStore.repository.source   

    repository = new SourceRepository(

            path: repositoryPath,

            builder: new AntBuilder())

    repository.addDocument(id, format, fromPath)

}

Behavior:

@Before("storeDocument()")

    void storeDocument(JoinPoint jp){

        log.info "Called ${jp.signature.name} with ${jp.args} on ${jp.target}: Storing a new document"

    }

Det nya handlingssättet kommer att skriva ut en logg med namnet på called method, arguments och instance description.

4- Slutsats

Aspektorienterad programmering (AOS) är ett nytt koncept, genom att använda det korrekt så ser vi till att vår kod hålls ren. Med AOS kan vi lägga till funktionalitet enklare utan att ändra befintliga klasser och metoder. Det är nödvändigt vara försiktig och hålla en ordentlig dokumentation för att undvika oönskat beteende i vår i vår applikation.

5- Referencer

https://docs.spring.io

Origin: www.linkedin.com

/ Carlos

Senior Web Application Developer på Visionmate

Alla artiklar