Overview

If the field name for a @Lazy @Resource does not match the bean name of the desired dependency, both CommonAnnotationBeanPostProcessor.buildLazyResourceProxy(...) and ContextAnnotationAutowireCandidateResolver.buildLazyResolutionProxy(...) will create proxies for the injected bean, with the latter creating a proxy that wraps the proxy created by the former.

For example, in the following test case, the fieldNameMatchesBeanName() test passes; whereas, the fieldNameDoesNotMatchBeanName() test fails with an error similar to the following.

Expected size: 2 but was: 3 in:
[example.LazyResourceTests$MyBean$$EnhancerBySpringCGLIB$$c2407600,
    example.LazyResourceTests$MyBean$$EnhancerBySpringCGLIB$$c2407600,
    example.LazyResourceTests.MyBean]
@SpringJUnitConfig
class LazyResourceTests {

    @Lazy
    @Resource
    MyBean myBean;

    @Lazy
    @Resource
    MyBean bean;

    @Test
    void fieldNameMatchesBeanName() throws Exception {
        assertPathToTarget(this.myBean);
    }

    @Test
    void fieldNameDoesNotMatchBeanName() throws Exception {
        assertPathToTarget(this.bean);
    }

    private static void assertPathToTarget(Object beanToInspect) throws Exception {
        List<Class<?>> classes = new ArrayList<>();
        Object current = beanToInspect;
        while (current != null) {
            classes.add(current.getClass());
            if (AopUtils.isAopProxy(current) && current instanceof Advised) {
                current = ((Advised) current).getTargetSource().getTarget();
            }
            else {
                break;
            }
        }
        assertThat(classes).hasSize(2);
    }

    @Configuration
    static class Config {
        @Bean
        MyBean myBean() {
            return new MyBean();
        }
    }

    static class MyBean {
    }

}

Related Issues

  • 28173

Comment From: sbrannen

Although the creation of two proxies for the same lazy injection point is unintentional, the team has decided to close this issue on the following grounds.

The only way for two such proxies to be created is if a @Lazy @Resource field has a name that does not match the name of a bean in the ApplicationContext, which should not be the case in practice.

When using @Resource, the developer is requesting that Spring perform dependency injection by name. When the field name does not match an existing bean name, Spring's dependency resolution mechanism falls back to dependency injection by type.

Thus, if a user wishes to use @Lazy with dependency injection by type, the injection point should be annotated with @Autowired or @Inject instead of @Resource. Otherwise, the user should ensure the @Resource field name matches an existing bean name or accept the fact that two lazy proxies are created for such scenarios.