xing
xing

Reputation: 484

how to autowire spring beans from a non-bean object created at runtime?

I have some Jpa repositories and several Entity class. I need to create a helper object for one of my Entity. Inside that helper I use @Autowire to access the Jpa repositories.

@Entity
class A {
    @Transient
    Helper helper;

    ...

}

class Helper {
    A a;
    @Autowired
    CRepository repo;

    public Helper(A a) {
        this.a = a;
    }
}

However, the repo is always null. I've tried using SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this) and @Configurable, but both of them failed. Can anybody provide some hint for me?

BTW, A is instantiated inside a rest controller.

Thanks!.

Upvotes: 1

Views: 8420

Answers (4)

Kevin
Kevin

Reputation: 241

You can use a BeanUtil class to get any bean that created in Springl

@Service
public class BeanUtil implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static <T> T getBean(Class<T> beanClass) {
        return context.getBean(beanClass);
    }
}

Then you can get the bean.

MyBean obj = BeanUtil.getBean(MyBean.class);

Upvotes: 9

ibai
ibai

Reputation: 1308

@Configurable annotation works fine, but you need to use @EnableSpringConfigured annotation in any configuration class in order to make it work. Read my answer in other stackoverflow post: spring autowiring not working from a non-spring managed class

Entity class should not contain any helpers, even if transient. For a clean design you need to separate concerns, so the entity should not be aware of your business logic. I cannot help you more since I don't know which is the goal of that helper, but here you have other alternatives:

ALTERNATIVE 1 (based on your description seems that helper is an stateful bean, so it is not candidate to be a @Service, which I personally think it should be)

@Controller
public MyController {
    @RequestMapping(...)
    public void processRequest() {
        A a = new A();
        ...
        Helper helper = new Helper(a);  // CRepository is successfully autowired
    }
}

@Configurable(autowire = Autowire.BY_TYPE)
public class Helper {
    A a;
    @Autowired
    CRepository repo;
}

@Configuration
@EnableSpringConfigured
public Application {
    ...
}

ALTERNATIVE 2 (make your Helper class stateless so that spring is aware of your beans without the need of extra stuff like @Confgurable/@EnableSpringConfigured)

@Controller
public MyController {
     @Autowired Helper helper;  // CRepository is correctly autowired

    @RequestMapping(...)
    public void processRequest() {
        A a = new A();
        ...
        helper.doSomething(a);
    }
}

@Service
public class Helper {
    // A a;  remove dependency to A to make it stateless
    @Autowired
    CRepository repo;

    public Helper() {
    }

    public void doSomething(A a) {
        ...
        repo.save(a);
    }
}

Upvotes: 0

Use constructor injection instead of field injection; this is a best practice all the time anyway. Then it's trivial to inject your A into the controller and pass it as a constructor argument.

Upvotes: 0

Francesco Pitzalis
Francesco Pitzalis

Reputation: 2102

You cannot autowire nothing in your Helper class because it isn't managed by Spring. You can use this approach:

public class HelperManager {
  @Autowired
  private ApplicationContext context;

  public Helper getHelper(A a) {
    return context.getBean(Helper.class, a);
}

Configure Helper to be a prototype bean:

@Configuration
public class MyConfiguration {
  @Bean
  public HelperManager helperManager() {
    return new HelperManager();
  }

  @Bean
  @Scope("prototype")
  public Helper helper(A a) {
    return new Helper(a);
  }
}

And finally in your controller:

@Controller
public class MyController {
  @Autowired
  private HelperManager helperManager;

  public someMethodWhereToInstanceYourHelper(A a) {
    ...
    Helper helper = helperManager.getHelper(a);
    ...
  }
}

Upvotes: -1

Related Questions