Java面向方面编程与注释

在一篇题为“AOP基础”的文章中 ,我要求国王的英文解释AOP是什么,它做了什么。 我收到了一些非常有帮助的答案,并链接了那些帮助我理解所有理论的文章。

但是现在AOP得到了我的全部关注,所有这些文章和章节摘录都是非常棒的,但是在任何情况下,它们都是由崇高的理论,模糊的UML模型和抽象的顺序组成的。

这里是我对AOP理论的理解,只是为了澄清,所以如果你看到一些看起来不对的东西,让我知道!

  1. 日志logging,身份validation,同步,validation,exception处理等交叉问题在非AOP系统中变得高度耦合,因为它们几乎被代码库中的每个组件/模块普遍使用。

  2. AOP定义了使用连接点build议切入点来抽象这些横切关注点的 方面 (类/方法)。

    一个。 build议 – 实际的代码(一个方面的方法,也许?)实现交叉的关注(即做实际的日志logging,validation,authentication等)

    join点Join Point) – 在非AOP代码中触发的事件,它导致特定方面的build议被执行(“编织”为非AOP代码)

    C。 切入点 – 实质上是连接点(触发事件)到build议执行的映射

  3. 所有方面都被模块化(LoggingAspect,AuthenticationAspect,ValidationAspect等)成组件并注册一个AspectWeaver 。 当非AOP / POJO代码遇到连接点时,AspectWeaver围绕非AOP代码“编织”(集成)映射build议:

公共类LoggingAspect
 {
     // ...

    公共无效的日志(string味精){...}
 }

公共类ExceptionHandlingAspect
 {
     // ...

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

公共类NonAOPCode
 {
     // ...

     @LoggingAspect @ExceptionHandlingAspect
     public void foo()
     {
         //做一些东西...
     }
 }

 //现在在驱动程序中
公共静态诠释主要无效(string[]参数)
 {
     NonAOPCode nonAOP = new NonAOPCode();
     nonAOP.foo();
 }

 // AspectWeaver *神奇*可能在方法调用中编织,现在主要变成:
 {
     NonAOPCode nonAOP = new NonAOPCode();

    日志(someMsg);
     nonAOP.foo();
    处理(someExc);
 }

64000美元问题:我是否理解了基于Java的AOP的目标或途径,为什么? 如何正确使用注释来实现方面,build议,连接点,切入点和这个所谓的方面编织器?

假设您想使用@LogExecTime批注logging一些@LogExecTime注释的方法所用的@LogExecTime

我首先创build一个注解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"); } } } 

我创build了一个用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(); } } 

如果我运行这个类,我得到了标准输出上的以下输出:

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

现在与魔术 。 就像我使用Spring AOP而不是AspectJ Weaver一样,使用代理机制在运行时发生了魔法。 所以.class文件保持不变。 例如,如果我debugging这个程序,并在operate放置一个断点,您将看到Spring如何执行这个魔术:

调试屏幕截图

由于Spring的AOP实现是非侵入式的,并且使用了Spring机制,所以需要添加@Component注解并使用Spring上下文而不是纯new创build对象。

另一方面的AspectJ将改变.class文件。 我用AspectJ试过这个项目,并用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(); } 

几个月前,我写了一篇关于如何实现将Aspect / J方面与Java注释相结合的实例的文章,您可能会发现它很有用:

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

我相信应用于注释的方面是一个很好的组合,因为它们使得代码中的方面更加明确,但干净利落,您可以在注释中使用参数以获得更大的灵活性。

顺便说一句Aspect / J的工作方式是在编译时修改你的类,而不是在运行时。 您通过Aspect / J编译器运行源代码和方面,并创build修改后的类文件。

就我所知,Spring AOP以不同的方式编织(操纵类文件以包含方面处理),通过创build代理对象,我相信在实例化时(但不要拿我的话来说) 。

挖掘和肘部油脂后自己find答案…

是的,AOP在Java世界中应该是基于注释的,但是不能像普通(元数据)注释那样处理与方面相关的注释。 要拦截标记的方法调用,并在它之前/之后“编织”通知方法,您需要一些非常漂亮的以AOP为中心的引擎(如AspectJ)的帮助。 @Christopher McCann在另一个与注释相关的主题中提供了一个非常好的解决scheme,他build议将AOP联盟与Google Guice结合使用。 在阅读Guice关于AOP支持的文档之后,这正是我正在寻找的:一个简单易懂的框架,用于在交叉关注的“build议”(方法调用)中进行编织,例如日志logging,validation,caching,等等

这是一个很酷的。

改变评论

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

我喜欢AOP的spring写作。 看看第七章

这是我对这个非常有用的职位的贡献。

我们将举一个非常简单的例子:我们需要对某些方法的处理采取行动。 它们使用包含要处理的数据的自定义注释进行注释。 给定这个数据,我们想引发一个exception,或者让这个过程继续下去,就像方法没有注释一样。

用于定义我们方面的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(); } } } } 

xmlconfiguration:

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

希望能帮助到你 !