A series of highlights of Spring official documents: lookup method

Keywords: Programming Spring

There are different scope s between beans

For example, when bean A is a singleton, if bean B is relied on, it is a prototype rather than a singleton. Since beanA has been assembled in the container, when beanA's method a is called to get the bean B object, it is the same object. Does not meet the prototype requirements of bean B.

One of the implementation methods is implemented in methodA through beanFactory.getBean, as follows:

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

But this approach needs to couple the spring api and rely on the spring framework. In fact, spring provides a flexible way to deal with this scenario.

Lookup method injection

spring creates a subclass dynamically for beanA through cglib bytecode, and returns the bean requirements of different scope s through the subclass's replication methodA method. Limitation of lookup method:

1. Class and method cannot be final

2. Concrete methods are also necessary for component scanning, which requires concrete classes to pick up

3,A further key limitation is that lookup methods do not work with factory methods and in particular not with @Bean methods in configuration classes

This means that the method cannot be used with the factory method function or with the @ bean annotation. In this case, the creation of beans is not the responsibility of the container, so there is no way to create dynamic subclasses.

Compared with the command manager example mentioned above, the spring container can dynamically implement the createCommand method, while the CommandManager does not need to rely on the spring framework. As follows:

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

A target class needs method injection (for example, CommandManager in the current example) to meet the following format requirements:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

Abstract can be ignored. If it is an abstract method, the dynamically generated subclass will implement this method, otherwise the subclass will copy this method. ---Is this a bit of rubbish?

The configuration of CommandManager is as follows:

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>

The configuration by annotation is as follows:

 @Lookup("myCommand")
 protected abstract Command createCommand();

At the same time, spring can push the bean id by returning the type of the object without specifying it explicitly, such as:

 @Lookup
 protected abstract MyCommand createCommand();

say something

  1. When I checked the source code, I noticed the lookup method. At that time, I initially understood that it was an override form, but I didn't expect the actual scenario it solved. For example, in the determineCandidateConstructors method of Autowired annotation beanpostprocessor, it will be judged that the method is annotated with a lookup annotation, and a LookupMethpd will be created and added to BeanDefinition. As follows:
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
    @Override
    public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
        Lookup lookup = method.getAnnotation(Lookup.class);
        if (lookup != null) {
            LookupOverride override = new LookupOverride(method, lookup.value());
            try {
                RootBeanDefinition mbd = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName);
                mbd.getMethodOverrides().addOverride(override);
            }
            catch (NoSuchBeanDefinitionException ex) {
                throw new BeanCreationException(beanName,
                        "Cannot apply @Lookup to beans without corresponding bean definition");
            }
        }
    }
});

Here, mdb.getMethodOverrides should maintain a list of all lookup method s.

Practice scenario

Related knowledge

1. Other solutions

  1. ObjectFactory
  2. Provider
  3. ServiceLocatorFactoryBean

Posted by mpharo on Sat, 01 Feb 2020 22:35:50 -0800