spring-projects / spring-projects/spring-framework

Spring AOP Proxy Not Working with BeanPostProcessor and FactoryBean Dependencies

Open
#34,563 8 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

in: core status: waiting-for-triage
Dominant language
Java
Stars
60.2k
Forks
38.8k
Avg merge
5d 2h
Merged PRs (30d)
27

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:

  1. 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>
  1. 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;
    }
}
  1. 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;
    }
}
  1. 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";
    }
}
  1. 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 {
    }
}
  1. 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 ...");
    }
}
  1. 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();
    }
}
  1. 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:

  1. Annotating the HelloService dependency in MyFactoryBean with @Lazy.
  2. 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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the supplied minimal reproducer from MyConfig with AnnotationConfigApplicationContext, then trace how MyPostProcessor's GoodByeService dependency and MyFactoryBean's HelloService dependency affect bean initialization and AOP proxy creation. Compare the behavior with @Lazy and with the generic FactoryBean declaration; done means explaining the cause and whether the configuration is required or Spring should change, using the AopUtils checks and advice invocation as evidence.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.