如何将对象作为参数传递给 spring 方面?

How to pass object as parameter to a spring aspect?

假设我需要用 @MusicAround 建议增强 shower() 方法,以便在执行 shower() 方法前后给我一些音乐。

public class Me {
    @MusicAround
    public void shower() {
        // shower code omitted
    }
}

首先我创建了新注释 @MusicAround

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

然后绑定一个aspect MusicAspect.

@Aspect
public class MusicAspect {
    @Around("@annotation(MusicAround)")
    public Object musicAround(ProceedingJoinPoint joinPoint) throws Throwable {
        IPhone iphone = new IPhone();
        Iphone.music();
        joinPoint.proceed();
        iphone.music();
    }
}

MusicAspect 配置为 Bean@EnableAspectJAutoProxy 注释留下 spring 为我封装方面代理。

@Configuration
@EnableAspectJAutoProxy
public class ApplicationConfig {
    // ... other beans omitted

    @Bean 
    public MusicAspect musicAspect() {
        return new MusicAspect();
    }
}

在 main 方法中,从上下文中获取 Me 实例,并执行 shower() 方法。

public static void main(String[] args) {
    try {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
        Me me = context.getBean(Me.class);
        me.shower();
        context.close();
    } catch (ApplicationContextException ace) {
        // handle exception
    }
}

现在我可以在淋浴时听音乐了。

<hey jude>
I'm showering
<don't be so serious>

问题是这样 MusicAspect class 和 IPhone class 耦合了。我想通过注入 IPhone 对象作为参数来分离它们,如下所示,

@Aspect
public class MusicAspect {
    @Around("@annotation(MusicAround)")
    public Object musicAround(ProceedingJoinPoint joinPoint, IPhone iphone) throws Throwable {
        iphone.music();
        joinPoint.proceed();
        iphone.music();
    }
}

当然,musicAround() 方法中的第二个参数“iphone”在这里是不允许的。在这种情况下,我可以使用任何 spring 功能来分离 IPhoneMusicAspect 吗?

!注:感谢@kriegaex 校对

这是初步的回答,因为内容不适合评论

在查看您更新后的问题中的代码时,有些事情让我觉得很奇怪:

  • 我想知道为什么每个人都如此热衷于始终将方面与注释结合使用。为什么不使用直接针对包、classes 或感兴趣的方法的切入点?如果不是绝对必要的话,所有这些注释污染都是可怕的。理想情况下,应用程序代码应该完全不知道方面的存在。
  • 您错误地使用了 @annotation 切入点指示符。而不是 @annotation(@MusicAround),它应该是 @annotation(MusicAround)。但这也仅在注释恰好与方面位于完全相同的包中时才有效,否则您需要 @annotation(fully.qualified.package.name.MusicAround).
  • 您使用 MusicAspect,但随后声明了一个 MinstrelAroundAdvice bean。这似乎不匹配。此外,切面就是切面,它里面实际做某事的方法就是建议。因此,某个方面的 class 名称 *Advice 完全是错误的。最好改用 *Aspect 或其他可以正确描述方面功能的东西。在这种情况下,MusicAspect 对我来说似乎很好。

现在关于你的实际问题,我还不清楚。它是关于如何将另一个 bean 注入(自动连接)到方面实例中吗?

Of course I was not allowed to do so.

为什么是“当然”?什么是不允许的?你是怎么注意到的?是不是有什么问题?您收到错误消息了吗?堆栈跟踪?请清楚说明您尝试了什么,预期结果是什么以及发生了什么。使您的问题可重现。不幸的是,您的代码片段不会这样做。试想一下,如果您没有看到完整的代码,也没有了解问题所需的其他上下文信息,其他人会问您同样的问题。你能回答吗?如果你的助手不明白这个问题,他们怎么能回答你的问题呢?请注意了解什么是 MCVE

@k-wasilewski@kriegaex 的帮助下解决了问题。谢谢兄弟

答案是@Autowired注解

IPhone 定义为 MusicAspect class 的字段。添加 @Autowired 标签,告诉 spring 上下文为我们初始化 IPhone 实例。

@Aspect
public class MusicAspect {

    @Autowired
    private IPhone iphone;

    @Around("@annotation(MusicAround)")
    public Object musicAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Iphone.music();
        joinPoint.proceed();
        iphone.music();
    }

}

不要忘记在 ApplicationConfig 中注册 IPhone bean。其余部分保持不变。

@Configuration
@EnableAspectJAutoProxy
public class ApplicationConfig {
    // ... other beans omitted

    @Bean 
    public IPhone iphone() {
        return new IPhone();
    }
}

代码在我的笔记本电脑上通过了单元测试。