让我把问题简化如下:
我有一个名为project-parent的Java Maven项目,其中有多个子模块项目。
其中一个项目叫做project-common,我把整个项目中使用的所有自定义的Spring AOP方面都放在里面,我用project-common编写了单元测试,方面在单元测试中工作正常。
然后,我想将这些方面应用到其他模块中,其中一个子模块叫做project-service,我应用到服务中方法的方面应该在服务方法之前和之后进行auth管理,但是我发现这些方面在服务运行时不工作,而且project-service对project-common有一个maven依赖。
项目结构如下
project-parent
-- project-common (in which define the aspect)
-- project-service (where my aspect is used)
...
-- other submodules omitted for simplicity
我的方面定义如下:
@Aspect
@Component
public class RequestServiceSupportAspect {
@Pointcut(value = "@annotation(RequestServiceSupport)")
public void matchMethod() {
// pointcut
}
@Around("matchMethod()")
public Object basicAuthSupport(ProceedingJoinPoint joinPoint) {
...
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestServiceSupport {
}
我使用方面的服务如下:
public class RequestServiceImpl implements RequestService {
...
@RequestServiceSupport
public Request addComment(Comment comment) {
...
}
}
3条答案
按热度按时间kuuvgm7e1#
我最终解决了这个问题,并有机会了解Spring AOP在后台是如何工作的,以及如果它不工作,我们如何调试这种AOP方面的问题。
根本原因
根本原因是该方面不是由Spring在project-service中管理的Bean,只需在project-service的Config类中添加以下内容即可解决该问题:
RequestServiceSupportAspect在用project-common编写的单元测试中工作的原因是,我们在方面定义上使用了@Component,并且在project-common中,有一个由Spring管理的RequestServiceSupportAspect bean,因此方面可以工作。
但是在另一个子模块项目服务中,标注了@Component的Aspect类默认不会创建Spring管理的Bean。因为它不在SpringBoot应用程序扫描的路径中。您需要在Config类中手动声明Bean定义,或者需要在project-common中定义Aspect Bean并导入Config文件,或者让project-commmon通过配置resources/META-INF/spring来公开它。工厂如下:
AOP如何在后面工作
上面的解释应该已经解决了这个问题。但是如果你对我如何到达那里感兴趣,下面可能会提供一些提示。
1.我首先开始检查我的服务bean是否被代理了,答案是否定的,我只看到了一个原始bean,所以我开始思考方面在运行时是如何工作的,并代理对真实的服务的直接调用,以便方面可以在服务上添加它的动作。
1.经过一番挖掘,我发现BeanPostProcessor是一个需要深入研究的关键入口点。首先,我们可以深入下面的注解链:
如果你看到AnnotationAwareAspectJAutoProxyCreator的层次结构,它实现了BeanPostProcessor接口,这是合理的,因为这样Spring就可以将代理添加到绑定了方面的类中。它有两个方法要实现:
1.我开始阅读AnnotationAwareAspectJAutoProxyCreator是如何实现该方法的,我发现是它的基类AbstractAutoProxyCreator实现了该方法,如下所示:
很明显wrapIfNecessary是方面代理被添加到Bean的地方,我在这里做了一个断点并检查哪里出错了。
1.在wrapIfNecessary方法中,我发现当我的服务bean被创建时,它进入DO_NOT_PROXY的分支。
原因是getAdvicesAndAdvisorsForBean没有返回我想要的方面。
我深入研究getAdvicesAndAdvisorsForBean,发现BeanFactoryAspectJAdvisorsBuilder::buildAspectJAdvisors是导入所有候选Bean的地方。
它使用单例模式中常见的代码对aspectNames进行了一次初始化,这些代码稍后将在BeanNameAutoProxyCreator::getAdvicesAndAdvisorsForBean中使用,以获取您创建的方面。
然后我发现是这个项目中没有包含的Aspect Bean导致我的Aspect无法工作。
1.如果您查看wrapIfNecessary方法,您还会发现SpringAOP将为其bean类创建的不同代理
如果AOP方面不工作,我们如何调试问题
如果您发现方面不工作,请在以下位置设置断点:
为您想要添加方面的服务bean添加条件断点过滤器,逐步执行将引导您找到根本原因。
摘要
虽然调查过程需要一些时间,但最后的根本原因是相当简单和直接的。但在我们的日常工作中,有些人很容易忽略它。这就是为什么我把我的答案张贴在这里,以便将来有人遇到类似的问题,张贴可以保存一些时间。
h9a6wy2h2#
您可以只添加(或扩展)
@ComponentScan
来使方面组件可访问,而不是显式地提供@Bean
方面。83qze16e3#
我遇到了这个bug,用这个方法解决了它。项目结构如下
Aop.class
Config.class
Application.class