spring not eligible for auto-proxying

240 阅读1分钟

问题

日志中出现如下warning:

demo.test.bpp.DataCache] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

image.png

问题复现

@Retention(RetentionPolicy.RUNTIME)
public @interface RandomInt {
    int min();

    int max();
}
@Component
@Data
// @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class DataCache {

    public DataCache() {
        System.out.println("new DataCache");
    }

    @RandomInt(min = 2, max = 10)
    private int group;
    private String name;
}
public class RandomIntProcessor implements BeanPostProcessor {

    public RandomIntProcessor() {
        System.out.println("new RandomIntProcessor");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean name in RandomIntProcessor: " + beanName);
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            RandomInt injectRandomInt = field.getAnnotation(RandomInt.class);
            if (injectRandomInt != null) {
                int min = injectRandomInt.min();
                int max = injectRandomInt.max();
                int randomValue = RandomUtils.nextInt(min, max);
                field.setAccessible(true);
                ReflectionUtils.setField(field, bean, randomValue);
            }
        }
        return bean;
    }
}
@Component
public class BizProcessor implements BeanPostProcessor {

    @Autowired
    // @Lazy
    private DataCache dataCache;

    public BizProcessor() {
        System.out.println("new BizProcessor");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean name in BizProcessor: " + beanName);
        
        // for debug
        try {
            throw new RuntimeException("see see");
        } catch (Exception e) {
            e.printStackTrace();
        }

        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    public int getData() {
        return dataCache.getGroup();
    }
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RandomIntProcessor.class, DataCache.class, BizProcessor.class})
public class NotEligibleForAutoProxyingIntegrationTest {

    @Autowired
    private BizProcessor bizProcessor;
    @Autowired
    private DataCache dataCache;

    @Test
    public void givenAutowireInBeanPostProcessor_whenSpringContextInitialize_thenNotEligibleLogShouldShow() {
        System.out.println("result: " + bizProcessor.getData());
        System.out.println("result: " + dataCache.getGroup());
    }
}
  • 在BizProcessor中,dataCache的@Lazy注解被注释的话,输出如下:
result: 0
result: 0
  • 在BizProcessor中,增加dataCache的@Lazy注解
result: 6
result: 6

原理解释

image.png

BeanPostProcessor会被spring容器特殊处理、不同对待。所有的BeanPostProcessor实例和他们的直接依赖会在启动的时候被直接实例化,这作为ApplicationContext特殊的启动阶段。下一步,所有的BeanPostProcessor会被按照顺序被注册进容器,并被应用到后面所有的bean。因为AOP自动代理也是一种BeanPostProcessor,所以所有的BeanPostProcessor实例以及他们的直接依赖都不会被自动代理,也就不会被应用切面织入(AOP这一句不太懂)。

image.png

为了解决BeanPostProcessor和他的依赖之间的死循环,可以在BeanPostProcessor依赖的Bean上增加@Lazy注解。这样当该依赖Bean被使用时,会被初始化。这样就可以被BeanPostProcessor处理了。

参考