Spring @Autowired on a class new instance

40,033

Solution 1

Spring itself offers some functionality for doing auto-wiring in your objects which you created by new or newInstance() or whatever.

To use it you need an AutowireCapableBeanFactory which you get by Spring's normal dependency injection with @Autowired.

@Autowired
private  AutowireCapableBeanFactory autowireCapableBeanFactory;

Then you use its autowireBean(Object) method to inject the @Autowired properties into your bean.

Object myBean = map.get(className).newInstance();
autowireCapableBeanFactory.autowireBean(myBean);

Design note:

Think well if you really need the approach above. The javadoc of AutowireCapableBeanFactory advises against using this interface for most use-cases:

This subinterface of BeanFactory is not meant to be used in normal application code: stick to BeanFactory or ListableBeanFactory for typical use cases.

Integration code for other frameworks can leverage this interface to wire and populate existing bean instances that Spring does not control the lifecycle of. This is particularly useful for WebWork Actions and Tapestry Page objects, for example.

Solution 2

You can use Factory Design Pattern over here.

This might seem a little complicated in start but I am sure you will love it after you have implemented it.

Steps:

  1. Add @Component on all implementations of AbstractClass.
  2. Create a factory class as:

    @Component
    public class MyFactory {
    
        private final Map<String, AbstractClass> impletationMap = new HashMap<>();
    
        @Autowired
        ApplicationContext context;
    
        @PostConstruct
        public void initialize() {
            populateDataMapperMap(context.getBeansOfType(AbstractClass.class).values().iterator());
        }
    
        private void populateDataMapperMap(final Iterator<AbstractClass> classIterator) {
            while (classIterator.hasNext()) {
                AbstractClass abstractClassImpl = (AbstractClass) classIterator.next();
                impletationMap.put(abstractClassImpl.getClass().getName(), abstractClassImpl);
    
            }
        }
    }
    

When the Bean of this MyFactory class is initialized, then it will lookup for all beans of type AbstractClass and put them in the HashMap(implementationMap).

Now from this factory you can get the HashMap and then get the implementations as and when you require. It will be very easy when you add new implementation of AbstractClass as factory will take care of it.

Solution 3

One work around is instead of binding the MyClass to the Hashmap to bind a Factory class. MyClassFactory. This way you will delegate the construction to a concrete factory that will do the job to instantiate the correct class and initialize the correct repository.

Here is an example:

{"MyClass", MyClassFactory.class}

The factory can be Component as well, then you need to bind the hashmap to the factory instance instead of the factory class. But lets say it is not a component:

//@Component   this is optional
    public MyClassFactory {
        //@Autowired optional
        ApplicationContext ctx;


       public MyClass createInstance() {
            MyRepository repo = ctx.getBean("")
            MyClass myclass = new MyClass(repo)
            return myclass;
       }
    }

If you mark it as component you can well also use ApplicationContextAware interface if you are going to autowire the ApplicationContext.

Solution 4

Try this

@Component    
public class SomeClass extends AbstractClass {

  private static ApplicationContext applicationContext;

  public MyClass getMyClass(){
      // Now @Autowired MyRepository will work
      return applicationContext.getBean(MyClass.class);
  }

}

Solution 5

One approach is to declare @Component on top of MyClass.

Then, in the setup phase, you can pass the instance of MyClass instead of MyClass.class itself, in the HashMap. There won't be any need to create instances via reflection.

Note: You can fetch the instance of MyClass from your ApplicationContext in the setup phase.

Share:
40,033
João Menighin
Author by

João Menighin

Updated on February 02, 2020

Comments

  • João Menighin
    João Menighin about 4 years

    I'm not so familiar with Spring and I have the following situation:

    A repository class:

    @Repository
    public class MyRepository {
        // ...
    }
    

    A class that uses the repository class:

    public class MyClass extends AbstractClass {
    
        @Autowired
        private MyRepository myRepository;
    
        //...
    }
    

    I know that if I annotate my MyClass with @Component and use it with an @Autowired, then the @Autowired MyRepository is resolved just fine. Problem is I am in a situation that I need to create new instances of MyClass with reflection. So MyRepository is never resolved and is null all the time.

    Is there a way to use @Autowired in this situation?

    Explaining better my situation: I have some implementations of AbstractClass. In a setup phase of my application I create a HashMap of these implementations. Basically:

    {"MyClass", MyClass.class}
    //...
    

    Then I have a generic Controller that maps to the url /{class}?options=... Using the {class} @PathVariable, the HashMap above and reflection I am able to create a instance of a class based on the given options (this part is important). Do you guys think there's a better way of doing this?

    Thanks in advance

  • João Menighin
    João Menighin over 5 years
    Thanks for the answer Pankaj, the problem is that I can't have singletons of this guy. Each instance may depend on the options given in the URL... Any ideas? hmm
  • Pankaj Singhal
    Pankaj Singhal over 5 years
    you can then pass an instance of a factory class, that can fetch you beans at runtime based on the options that you can pass give via it's constructor. But, the instance has to be generated via spring, else it won't work.
  • Pankaj Singhal
    Pankaj Singhal over 5 years
    @JoãoMenighin Refer this example for implementing factory design pattern - howtodoinjava.com/spring-core/…
  • Alexander Petrov
    Alexander Petrov over 5 years
    I am not going to downvote you, but consider you have a downvote from me. I will just quote you one sentence from javadoc. "This subinterface of BeanFactory is not meant to be used in normal application code: stick to BeanFactory or ListableBeanFactory for typical use cases. Integration code for other frameworks can leverage this interface to wire and populate existing bean instances that Spring does not control the lifecycle of. This is particularly useful for WebWork Actions and Tapestry Page objects, for example"
  • Thomas Fritsch
    Thomas Fritsch over 5 years
    @AlexandarPetrov I agree. This approach is kind of smelly and should be avoided if there are cleaner solutions possible.
  • João Menighin
    João Menighin over 5 years
    This is actually my case I think. I'm kinda building up a mini framework to generate code... It's really good to know about this. Another approach that worked for me was making my MyClass marked with Scope("prototype") (so it would get a new instance everytime) and using the ApplicationContext.getBean() method. Since my question was basically "Is there a way to Autowire an object outside Spring lifecycle?", I think your answer fits best. I will give an upvote to the others. Thanks a lot guys!
  • João Menighin
    João Menighin over 5 years
    @ThomasFritsch maybe you could edit your answer with Alexandar's comment so people getting here would know about this "caveat" on the java doc. Thanks again!
  • Thomas Fritsch
    Thomas Fritsch over 5 years
    @JoãoMenighin good point, I've added Alexandar's objection into my answer.