首页 > 解决方案 > SpringAOP 介绍与 AspectJ 使用 Annotations

问题描述

我正在学习介绍(在 AspectJ 中称为类型间声明)。我从使用 xml的 AspectJ 的 SpringAOP 介绍中得到了一个示例。我正在尝试使用 annotations 复制相同的内容,但我不知道如何继续。我在互联网上做了很多研究,但找不到任何样本。你能帮我解决这个问题吗?

性能测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ConcertConfig.class)
public class PerformanceTest {

    @Autowired
    public Audience audience;

    @Autowired
    public Performance liveOpera;

    @Autowired
    public EncoreableIntroducer encoreable;

    @Test
    public void testPerformance(){
        liveOpera.perform();
    }
}

LiveOpera.class

public class LiveOpera implements Performance{
    @Override
    public void perform() {
        System.out.println("Live Opera Performance Started");   
    }}

Encoreable.interface

public interface Encoreable {
    public void performEncore();
}

DefaultEncoreable.class

public class DefaultEncoreable implements Encoreable {
    @Override
    public void performEncore() {
        System.out.println("WoW!! What an encore performance!!");
    }
}

ConcertConfig.class

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig {

    @Bean
    public Audience audience(){
        return new Audience();
    }

    @Bean
    public LiveOpera opera(){
        return new LiveOpera();
    }

    @Bean
    public EncoreableIntroducer encoreable(){
        return new EncoreableIntroducer();
    }

}

性能接口

public interface Performance {
    public void perform();
}

EncoreableIntroducer.class

@Aspect
public class EncoreableIntroducer {
    @DeclareParents(value="com.example.introduction.Performance+",
            defaultImpl=DefaultEncoreable.class)
    public static Encoreable encoreable;
}

观众类

@Aspect
public class Audience {

    @Pointcut("execution(** com.example.introduction.Performance.perform(..))")
    public void performance() {
    }

    public void silenceMobilePhones() {
        System.out.println("Silencing Mobile Phones");
    }

    public void takeSeats() {
        System.out.println("Taking seats");
    }

    public void applause() {
        System.out.println("CLAP CLAP CLAP!!");
    }

    public void demandRefund() {
        System.out.println("Need a Refund!!");
    }

    @Around("performance()")
    public void wrapPerformance(ProceedingJoinPoint jp) {
        try {
            silenceMobilePhones();
            takeSeats();
            jp.proceed();
            applause();
        } catch (Throwable e) {
            System.out.println(e);
            e.printStackTrace();
            demandRefund();
        }
    }
}

输出:在 PerformanceTest.class 中执行 @Test 方法时

Silencing Mobile Phones
Taking seats
Live Opera Performance Started
CLAP CLAP CLAP!!

请告诉我,如何使用 AspectJ-Introductions 使用 EncoreableIntroducer.class 中的方面,以便可以使用 DefaultEncoreable.class 中的 perform() 方法?

在使用 AspectJ Introductions 时期望如下输出:

Silencing Mobile Phones
Taking seats
WoW!! What an encore performance!!
Live Opera Performance Started
CLAP CLAP CLAP!!

标签: javaspringspring-bootaspectjspring-aop

解决方案


您在这里遇到了几个问题:

  • 您正在使用实现相同方法的两个接口( Performancevs. )。Encoreable为什么?
  • 您正在尝试引入LiveOpera它已经拥有的方法,即void perform(). 可能您的意图是覆盖原来的。这在 Spring AOP 和 AspectJ 中都不起作用。您只能引入目标类中尚不存在的方法。
  • 您正试图在另一个方面 B 中拦截方面 A 引入的方法。据我所知,这只适用于 AspectJ,而不适用于 Spring AOP。我不是 Spring 用户,但很快尝试了一下,但它不起作用。在 AspectJ 中复制您的情况时,它起作用了。所以你可以做的是在你的 Spring 应用程序中通过 LTW 激活 AspectJ并修改你的代码以使其工作,避免我描述的前两个问题。

更新并回答评论中提出的问题:

首先,我摆脱@Bean了配置类中的手动方法,因为无论如何您都启用了组件扫描。相反,我只是在类和两个方面添加了@Component注释。LiveOpera你的方法也有效,但我更喜欢这个:

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig {}

看起来更干净,不是吗?只是不要忘记@Component它们所属的注释。

现在,真正的解决方案。在这方面,将您的建议更改为:

@Around("performance() && this(encoreable)")
public void wrapPerformance(ProceedingJoinPoint jp, Encoreable encoreable) {
  try {
    silenceMobilePhones();
    takeSeats();
    encoreable.performEncore();
    jp.proceed();
    applause();
  } catch (Throwable e) {
    System.out.println(e);
    e.printStackTrace();
    demandRefund();
  }
}

推荐阅读