80

I am developing a SpringBoot project and I want to get the bean by its name using applicationContext. I have tried many solution from web but could not succeed. My Requirement is that I have a controller

ControllerA

and inside the controller I have a method getBean(String className). I want to get instance of registered bean. I have hibernate entities and I want to get an instance of the bean by passing the name of class only in getBean method.

Please help if someone know the solution.

Wojciech Wirzbicki
  • 3,887
  • 6
  • 36
  • 59
Qasim
  • 9,058
  • 8
  • 36
  • 50
  • In main class add method: `@Bean public CommandLineRunner run(ApplicationContext appContext) { return args -> { String[] beans = appContext.getBeanDefinitionNames(); Arrays.stream(beans).sorted().forEach(System.out::println); }; }` [how-to-display-all-beans-loaded-by-spring-boot/](https://mkyong.com/spring-boot/how-to-display-all-beans-loaded-by-spring-boot/) – Nick Dong Nov 28 '22 at 09:01

13 Answers13

129

You can Autowire the ApplicationContext, either as a field

@Autowired
private ApplicationContext context;

or a method

@Autowired
public void context(ApplicationContext context) { this.context = context; }

Finally use

context.getBean(SomeClass.class)
reos
  • 8,766
  • 6
  • 28
  • 34
  • 36
    Field injection is considered as bad practice. – luboskrnac Dec 04 '15 at 22:42
  • 3
    @luboskrnac: Yes.Curious to understand why. – Priyanka.Patil Mar 17 '16 at 10:04
  • 9
    Just google "Field vs Constructor injection". Also Oliver Gierke (Spring Data Team lead) opposes field injection. – luboskrnac Mar 17 '16 at 10:52
  • 2
    So how should this answer be changed so that it uses constructor injection? – ssimm Nov 06 '19 at 11:47
  • Autowired via constructor: `public MyClass (ApplicationContext context) { context.getBean(ControllerA.class)}` – Zon Dec 25 '19 at 12:31
  • 5
    @crabbly Baeldung is NOT the Spring documentation! – zyexal Jun 12 '20 at 08:33
  • the preference of constructor injection over field injection is the fact that if a class instantiation is being delegated to spring, you shouldn't be able to do new MyClass(), therefore, by autowiring the constructor parameters, you are telling the developer who MAY try to call the constructor, that he can't, because he needs to pass through those parameters in order to build an instance... hope i was clear. best regards – Nicolas Mariano Obregon Sep 01 '20 at 04:03
  • Make your own choice, but don't impose them: here it is : https://softwareengineering.stackexchange.com/questions/300706/dependency-injection-field-injection-vs-constructor-injection – pdem Sep 09 '21 at 08:50
51

You can use ApplicationContextAware.

ApplicationContextAware:

Interface to be implemented by any object that wishes to be notified of the ApplicationContext that it runs in. Implementing this interface makes sense for example when an object requires access to a set of collaborating beans.

There are a few methods for obtaining a reference to the application context. You can implement ApplicationContextAware as in the following example:

package hello;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
 
@Component
public class ApplicationContextProvider implements ApplicationContextAware {

    private ApplicationContext applicationContext;

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

 public ApplicationContext getContext() {
        return applicationContext;
    }
    
}

Update:

When Spring instantiates beans, it looks for ApplicationContextAware implementations, If they are found, the setApplicationContext() methods will be invoked.

In this way, Spring is setting current applicationcontext.

Code snippet from Spring's source code:

private void invokeAwareInterfaces(Object bean) {
        .....
        .....
 if (bean instanceof ApplicationContextAware) {                
  ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
   }
}

Once you get the reference to Application context, you get fetch the bean whichever you want by using getBean().

Community
  • 1
  • 1
Sundararaj Govindasamy
  • 8,180
  • 5
  • 44
  • 77
21

actually you want to get the object from the Spring engine, where the engine already maintaining the object of your required class at that starting of the spring application(Initialization of the Spring engine).Now the thing is you just have to get that object to a reference.

in a service class

@Autowired
private ApplicationContext context;

SomeClass sc = (SomeClass)context.getBean(SomeClass.class);

now in the reference of the sc you are having the object. Hope explained well. If any doubt please let me know.

Debendra Parida
  • 279
  • 3
  • 9
6

Using SpringApplication.run(Class<?> primarySource, String... arg) worked for me. E.g.:

@SpringBootApplication
public class YourApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(YourApplication.class, args);

    }
}
IKo
  • 4,998
  • 8
  • 34
  • 54
5

As an alternative approach you can use ConfigurableApplicationContext to get bean of any class which is annotated with @Component, @Repository or @Service.

Let's say you want to get a bean of the class BaseComponent :

@Service
public class BaseComponent {
    public String getMessage() {
        return "hello world";
    }
}

Now you can use ConfigurableApplicationContext to get the bean:

@Component
public class DemoComponent {
    @Autowired
    ConfigurableApplicationContext applicationContext;
    
    public BaseComponent getBeanOfBaseComponent() {
        return applicationContext.getBean(BaseComponent.class);
    }
}
Papai from BEKOAIL
  • 1,469
  • 1
  • 11
  • 17
4

Even after adding @Autowire if your class is not a RestController or Configuration Class, the applicationContext object was coming as null. Tried Creating new class with below and it is working fine:

@Component
public class SpringContext implements ApplicationContextAware{

   private static ApplicationContext applicationContext;

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

you can then implement a getter method in the same class as per your need to get the bean. Like:

    applicationContext.getBean(String serviceName,Interface.Class)
3

You can use the ApplicationContextAware class that can provide the application context.

public class ApplicationContextProvider implements ApplicationContextAware {

    private static ApplicationContext ctx = null;

    public static ApplicationContext getApplicationContext() {
        return ctx;
    }

    @Override
    public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
        ApplicationContextProvider.ctx = ctx;
    }

    /**
     * Tries to autowire the specified instance of the class if one of the specified
     * beans which need to be autowired are null.
     *
     * @param classToAutowire        the instance of the class which holds @Autowire
     *                               annotations
     * @param beansToAutowireInClass the beans which have the @Autowire annotation
     *                               in the specified {#classToAutowire}
     */
    public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) {
        for (Object bean : beansToAutowireInClass) {
            if (bean == null) {
                ctx.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
            }
        }
    }

}
yeaaaahhhh..hamf hamf
  • 746
  • 2
  • 13
  • 34
1

If you are inside of Spring bean (in this case @Controller bean) you shouldn't use Spring context instance at all. Just autowire className bean directly.

BTW, avoid using field injection as it's considered as bad practice.

luboskrnac
  • 23,973
  • 10
  • 81
  • 92
  • Not true. Constructor injection with a huge amount of parameters is bad practice. Field injection should be used instead. – T3rm1 Jun 13 '19 at 08:02
  • 1
    @T3rm1, can you provide some links backing up your statement? If you use intellij there is by default warning if you use field injection. I read various times Spring team members stating that constructor injection is preferred. So your statement is "Not true". – luboskrnac Jun 18 '19 at 08:50
  • This is an endless discussion. All the boilerplate introduced by the constructor is really ugly. Have you ever used subclasses? Try to add another dependency to the base class if you have a lot of subclasses. Also, with constructor injection the subclasses have to provide all the dependecies instead of the container. That's not the idea of IoC. – T3rm1 Jun 18 '19 at 09:59
  • 1
    You can use Lombok's @AllArgsConstructor to avoid boilerplate. Inheritance of singleton beans? To me design smell. – luboskrnac Jun 25 '19 at 11:29
  • 1
    @T3rm1 "shared mutable state is the root of all evil". Proceed with caution. – java-addict301 Jul 18 '19 at 19:58
1

One API method I use when I'm not sure what the bean name is org.springframework.beans.factory.ListableBeanFactory#getBeanNamesForType(java.lang.Class<?>). I simple pass it the class type and it retrieves a list of beans for me. You can be as specific or general as you'd like to retrieve all the beans associated with that type and its subtypes, example

@Autowired
ApplicationContext ctx

...

SomeController controller = ctx.getBeanNamesForType(SomeController)
Jose Quijada
  • 558
  • 6
  • 13
0
package com.codewithsouma.firstspringproject;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class TestBinarySearch {
    @Autowired
    private ApplicationContext applicationContext;

    public void execute(){
        BinarySearchImpl binarySearch = applicationContext.getBean(BinarySearchImpl.class);
        int index = binarySearch.binarySearch(new int[]{10,20,30,40,50},30);
        System.out.println("The number is present at index: "+index);
    }
}
-1

Easy way in configration class call the BEAN annoted method . Yes u heard it right---- :P calling SpringBoot @Bean annoted method return the same bean from config .I was trying to call a logout in @predestroy method in config class from a bean and direcltly called the method to get the same bean . P.S. : I added debug in the @bean annotated method but it didn't entered the method even when i called it.Sure to blame -----> Spring Magic <----

-2

You can use ServiceLocatorFactoryBean. First you need to create an interface for your class

public interface YourClassFactory {
    YourClass getClassByName(String name);
}

Then you have to create a config file for ServiceLocatorBean

@Configuration
@Component
public class ServiceLocatorFactoryBeanConfig {

    @Bean
    public ServiceLocatorFactoryBean serviceLocatorBean(){
        ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
        bean.setServiceLocatorInterface(YourClassFactory.class);
        return bean;
    }
}

Now you can find your class by name like that

@Autowired
private YourClassfactory factory;

YourClass getYourClass(String name){
    return factory.getClassByName(name);
}
Ali Katkar
  • 517
  • 4
  • 7
  • I think this solution can not be understood very well. Getting a bean from ApplicationContext has already been replied. This solution is getting a regular class' object in Spring Boot. Just to show an hint, it is not answer for original question. – Ali Katkar Sep 06 '20 at 08:45
-3

Just use:

org.springframework.beans.factory.BeanFactory#getBean(java.lang.Class)

Example:

@Component
public class Example {

    @Autowired
    private ApplicationContext context;

    public MyService getMyServiceBean() {
        return context.getBean(MyService.class);
    }

    // your code uses getMyServiceBean()
}
Sam
  • 899
  • 6
  • 9