Подтвердить что ты не робот

Java-ориентированное программирование с аннотациями

В сообщении под названием "Основы AOP" , я попросил объяснение короля на английском языке о том, что такое АОП, и что он делает. Я получил очень полезные ответы и ссылки на статьи, которые помогли мне заполнить всю теорию.

Но теперь AOP привлекло мое внимание, и все эти статьи и выдержки из главы являются фантастическими, но в каждом отдельном случае они состоят из высокой теории, неопределенных моделей UML и порядка абстракции, которые являются способом слишком высоко для меня.

Вот мое понимание теории АОП, просто чтобы уточнить, поэтому, если вы видите что-то, что выглядит неправильно, дайте мне знать!:

  • Такие сквозные проблемы, как ведение журнала, аутентификация, синхронизация, проверка, обработка исключений и т.д., становятся высокосвязными в системах, отличных от AOP, поскольку они используются повсеместно почти каждым компонентом/модулем в базе кода.

  • AOP определяет аспекты (классы/методы), которые абстрагируют эти сквозные проблемы с использованием точек соединения, рекомендаций, pointcuts.

    а. Совет. Фактический код (метод аспекта, возможно?), Реализующий сквозную задачу (например, выполнение фактического ведения журнала, проверка подлинности, аутентификация и т.д.).

    б. Присоединительная точка - событие, которое запускается в коде, отличном от AOP, который вызывает конкретный совет по аспектам, который должен выполняться ( "сотканный" в не-AOP-код)

    с. Pointcut - по существу, отображение точек соединения (инициирующих событий) для выполнения рекомендаций

  • Все аспекты модулярны (LoggingAspect, AuthenticationAspect, ValidationAspect и т.д.) на компоненты и зарегистрированы с помощью AspectWeaver. Когда код не-AOP/POJO встречается с точкой соединения, AspectWeaver "переплетает" (интегрирует) сопоставленный совет вокруг кода, отличного от 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: Является ли мое понимание AOP на основе Java на целевом или удачном пути и почему? Как один правильно использовать аннотации для реализации аспектов, советов, точек соединения, точек и этого так называемого ткача ткачества?

4b9b3361

Ответ 1

Предположим, вы хотите записать время, затраченное некоторыми аннотированными методами, используя аннотацию @LogExecTime.

Сначала создаю аннотацию LogExecTime:

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

}

Затем я определяю аспект:

@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");
        }

    }
}

Я создаю класс, аннотированный с помощью LogExecTime:

@Component
public class Operator {

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

И основное использование Spring 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();
    }
}

Если я запускаю этот класс, я получаю следующий вывод на stdout:

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

Теперь с магией. Так как я использовал Spring AOP, а не ткач AspectJ, магия происходит во время выполнения с использованием механизмов прокси-иша. Таким образом, файлы .class остаются нетронутыми. Например, если я отлаживаю эту программу и помещаю точку останова в operate, вы увидите, как Spring выполнил магию:

Debug screen shot

Как Spring Реализация AOP не является навязчивой и использует механизмы Spring, вам нужно добавить аннотацию @Component и создать объект, используя Spring контекст, а не простой new.

AspectJ с другой стороны изменит файлы .class. Я пробовал этот проект с AspectJ и декомпилировал класс Operator с jad. Что приводит к:

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$0 == null && (ajc$anno$0 = testaop/Operator.getDeclaredMethod("operate", new Class[0]).getAnnotation(testaop/LogExecTime)) == null ? ajc$anno$0 : ajc$anno$0));
}

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$0; /* synthetic field */

static 
{
    ajc$preClinit();
}

Ответ 2

Несколько месяцев назад я написал статью с примером того, как я применил практический пример объединения аспектов Aspect/J с аннотациями Java, которые могут оказаться полезными:

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

Я считаю, что аспекты, примененные к аннотациям, делают хорошую комбинацию, потому что они делают аспект более явным в вашем коде, но чистым способом, и вы можете использовать параметры в своих аннотациях для дальнейшей гибкости.

Кстати, Aspect/J работает, изменяя ваши классы во время компиляции, а не во время выполнения. Вы запускаете свои источники и аспекты через компилятор Aspect/J и создаете измененные файлы классов.

Spring AOP, насколько я понимаю, делает переплетение (манипулирование файлами класса для включения обработки аспектов) по-другому, создавая прокси-объекты, я считаю, что во время создания экземпляра (но не принимаю мое слово для этого).

Ответ 3

Нашел ответ сам после много рытья и локтя...

Да, AOP должен быть основан на аннотациях в мире Java, однако вы не можете обрабатывать аннотации, связанные с аспектами, как обычные аннотации (метаданные). Чтобы перехватить метод меток с тегами и "переплетать" советы до/после него, вам нужна помощь некоторых очень полезных AOP-ориентированных движков, таких как AspectJ. Очень приятное решение было предложено @Christopher McCann в другой теме, связанной с аннотациями, где он предложил использовать AOP Alliance в сочетании с Google Guice. После прочтения документации Guice по поддержке AOP это именно то, что я ищу: простая в понимании структура для плетения в "советах" (вызовы методов) сквозных проблем, таких как ведение журнала, проверка, кеширование, и др.

Это было douzy.

Ответ 4

Изменить комментарий

// 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)

Мне нравится spring запись АОП. Проверьте Глава 7

Ответ 5

Вот мой вклад в этот очень полезный пост.

Мы рассмотрим очень простой пример: нам нужно принять меры для обработки некоторых методов. Они аннотируются с пользовательскими аннотациями, которые содержат данные для обработки. Учитывая эти данные, мы хотим создать исключение или продолжить процесс, как метод не был аннотирован.

Код Java для определения нашего аспекта:

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();
           }
        }
    }
}

Конфигурация 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" />

Надеюсь, что это поможет!