面向Java面向方面的注释编程 [英] Java Aspect-Oriented Programming with Annotations

查看:245
本文介绍了面向Java面向方面的注释编程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一篇名为AOP Fundamentals的帖子中,我要求提供 King's English 解释AOP是什么,以及它的作用。我收到了一些非常有用的答案和链接到文章,这些文章帮助我完成了所有的理论。



但现在AOP得到我的全力关注,所有这些文章和章节摘录非常棒,但是在每一个案例中,它们都包含了崇高的理论,模糊的UML模型,以及抽象的顺序,这些都是我喜欢的过高。



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


  1. 日志记录,身份验证,同步,验证,异常处理等交叉问题在非AOP系统中变得高度耦合,因为它们几乎被代码库中的每个组件/模块广泛使用。 / p>


  2. AOP定义方面(类/方法),通过使用连接点来抽象这些横切关注点strong>,建议切入点



    一个。 建议 - 实现跨领域关注(即进行实际记录,验证,验证等)的实际代码(方面的方法,可能?)



    b。 加入点 - 在非AOP代码中触发的事件,导致特定方面的建议被执行(编织到非AOP代码中)



    c。 Pointcut - 基本上,连接点(触发事件)到建议执行的映射


  3. 所有方面都是模块化的(LoggingAspect,AuthenticationAspect, ValidationAspect等)到组件中并使用 AspectWeaver 进行注册。当非AOP / POJO代码遇到连接点时,AspectWeaver会围绕非AOP代码编织(集成)映射的建议:




 
公共类LoggingAspect
{
// ...

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

公共类ExceptionHandlingAspect
{
// ..

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

公共类NonAOPCode
{
// ...

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

//现在在驱动程序
public static int main void(String [] args)
{
NonAOPCode nonAOP = new NonAOPCode();
nonAOP.foo();
}

// AspectWeaver *可能会在方法调用中编织,因此main现在变为:
{
NonAOPCode nonAOP = new NonAOPCode();

log(someMsg);
nonAOP.foo();
句柄(someExc);
}

$ 64,000问题:我对基于Java的AOP的理解目标,或关闭,为什么?一个正确如何使用注释来实现方面,建议,连接点,切入点和这个所谓的方面编织器?

解决方案

假设您想要使用 @LogExecTime 注释记录一些带注释方法所花费的时间。



我首先创建一个注释 LogExecTime

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

}

然后我定义一个方面:

  @Component //对于Spring AOP 
@Aspect
public class LogTimeAspect {
@Around(value =@ annotation(annotation))
public Object LogExecutionTime( final ProceedingJoinPoint joinPoint,final LogExecTime annotation)抛出Throwable {
final long startMillis = System.currentTimeMillis();
try {
System.out.println(开始定时操作);
final Object retVal = joinPoint.proceed();
返回retVal;
} finally {
final long duration = System.currentTimeMillis() - startMillis;
System.out.println(Call to+ joinPoint.getSignature()+take+ 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上的输出:

 开始计时操作
执行操作
调用void testaop.Operator.Operate( )花了1044毫秒

现在使用 magic 。正如我使用Spring AOP而不是AspectJ weaver一样,魔术是在运行时使用proxy-ish机制发生的。所以 .class 文件保持不变。例如,如果我调试这个程序并在操作中放置一个断点,你就会看到Spring如何表现出魔力:





作为Spring AOP实施非侵入式并使用你需要的Spring机制添加 @Component 注释并使用Spring上下文而不是普通的<$ c创建对象$ c> new 。



另一方面的AspectJ将更改 .class 文件。我用AspectJ尝试了这个项目,用jad反编译了Operator类。导致:

  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( 操作,新类[0])。getAnnotation(testaop / LogExecTime))== null?ajc $ anno $ 0:ajc $ anno $ 0));
}

private static final void operate_aroundBody0(运算符ajc $ this,JoinPoint joinpoint)
{
System.out.println(执行操作);
Thread.sleep(1000L);
}

private static final Object operate_aroundBody1 $ advice(运营商ajc $ this,JoinPoint thisJoinPoint,LogTimeAspect ajc $ aspectInstance,ProceedingJoinPoint joinPoint,LogExecTime注释)
{
long startMillis = System.currentTimeMillis();
Object obj;
System.out.println(开始定时操作);
ProceedingJoinPoint proceedingjoinpoint = joinPoint;
operate_aroundBody0(ajc $ this,proceedingjoinpoint);
Object retVal = null;
obj = retVal;
long duration = System.currentTimeMillis() - startMillis;
System.out.println((新的StringBuilder(Call to))。append(joinPoint.getSignature())。append(take)。append(duration).append(ms)。toString ());
返回obj;
异常例外;
例外;
long duration = System.currentTimeMillis() - startMillis;
System.out.println((新的StringBuilder(Call to))。append(joinPoint.getSignature())。append(take)。append(duration).append(ms)。toString ());
抛出异常;
}

private static void ajc $ preClinit()
{
工厂工厂=新工厂(Operator.java,testaop / Operator);
ajc $ tjp_0 = factory.makeSJP(method-execution,factory.makeMethodSig(1,operation,testaop.Operator,,,java.lang.InterruptedException, 无效),5);
}

private static final org.aspectj.lang.JoinPoint.StaticPart ajc $ tjp_0; / * synthetic field * /
private static Annotation ajc $ anno $ 0; / *合成字段* /

static
{
ajc $ preClinit();
}


In a post entitled "AOP Fundamentals", I asked for a King's English explanation of what AOP is, and what it does. I received some very helpful answers and links to articles that helped fill me in on all the theory.

But now AOP's got my full attention, and all these articles and chapter excerpts are fantastic, but in every single case they consist of lofty theory, vague UML models, and order of abstraction that are way too high-up for my liking.

Here is my understanding of AOP theory, just to clarify, so if you see something that looks wrong, let me know!:

  1. Cross-cutting concerns such as Logging, Authenticating, Synchronizing, Validating, Exception Handling, etc. become highly-coupled in non-AOP systems as they are used universally by almost every component/module in the codebase.

  2. AOP defines aspects (classes/methods) that abstract these cross-cutting concerns with the use of join points, advice, and pointcuts.

    a. Advice - The actual code (method of an aspect, perhaps?) implementing the cross-cutting concern (i.e. doing the actual logging, validating, authenticating, etc.)

    b. Join Point - An event that is triggered in non-AOP code that causes a particular aspect's advice to be executed ("woven" into the non-AOP code)

    c. Pointcut - Essentially, a mapping of join points (triggering events) to advice execution

  3. All aspects are modularized (LoggingAspect, AuthenticationAspect, ValidationAspect, etc.) into components and registered with an AspectWeaver. When non-AOP/POJO code comes across a join point, AspectWeaver "weaves" (integrates) the mapped advice around the non-AOP code:

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

The $64,000 Question: Is my understanding of Java-based AOP on target, or way off, and why? How could one correctly use annotations to implement aspects, advice, join points, pointcuts and this so-called aspect weaver?

解决方案

Let's imagine you want to log the time taken by some annoted methods using a @LogExecTime annotation.

I first create an annotation LogExecTime:

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

}

Then I define an 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");
        }

    }
}

I create a class annoted with LogExecTime:

@Component
public class Operator {

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

And a main using 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();
    }
}

If I run this class I'm getting the following output on stdout:

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

Now with the magic. As I did use Spring AOP rather than AspectJ weaver, the magic is occurring at run time using proxy-ish mechanisms. So the .class files are left untouched. For instance if I debug this program and put a breakpoint in operate you'll see how Spring has performed the magic:

As Spring AOP implementation is non-intrusive and uses the Spring mechanisms you need to add the @Component annotation and create the object using Spring context rather than plain new.

AspectJ on the other side will change the .class files. I tried this project with AspectJ and decompiled the Operator class with jad. Which lead to:

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

这篇关于面向Java面向方面的注释编程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆