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 на целевом или удачном пути и почему? Как один правильно использовать аннотации для реализации аспектов, советов, точек соединения, точек и этого так называемого ткача ткачества?
Ответы
Ответ 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" />
Надеюсь, что это поможет!