@ Lookup annotation of Spring dependency injection

@ Lookup annotation of Spring dependency injection

When understanding the Spring constructor inference, I saw the following source code:

problem analysis

Generally, we will use the @ Autowired annotation to realize the automatic injection of dependencies:

  1. Attribute injection

    @Component
    public class Xxx{
        @Autowired
        private Aaa aaa;
    }
    
  2. Constructor Injection

    @Component
    public class Xxx{
        private final Aaa aaa;
        
        @Autowired
        public Xxx(Aaa aaa){
            this.aaa = aaa;
        }
    }
    
  3. Common method injection

    @Component
    public class Xxx{
        private final Aaa aaa;
        
        @Autowired
        public void SetAaa(Aaa aaa){
            this.aaa = aaa;
        }
    }
    

The above dependency injection method can cope with most usage scenarios, except for a special usage scenario: a singleton Bean needs to rely on a prototype Bean

@Component
public class SingletonBean{
    @Autowired
    private PrototypeBean prototype;
    
    public void usePrototype(){
        // use prototype bean do something
    }
}

In the above code, SingletonBean is a singleton Bean, and its dependent PrototypeBean is a prototype Bean. Because the PrototypeBean has only one injection opportunity in the SingletonBean's life cycle, the subsequent usePrototype() method actually uses the same PrototypeBean instance, which is obviously problematic: Although we set the PrototypeBean as a prototype Bean, under the current situation, the PrototypeBean has become a de facto singleton Bean.

There are generally two solutions to the above problems:

  1. Introduce ApplicationContext dependency in Bean, and then call getBean() method to get prototype Bean.

    @Component
    public class SingletonBean{
        @Autowired
        private ApplicationContext applicationContext;
        
        public void usePrototype(){
            PrototypeBean prototype = applicationContext.getBean(PrototypeBean.class);
            // use prototype bean do something
        }
    }
    
  2. Use @ Lookup annotation

    @Component
    public class SingletonBean{
        
        public void usePrototype(){
            PrototypeBean prototype = getPrototypeBean();
            // use prototype bean do something
        }
        
        @Lookup
        public PrototypeBean getPrototypeBean(){
            return null;
        }
    }
    

    Methods using method injection need to meet the following syntax requirements:

    <public|protected> [abstract] <return-type> theMethodName(no-arguments);
    
    • Method access permission must be public or protected: This is related to the underlying principle of @ Lookup - dynamic proxy
    • Abstract (optional): if it is an abstract method, the proxy class will implement it; if it is not an abstract method, the proxy class will override it
    • Return type: the type of the prototype Bean
    • No arguments: parameter list must be empty

Code verification

Declare prototype Bean:

public class LookupConfig {
   @Bean
   @Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
   public PrototypeService prototypeService() {
      return new PrototypeServiceImpl();
   }
}

Singleton Bean dependent on prototype Bean:

public class SingletonServiceImpl implements SingletonService {
    
   private final PrototypeService prototypeBean;
   private final ApplicationContext applicationContext;

   @Autowired
   public SingletonServiceImpl(PrototypeService prototypeBean, ApplicationContext applicationContext) {
      this.prototypeBean = prototypeBean;
      this.applicationContext = applicationContext;
   }

   /**
    * Get prototype Bean
    *
    * @return Prototype Bean obtained through @ Autowired
    */
   @Override
   public PrototypeService getPrototypeBeanThroughAutowiredAnnotation() {
      return this.prototypeBean;
   }

   /**
    * Get prototype Bean
    *
    * @return Prototype Bean obtained through ApplicationContext
    */
   @Override
   public PrototypeService getPrototypeBeanThroughApplicationContext() {
      return this.applicationContext.getBean(PrototypeService.class);
   }

   /**
    * Get prototype Bean
    *
    * @return Prototype Bean obtained through @ Lookup
    */
   @Lookup
   @Override
   public PrototypeService getPrototypeBeanThroughLookupAnnotation() {
      return null;
   }
}

Test code:

@Slf4j
public class Main {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LookupConfig.class);
		applicationContext.register(SingletonServiceImpl.class);
		SingletonService singletonService = applicationContext.getBean(SingletonService.class);
		log.info("Single case Bean: {}", singletonService.toString());

		for (int i = 0; i < 3; i++) {
			log.info("adopt@Autowire Prototype acquired Bean: {}", singletonService.getPrototypeBeanThroughAutowiredAnnotation().toString());
		}

		for (int i = 0; i < 3; i++) {
			log.info("adopt ApplicationContext Prototype acquired Bean: {}", singletonService.getPrototypeBeanThroughApplicationContext().toString());
		}

		for (int i = 0; i < 3; i++) {
			log.info("adopt@Lookup Prototype acquired Bean: {}", singletonService.getPrototypeBeanThroughLookupAnnotation().toString());
		}
	}
}

Test code execution results:

[main] INFO com.xzy.test.lookup.Main - Single case Bean: com.xzy.service.impl.SingletonServiceImpl$$EnhancerBySpringCGLIB$$a5fddad5@6f96c77
[main] INFO com.xzy.test.lookup.Main - adopt@Autowire Prototype acquired Bean: com.xzy.service.impl.PrototypeServiceImpl@61230f6a
[main] INFO com.xzy.test.lookup.Main - adopt@Autowire Prototype acquired Bean: com.xzy.service.impl.PrototypeServiceImpl@61230f6a
[main] INFO com.xzy.test.lookup.Main - adopt@Autowire Prototype acquired Bean: com.xzy.service.impl.PrototypeServiceImpl@61230f6a
[main] INFO com.xzy.test.lookup.Main - adopt ApplicationContext Prototype acquired Bean: com.xzy.service.impl.PrototypeServiceImpl@3c130745
[main] INFO com.xzy.test.lookup.Main - adopt ApplicationContext Prototype acquired Bean: com.xzy.service.impl.PrototypeServiceImpl@cd3fee8
[main] INFO com.xzy.test.lookup.Main - adopt ApplicationContext Prototype acquired Bean: com.xzy.service.impl.PrototypeServiceImpl@3e2e18f2
[main] INFO com.xzy.test.lookup.Main - adopt@Lookup Prototype acquired Bean: com.xzy.service.impl.PrototypeServiceImpl@470f1802
[main] INFO com.xzy.test.lookup.Main - adopt@Lookup Prototype acquired Bean: com.xzy.service.impl.PrototypeServiceImpl@63021689
[main] INFO com.xzy.test.lookup.Main - adopt@Lookup Prototype acquired Bean: com.xzy.service.impl.PrototypeServiceImpl@703580bf

As can be seen from the execution results, Spring uses CGLIB to create a proxy class for SingletonServiceImpl

matters needing attention

  • Spring will create proxy objects through CGLIB, and then implement or override the methods marked with @ Lookup. Therefore, it doesn't matter what logic is inside the methods marked with @ Lookup. Just return null

  • Back to the beginning of this article, the code dealing with @ Lookup exists in the method of Spring inference constructor:

This means that if declared manually Bean,@Lookup Will not take effect:

```java
@Bean
@Scope(value = BeanDefinition.SCOPE_SINGLETON)
public SingletonService singletonService(PrototypeService prototypeService, ApplicationContext applicationContext) {
   return new SingletonServiceImpl(prototypeService, applicationContext);
}
![Please add a picture description](https://img-blog.csdnimg.cn/930857563378415f8b499ac1d495c64f.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASGVsbG9feHp5X1dvcmQ=,size_20,color_FFFFFF,t_70,g_se,x_16)


 (Because the manual declaration method has been told Spring Which constructor is used to instantiate Bean,therefore Spring There is no need for constructor inference, so processing@Lookup The code will not be executed, and all proxy classes will not be created, so what is actually called is SingleServiceImple The one in the list returns null The way, so NPE (yes)

 ---

Reference article:[spring in@Lookup Role of](https://www.cnblogs.com/wl20200316/p/12850300.html)

Keywords: Java Spring Back-end

Added by Kingkerry on Sat, 01 Jan 2022 11:26:25 +0200