16

I would like to create a Spring's bean producer method which is aware who invoked it, so I've started with the following code:

@Configuration
public class LoggerProvider {

    @Bean
    @Scope("prototype")
    public Logger produceLogger() {
        // get known WHAT bean/component invoked this producer 
        Class<?> clazz = ...

        return LoggerFactory.getLogger(clazz);
    }
}

How can I get the information who wants to get the bean injected?

I'm looking for some equivalent of CDI's InjectionPoint in Spring world.

Piotr Nowicki
  • 17,914
  • 8
  • 63
  • 82

2 Answers2

26

Spring 4.3.0 enables InjectionPoint and DependencyDescriptor parameters for bean producing methods:

@Configuration
public class LoggerProvider {

    @Bean
    @Scope("prototype")
    public Logger produceLogger(InjectionPoint injectionPoint) {
        Class<?> clazz = injectionPoint.getMember().getDeclaringClass();

        return LoggerFactory.getLogger(clazz);
    }
}

By the way, the issue for this feature SPR-14033 links to a comment on a blog post which links to this question.

Arend v. Reinersdorff
  • 4,110
  • 2
  • 36
  • 40
  • If I inject this into another bean, which is also scoped to prototype, then won't this create a logger for every instance of that bean, rather than sharing a single static instance? – Rupert Madden-Abbott Jan 24 '17 at 10:50
  • 1
    @RupertMadden-Abbott The logging framework normally caches loggers. For example see this question about the SLF4J logging framework: [What's the overhead of creating a SLF4J loggers in static vs. non-static contexts?](http://stackoverflow.com/questions/10345109/whats-the-overhead-of-creating-a-slf4j-loggers-in-static-vs-non-static-context) – Arend v. Reinersdorff Jan 24 '17 at 13:31
8

As far as I know, Spring does not have such a concept.

Then only thing that is aware of the point that is processed is a BeanPostProcessor.


Example:

@Target(PARAMETER)
@Retention(RUNTIME)
@Documented
public @interface Logger {}

public class LoggerInjectBeanPostProcessor implements BeanPostProcessor {   
    public Logger produceLogger() {
        // get known WHAT bean/component invoked this producer
        Class<?> clazz = ...    
        return LoggerFactory.getLogger(clazz);
    }


    @Override
    public Object postProcessBeforeInitialization(final Object bean,
            final String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean,
            final String beanName) throws BeansException {

        ReflectionUtils.doWithFields(bean.getClass(),
                new FieldCallback() {
                     @Override
                     public void doWith(final Field field) throws IllegalArgumentException, IllegalAccessException {
                         field.set(bean, produceLogger());
                     }
                },
                new ReflectionUtils.FieldFilter() {
                     @Override
                     public boolean matches(final Field field) {
                          return field.getAnnotation(Logger.class) != null;
                     }
                });

        return bean;
    }
}
Ralph
  • 118,862
  • 56
  • 287
  • 383
  • Thanks Ralph! Ok, so this `BeanPostProcessor` is invoked whenever any bean is injected anywhere. Is there any way I can recognize **what** bean is to be injected? I mean that I got: `postProcessBeforeInitialization(Object bean, String beanName)` where `bean` is a bean that wants the injection to be made (the **who** part I've asked in my question), and `beanName` is the `bean` name. Now I don't know what field in the `bean` wants the injection to be made - in other words I don't know if the injected bean is `LoggerProvider` or something else. – Piotr Nowicki Mar 14 '12 at 13:04
  • As fare as I understand, the post processor is invoked afer a bean is created! (not injected anywhere). The Idea was to implement your own small injection framework on top of that bean Post Processor. – Ralph Mar 14 '12 at 13:34
  • Ok, so the `BeanPostProcessor` implementor will be called whenever a bean is created. So, your idea was to implement `postProcessBeforeInitialization` and e.g. scan for fields that have my custom annotation (let's say `@Logger`) and, through reflection, initialize such field using my own custom logic. Was this your approach or did I complicate it too much? Thanks once again! – Piotr Nowicki Mar 14 '12 at 13:47
  • 1
    @Piotr Nowicki: Yes, but is is really easy, and not so much work. See the example I have added. (I have not tested that code, but it should give you a good idea, how easy it is to extend Spring, and how powerfull the spring utils are.) – Ralph Mar 14 '12 at 19:03