面向Java的面向方面编程和注释

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (64)

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

  1. 诸如日志记录,身份验证,同步,验证,异常处理等交叉问题在非AOP系统中变得高度耦合,因为它们几乎被代码库中的每个组件/模块普遍使用。
  2. AOP定义了通过使用连接点建议切入点来抽象这些横切关注点的方面(类/方法)。 一个。建议 - 实际的代码(某个方面的方法,或许?)实现交叉关注(即进行实际的日志记录,验证,认证等) 湾 加入点 - 在非AOP代码中触发的事件,它导致特定方面的建议被执行(“编织”为非AOP代码) C。切入点 - 实质上是连接点(触发事件)到建议执行的映射
  3. 所有方面都被模块化(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);
}

是否对目标或基于Java的AOP有所了解,以及为什么?如何正确使用注释来实现方面,建议,连接点,切入点和这个所谓的方面编织器?

提问于
用户回答回答于

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

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

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

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

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

用户回答回答于

假设您想使用@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 Weaver一样,这种魔法在运行时使用代理机制发生。所以.class文件保持不变。例如,如果我调试这个程序并在operate你放置一个断点,你会看到Spring如何表现神奇:

由于Spring AOP实现是非侵入式的并且使用Spring机制,因此需要添加@Component注释并使用Spring上下文而不是普通的方式创建对象new

另一方面的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();
}

扫码关注云+社区