简体   繁体   中英

Customize arguments in spring bean autowired constructor, before instantiation

What I need to achieve is the same behaviour as @RequestParam("foo") but in the constructor of a REQUEST scoped bean, where other constructor arguments would be autowired normally.

I would like to have a request parameter injected in the constructor as if it were a normal bean, but obviously without actually been a spring bean.

Example:

@Component
@RequestScope
public class RequestBean {    
    private final String filter;
    private final UserRepository userRepository;

    public RequestBean(@Param("filter") String filter, UserRepository userRepository) {
        this.filter = filter;
        this.userRepository = userRepository;
    }
    ...
}

I tried using a BeanFactoryPostProcessor but the value I need to inject have to be obtained at request time, not at bean definition time.

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        Stream.of(beanFactory.getBeanDefinitionNames())
                .map(name -> beanFactory.getBeanDefinition(name))
                .filter(def -> "request".equals(def.getScope()))
                .forEach(beanDefinition -> {
                    try {
                        System.out.println(beanDefinition);

                        for (Constructor<?> constructor : Class.forName(beanDefinition.getBeanClassName()).getConstructors()) {
                            for (Parameter parameter : constructor.getParameters()) {
                                Param param = parameter.getAnnotation(Param.class);
                                if(param != null) {
                                    String paramName = param.value(); // filter
                                    Object requestParameterValue = "request.getParameter('" + paramName + "')"; // bean creation time evaluated argument

                                    beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(
                                        new ConstructorArgumentValues.ValueHolder(requestParameterValue, parameter.getType().getName(), parameter.getName())
                                    );      
                                }
                            }
                        }
                    } catch (Exception ex) {
                        // whatever
                    }
                });
    }
}

For a request to /index?filter=blue I would expect "blue" and userRepository been autowired.

If I try with fixed values, it works perfectly, the value and the other bean are autowired normally.

I cannot use BeanPostProcessor because the values have to be acquired before instantiation (contructor injection)

Finally, I found out a way to achieve what I want.

I defined a REQUEST scoped to ease the process of getting and converting a request parameter. This bean will be called at creation time thanks to an expression (SprEL) registered in the BeanFactoryPostProcessor. The expression will be evaluated and the returned value injected in the annotated parameter.

This adds the dynamic expression to the constructor parameter:

String name = param.value();
TypedStringValue dynamicValue = new TypedStringValue("#{@myBean.getParameter('"+name+"')}");
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(index, dynamicValue, constructorParameter.getType().getName());

And this is the bean called by the expression before the annotated bean instantiation:

@Component
@RequestScope
class MyBean {
    final HttpServletRequest request;

    public MyBean(HttpServletRequest request) {
        this.request = request;
    }        

    public String getParameter(String name){
        // do something 
        return request.getParameter(name);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM