admin管理员组

文章数量:1416489

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)

Additionally, 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=".0.0"
         xmlns:xsi=";
         xsi:schemaLocation=".0.0 .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>.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-versrion}</version>
        </dependency>
        <dependency>
            <groupId>.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring-versrion}</version>
        </dependency>
        <dependency>
            <groupId>.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:

  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.

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)

Additionally, 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./POM/4.0.0"
         xmlns:xsi="http://www.w3./2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache./POM/4.0.0 http://maven.apache./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>.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-versrion}</version>
        </dependency>
        <dependency>
            <groupId>.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring-versrion}</version>
        </dependency>
        <dependency>
            <groupId>.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:

  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.

Share Improve this question asked Mar 13 at 8:08 java-lbbjava-lbb 211 silver badge3 bronze badges 11
  • BeanPostProcessor s should be registered with a static method not an instance method. – M. Deinum Commented Mar 13 at 10:21
  • @M.Deinum, that does not seem to be the problem here. – kriegaex Commented Mar 14 at 2:35
  • Yes, MyPostProcessor myPostProcessor(@Lazy GoodByeService goodByeService) and MyFactoryBean myFactoryBean(@Lazy HelloService helloService) both work. BTW, GoodByeService will only be a proxy if e.g. targeted by an aspect, which in your example is not the case. There is a Baeldung article explaining and recommending this approach. I am not competent in bean post processors, but I find the explanation comprehensible enough to recommend it. If the link helps you, just let me know and I convert the comment into an answer. – kriegaex Commented Mar 14 at 2:37
  • As for the question of whether @Bean methods in @Configuration classes ought to be static or not, the Spring manual (search for the info note starting with "You may declare @Bean methods as static") provides some guidelines for it. – kriegaex Commented Mar 14 at 3:32
  • Yes, I have read that article, and I also see the same log message during project startup: "not eligible for auto-proxying." However, please note the example I provided. In my case, myBeanPostProcessor depends on GoodByeService, which is not enhanced by myAspect (i.e., it is not proxied by AOP). The actual proxy is created for HelloService, and the one depending on HelloService is MyFactoryBean, which is not a BeanPostProcessor. This is what confuses me, and I hope you can provide an explanation. – java-lbb Commented Mar 14 at 11:11
 |  Show 6 more comments

1 Answer 1

Reset to default 2

I added src/main/resources/logback.xml to your project as follows to trace bean creation and AOP:

<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <layout class="ch.qos.logback.classic.PatternLayout">
      <Pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</Pattern>
    </layout>
  </appender>

  <logger name=".springframework.beans" level="DEBUG"/>
  <logger name=".springframework.context" level="DEBUG"/>
  <logger name=".springframework.aop" level="DEBUG"/>

  <root level="INFO">
    <appender-ref ref="STDOUT"/>
  </root>
</configuration>

Then, I modified your aspect, adding another pointcut + advice to intercept GoodbyeService, because I wanted to see how it behaves when a proxy ought to be created for it. I also added some more logging to the main program. When running the program without @Lazy, it prints (timestamps removed to be able to diff it):

DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean '.springframework.context.annotation.internalConfigurationAnnotationProcessor'
DEBUG o.s.c.a.ClassPathBeanDefinitionScanner - Identified candidate component class: file [C:\Users\alexa\Documents\java-src\SO_AJ_SpringBeanPostProcessor_79505769\target\classes\com\cj\lb\MyAspect.class]
DEBUG o.s.c.a.ClassPathBeanDefinitionScanner - Identified candidate component class: file [C:\Users\alexa\Documents\java-src\SO_AJ_SpringBeanPostProcessor_79505769\target\classes\com\cj\lb\service\GoodByeServiceImpl.class]
DEBUG o.s.c.a.ClassPathBeanDefinitionScanner - Identified candidate component class: file [C:\Users\alexa\Documents\java-src\SO_AJ_SpringBeanPostProcessor_79505769\target\classes\com\cj\lb\service\HelloServiceImpl.class]
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean '.springframework.context.event.internalEventListenerProcessor'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean '.springframework.context.event.internalEventListenerFactory'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean '.springframework.context.annotation.internalAutowiredAnnotationProcessor'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myPostProcessor'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'helloServiceImpl'
HelloServiceImpl constructor
WARN  o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'helloServiceImpl' of type [com.cj.lb.service.HelloServiceImpl] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [myPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Autowiring by type from bean name 'myFactoryBean' via factory method to bean named 'helloServiceImpl'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'goodByeServiceImpl'
GoodByeServiceImpl constructor
WARN  o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'goodByeServiceImpl' of type [com.cj.lb.service.GoodByeServiceImpl] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [myPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Autowiring by type from bean name 'myPostProcessor' via factory method to bean named 'goodByeServiceImpl'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean '.springframework.aop.config.internalAutoProxyCreator'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myConfig'
DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.cj.lb.MyAspect.goodbyeAdvice(.aspectj.lang.JoinPoint,java.lang.Object)
DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.cj.lb.MyAspect.helloAdvice(.aspectj.lang.JoinPoint,java.lang.Object)
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myAspect'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myFactoryBean'

HelloService class:   class com.cj.lb.service.HelloServiceImpl
Is AOP proxy:         false
Is JDK proxy:         false
Is CGLIB proxy:       false
Hello, world!

GoodByeService class: class com.cj.lb.service.GoodByeServiceImpl
Is AOP proxy:         false
Is JDK proxy:         false
Is CGLIB proxy:       false
Goodbye, world!

Now, adding @Lazy to both bean method parameters in the config class:

DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean '.springframework.context.annotation.internalConfigurationAnnotationProcessor'
DEBUG o.s.c.a.ClassPathBeanDefinitionScanner - Identified candidate component class: file [C:\Users\alexa\Documents\java-src\SO_AJ_SpringBeanPostProcessor_79505769\target\classes\com\cj\lb\MyAspect.class]
DEBUG o.s.c.a.ClassPathBeanDefinitionScanner - Identified candidate component class: file [C:\Users\alexa\Documents\java-src\SO_AJ_SpringBeanPostProcessor_79505769\target\classes\com\cj\lb\service\GoodByeServiceImpl.class]
DEBUG o.s.c.a.ClassPathBeanDefinitionScanner - Identified candidate component class: file [C:\Users\alexa\Documents\java-src\SO_AJ_SpringBeanPostProcessor_79505769\target\classes\com\cj\lb\service\HelloServiceImpl.class]
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean '.springframework.context.event.internalEventListenerProcessor'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean '.springframework.context.event.internalEventListenerFactory'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean '.springframework.context.annotation.internalAutowiredAnnotationProcessor'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myPostProcessor'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean '.springframework.aop.config.internalAutoProxyCreator'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myConfig'
DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.cj.lb.MyAspect.goodbyeAdvice(.aspectj.lang.JoinPoint,java.lang.Object)
DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public void com.cj.lb.MyAspect.helloAdvice(.aspectj.lang.JoinPoint,java.lang.Object)
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myAspect'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'goodByeServiceImpl'
GoodByeServiceImpl constructor
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'helloServiceImpl'
HelloServiceImpl constructor
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myFactoryBean'

HelloService class:   class jdk.proxy2.$Proxy23
Is AOP proxy:         true
Is JDK proxy:         true
Is CGLIB proxy:       false
Hello, world!
execution(String com.cj.lb.service.HelloService.sayHello())

GoodByeService class: class jdk.proxy2.$Proxy15
Is AOP proxy:         true
Is JDK proxy:         true
Is CGLIB proxy:       false
Goodbye, world!
execution(String com.cj.lb.service.GoodByeService.sayGoodbye())

Please note the main difference in the relative order of these (irrelevant parts of the log removed):

Creating shared instance of singleton bean 'myPostProcessor'
Creating shared instance of singleton bean 'helloServiceImpl'
Creating shared instance of singleton bean 'goodByeServiceImpl'
Creating shared instance of singleton bean 'myAspect'
Creating shared instance of singleton bean 'myFactoryBean'

The aspect is created later than the two beans it is supposed to create proxies for, i.e. they already have been injected into the bean factory and the bean post processor, respectively.

With @Lazy, it looks better, which explains why it works now:

Creating shared instance of singleton bean 'myPostProcessor'
Creating shared instance of singleton bean 'myAspect'
Creating shared instance of singleton bean 'goodByeServiceImpl'
Creating shared instance of singleton bean 'helloServiceImpl'
Creating shared instance of singleton bean 'myFactoryBean'

本文标签: javaSpring AOP Proxy Not Working with BeanPostProcessor and FactoryBean DependenciesStack Overflow