Programmation axée sur l'Aspect Java avec Annotations

dans un billet intitulé " AOP Fundamentals " , j'ai demandé une explication en anglais du Roi de ce qu'est AOP et de ce qu'elle fait. J'ai reçu des réponses très utiles et des liens vers des articles qui m'ont aidé à me renseigner sur toute la théorie.

mais maintenant AOP a eu toute mon attention, et tous ces articles et extraits de chapitre sont fantastiques, mais dans chaque cas simple ils se composent de la théorie élevée, vague UML des modèles, et un ordre d'abstraction bien trop élevé à mon goût.

voici ma compréhension de la théorie de AOP, juste pour clarifier, donc si vous voyez quelque chose qui ne va pas, faites-le moi savoir!:

  1. préoccupations transversales telles que la journalisation, L'authentification, La synchronisation, la validation, le traitement des exceptions, etc. deviennent fortement couplés dans les systèmes non-AOP car ils sont utilisés universellement par presque tous les composants/modules dans le codebase.

  2. AOP définit aspects (classes/méthodes) résumé ces transversales préoccupations avec l'utilisation de rejoindre les points , conseils , et pointcuts .

    A. Conseil - le code réel (méthode d'un aspect, peut-être?) mettre en œuvre la préoccupation transversale (c'est-à-dire effectuer la journalisation proprement dite, valider, l'authentification, etc.)

    B. Join Point - événement déclenché dans un code non-AOP qui fait exécuter un conseil d'aspect particulier ("tissé "dans le code non-AOP)

    C. Pointcut - essentiellement, une cartographie des points de jointure (événements de déclenchement) à l'exécution de conseil

  3. tous les aspects sont modularisés (LoggingAspect, AuthenticationAspect, ValidationAspect, etc.) en composants et enregistrés avec un Spectweaver . Lorsque le code non-AOP/POJO tombe sur un point de jointure, L'AspectWeaver" tisse "(intègre) le Conseil cartographié autour du code non-AOP:

public class LoggingAspect
{
    // ...

    public void log(String msg) { ... }
}

public class ExceptionHandlingAspect
{
    // ..

    public void handle(Exception exc) { ... }
}

public class NonAOPCode
{
    // ...

    @LoggingAspect @ExceptionHandlingAspect
    public void foo()
    {
        // do some stuff...
    }
}

// Now in the driver
public static int main void(String[] args)
{
    NonAOPCode nonAOP = new NonAOPCode();
    nonAOP.foo();
}

// The AspectWeaver *magically* might weave in method calls so main now becomes:
{
    NonAOPCode nonAOP = new NonAOPCode();

    log(someMsg);
    nonAOP.foo();
    handle(someExc);
}

$64,000 Question: Est ma compréhension de Java à base d'AOP sur la cible, ou la façon de, et pourquoi? Comment pourrait-on correctement utiliser des annotations pour mettre en œuvre aspects, conseils, join points, pointcuts et ce soi-disant aspect weaver?

60
demandé sur Community 2011-01-28 17:07:15

5 réponses

imaginons que vous vouliez enregistrer le temps pris par certaines méthodes annotées en utilisant une annotation @LogExecTime .

je crée d'abord une annotation LogExecTime :

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecTime {

}

puis je définis un aspect:

@Component  // For Spring AOP
@Aspect
public class LogTimeAspect {
    @Around(value = "@annotation(annotation)")
    public Object LogExecutionTime(final ProceedingJoinPoint joinPoint, final LogExecTime annotation) throws Throwable {
        final long startMillis = System.currentTimeMillis();
        try {
            System.out.println("Starting timed operation");
            final Object retVal = joinPoint.proceed();
            return retVal;
        } finally {
            final long duration = System.currentTimeMillis() - startMillis;
            System.out.println("Call to " + joinPoint.getSignature() + " took " + duration + " ms");
        }

    }
}

je crée une classe annotée avec LogExecTime :

@Component
public class Operator {

    @LogExecTime
    public void operate() throws InterruptedException {
        System.out.println("Performing operation");
        Thread.sleep(1000);
    }
}

et un main utilisant le ressort AOP:

public class SpringMain {

    public static void main(String[] args) throws InterruptedException {
        ApplicationContext context = new GenericXmlApplicationContext("applicationContext.xml");
        final Operator bean = context.getBean(Operator.class);
        bean.operate();
    }
}

si je cours cette classe je vais obtenir la sortie suivante sur stdout:

Starting timed operation
Performing operation
Call to void testaop.Operator.Operate() took 1044 ms

Maintenant, avec le magie . Comme j'ai utilisé Spring AOP plutôt qu'AspectJ weaver, la magie se produit au moment de l'exécution en utilisant des mécanismes de proxy-ish. Ainsi ,les fichiers .class sont laissés intacts. Par exemple, si je debug ce programme et mettre un point de rupture dans operate vous verrez comment le printemps a exécuté la magie:

Debug screen shot

As L'implémentation AOP de printemps est non-intrusive et utilise les mécanismes de ressort dont vous avez besoin pour ajouter l'annotation @Component et créer l'objet en utilisant le contexte de ressort plutôt que le simple new .

AspectJ de l'autre côté va changer les fichiers .class . J'ai essayé ce projet avec AspectJ et j'ai décomposé la classe opérateur avec jad. Qui conduisent à:

public void operate()
    throws InterruptedException
{
    JoinPoint joinpoint = Factory.makeJP(ajc$tjp_0, this, this);
    operate_aroundBody1$advice(this, joinpoint, LogTimeAspect.aspectOf(), (ProceedingJoinPoint)joinpoint, (LogExecTime)(ajc$anno"151950920" == null && (ajc$anno"151950920" = testaop/Operator.getDeclaredMethod("operate", new Class[0]).getAnnotation(testaop/LogExecTime)) == null ? ajc$anno"151950920" : ajc$anno"151950920"));
}

private static final void operate_aroundBody0(Operator ajc$this, JoinPoint joinpoint)
{
    System.out.println("Performing operation");
    Thread.sleep(1000L);
}

private static final Object operate_aroundBody1$advice(Operator ajc$this, JoinPoint thisJoinPoint, LogTimeAspect ajc$aspectInstance, ProceedingJoinPoint joinPoint, LogExecTime annotation)
{
    long startMillis = System.currentTimeMillis();
    Object obj;
    System.out.println("Starting timed operation");
    ProceedingJoinPoint proceedingjoinpoint = joinPoint;
    operate_aroundBody0(ajc$this, proceedingjoinpoint);
    Object retVal = null;
    obj = retVal;
    long duration = System.currentTimeMillis() - startMillis;
    System.out.println((new StringBuilder("Call to ")).append(joinPoint.getSignature()).append(" took ").append(duration).append(" ms").toString());
    return obj;
    Exception exception;
    exception;
    long duration = System.currentTimeMillis() - startMillis;
    System.out.println((new StringBuilder("Call to ")).append(joinPoint.getSignature()).append(" took ").append(duration).append(" ms").toString());
    throw exception;
}

private static void ajc$preClinit()
{
    Factory factory = new Factory("Operator.java", testaop/Operator);
    ajc$tjp_0 = factory.makeSJP("method-execution", factory.makeMethodSig("1", "operate", "testaop.Operator", "", "", "java.lang.InterruptedException", "void"), 5);
}

private static final org.aspectj.lang.JoinPoint.StaticPart ajc$tjp_0; /* synthetic field */
private static Annotation ajc$anno"151950920"; /* synthetic field */

static 
{
    ajc$preClinit();
}
62
répondu gabuzo 2011-01-31 08:00:29

il y a quelques mois, j'ai écrit un article avec un exemple sur la façon dont j'ai mis en œuvre un cas pratique de combinaison Aspect/J aspects avec des annotations Java, que vous pouvez trouver utile:

http://technomilk.wordpress.com/2010/11/06/combining-annotations-and-aspects-part-1 /

je crois que les aspects appliqués aux annotations font une bonne combinaison parce qu'ils rendent l'aspect plus explicite dans votre code, mais d'une manière propre, et vous pouvez utiliser des paramètres dans vos annotations pour plus de flexibilité.

BTW la façon dont L'Aspect/J fonctionne est de modifier vos classes au moment de la compilation, pas au moment de l'exécution. Vous exécutez vos sources et aspects à travers le compilateur Aspect/J et il crée les fichiers de classe modifiés.

Spring AOP, autant que je le comprenne, fait le tissage (manipulation des fichiers de classe pour inclure le traitement d'aspect) d'une manière différente, en créant des objets proxy, je crois qu'à l'instanciation de temps (mais ne prenez pas mon mot pour lui).

12
répondu Carles Barrobés 2011-01-28 21:40:15

a trouvé la réponse moi-même après avoir creusé beaucoup et de l'huile de coude...

Oui, AOP devrait être basé sur les annotations dans le monde de Java, mais vous ne pouvez pas traiter les annotations liées aux aspects comme les annotations régulières (métadonnées). Pour intercepter un appel de méthode étiqueté et des méthodes de conseil "tisser" avant / après, vous avez besoin de l'aide de quelques moteurs AOP-centric très astucieux tels que L'AspectJ. Une très belle solution a été proposée par @Christopher McCann dans une autre annotation liée thread, où il a suggéré l'utilisation D'AOP Alliance en conjonction avec Google Guice. Après avoir lu la documentation de Guice sur le support AOP, c'est exactement ce que je cherche: un cadre simple à comprendre pour tisser dans le "Conseil" (appels de méthodes) des préoccupations transversales, telles que la journalisation, la validation, la mise en cache, etc.

celui-ci était un crétin.

4
répondu Eugie 2011-01-28 20:53:01

modifier le commentaire

// The AspectWeaver *magically* might weave in method calls so main now becomes

à

// The AspectWeaver *magically* might weave in method calls so main now
// becomes effectively (the .class file is not changed)

j'aime la version de printemps d'AOP. Découvrez Chapitre 7

0
répondu DwB 2011-01-31 21:46:07

Voici ma contribution à ce post très utile.

nous allons prendre un exemple très simple: nous devons prendre des mesures sur le traitement de certaines méthodes. Ils sont annotés avec des annotations personnalisées, qui contiennent des données à manipuler. Compte tenu de ces données, nous voulons soulever une exception ou laisser le processus continuer comme la méthode n'a pas été annotée.

le code Java pour définir notre aspect:

package com.example;

public class AccessDeniedForCustomAnnotatedMethodsAspect {

public Object checkAuthorizedAccess(ProceedingJoinPoint proceedingJointPoint)
throws Throwable {

    final MethodSignature methodSignature = (MethodSignature) proceedingJointPoint
                                            .getSignature();

    // how to get the method name
    final String methodName = methodSignature
                                            .getMethod()
                                            .getName();

    // how to get the parameter types
    final Class<?>[] parameterTypes = methodSignature
                                            .getMethod()
                                            .getParameterTypes();

    // how to get the annotations setted on the method
    Annotation[] declaredAnnotations = proceedingJointPoint
                                            .getTarget()
                                            .getClass()
                                            .getMethod(methodName, parameterTypes)
                                            .getDeclaredAnnotations();

    if (declaredAnnotations.length > 0) {

        for (Annotation declaredAnnotation : Arrays.asList(declaredAnnotations)) {

            // I just want to deal with the one that interests me
            if(declaredAnnotation instanceof CustomAnnotation) {

                // how to get the value contained in this annotation 
                (CustomAnnotation) declaredAnnotation).value()

                if(test not OK) {
                    throw new YourException("your exception message");
                }

                // triggers the rest of the method process
                return proceedingJointPoint.proceed();
           }
        }
    }
}

la configuration xml :

<aop:config>
    <aop:aspect id="accessDeniedForCustomAnnotatedMethods"
               ref="accessDeniedForCustomAnnotatedMethodsAspect">
        <aop:around pointcut="execution(@xxx.zzz.CustomAnnotation * *(..))"
               method="checkAuthorizedAccess" />
    </aop:aspect>
</aop:config>

<bean id="accessDeniedForCustomAnnotatedMethodsAspect"
   class="xxx.yyy.AccessDeniedForCustomAnnotatedMethodsAspect" />

Espère que cela aide !

0
répondu lboix 2015-11-28 00:53:18