Description
Description
When using Spring AOP to proxy a HelloService bean in combination with a custom BeanPostProcessor (MyPostProcessor) and a FactoryBean (MyFactoryBean), the AOP proxy is not applied to the target bean. The HelloService bean is returned as a plain Java object without any proxy applied. This issue occurs when the BeanPostProcessor has a dependency on another bean (GoodByeService in this case).
Expected Behavior
The HelloService bean should be proxied by AOP, and the following checks should return true:
AopUtils.isAopProxy(helloService)
AopUtils.isJdkDynamicProxy(helloService) or AopUtils.isCglibProxy(helloService)
The @AfterReturning advice in MyAspect should also be invoked when calling the sayHello method of HelloService.
Actual Behavior
The HelloService bean is not proxied. The following checks return false:
AopUtils.isAopProxy(helloService)
AopUtils.isJdkDynamicProxy(helloService)
AopUtils.isCglibProxy(helloService)
Additionally, the @AfterReturning advice in MyAspect is not invoked.
Steps to Reproduce
Here is a minimal reproducible example:
- POM File
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-aop</artifactId>
<parent>
<groupId>com.cj.lb</groupId>
<artifactId>spring-exploration</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<properties>
<spring-versrion>6.0.11</spring-versrion>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-versrion}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-versrion}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
</project>
- Configuration Class
@EnableAspectJAutoProxy
@Configuration
@ComponentScan
public class MyConfig {
@Bean
MyPostProcessor myPostProcessor(GoodByeService goodByeService) {
MyPostProcessor myPostProcessor = new MyPostProcessor();
myPostProcessor.setGoodByeService(goodByeService);
return myPostProcessor;
}
@Bean
MyFactoryBean myFactoryBean(HelloService helloService) {
MyFactoryBean myFactoryBean = new MyFactoryBean();
myFactoryBean.setHelloService(helloService);
return myFactoryBean;
}
}
- BeanPostProcessor
public class MyPostProcessor implements Ordered, BeanPostProcessor {
private GoodByeService goodByeService;
public GoodByeService getGoodByeService() {
return goodByeService;
}
public void setGoodByeService(GoodByeService goodByeService) {
this.goodByeService = goodByeService;
}
@Override
public int getOrder() {
return 0;
}
}
- Service Classes
@Service
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello() {
String result = "hello world";
System.out.println(result);
return result;
}
}
@Service
public class GoodByeServiceImpl implements GoodByeService {
@Override
public String sayGoodbye() {
return "Goodbye";
}
}
- FactoryBean
public class MyFactoryBean implements FactoryBean {
private HelloService helloService;
public HelloService getHelloService() {
return helloService;
}
public void setHelloService(HelloService helloService) {
this.helloService = helloService;
}
@Override
public Object getObject() throws Exception {
return new MyBean();
}
@Override
public Class<?> getObjectType() {
return MyBean.class;
}
public static class MyBean {
}
}
- Aspect
@Component
@Aspect
public class MyAspect {
@Pointcut("execution(* com.cj.lb.service.HelloService.sayHello(..))")
public void pointcut() {}
@AfterReturning(pointcut = "pointcut()", returning = "result")
public void afterReturning(JoinPoint jp, Object result) {
System.out.println("my aspect aop ...");
}
}
- Main Class
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
HelloService helloService = context.getBean(HelloService.class);
System.out.println("helloService.getClass() = " + helloService.getClass());
System.out.println("Is AOP Proxy: " + AopUtils.isAopProxy(helloService));
System.out.println("Is JDK Dynamic Proxy: " + AopUtils.isJdkDynamicProxy(helloService));
System.out.println("Is CGLIB Proxy: " + AopUtils.isCglibProxy(helloService));
context.close();
}
}
- Output
helloService.getClass() = class com.cj.lb.service.impl.HelloServiceImpl
Is AOP Proxy: false
Is JDK Dynamic Proxy: false
Is CGLIB Proxy: false
Analysis and Workaround
The issue can be resolved by either of the following:
- Annotating the HelloService dependency in MyFactoryBean with @lazy.
- Adding a proper generic type declaration for MyFactoryBean.
Question
Why does the presence of a BeanPostProcessor with dependencies interfere with the AOP proxy creation for HelloService? Is this a bug in Spring, or is there a specific configuration requirement that I missed?
Additional Context
This issue seems related to the bean initialization order or proxy creation timing, especially when BeanPostProcessor and FactoryBean dependencies are involved. Further clarification would be appreciated.