在Spring Boot中如何使用应用上下文获取bean

84

我正在开发一个SpringBoot项目,希望能够通过applicationContext获取bean的名称。我尝试了许多来自网络的解决方案,但都没有成功。我的要求是,我有一个控制器。

ControllerA
在控制器中,我有一个名为getBean(String className)的方法。我想要获取注册bean的实例。我有Hibernate实体,并且我希望通过仅在getBean方法中传递类名来获取bean的实例。
如果有人知道解决方案,请帮忙。

在主类中添加方法:`@Bean public CommandLineRunner run(ApplicationContext appContext) { return args -> { String[] beans = appContext.getBeanDefinitionNames(); Arrays.stream(beans).sorted().forEach(System.out::println); }; }` [如何显示Spring Boot加载的所有bean/](https://mkyong.com/spring-boot/how-to-display-all-beans-loaded-by-spring-boot/) - Nick Dong
13个回答

133

您可以将ApplicationContext自动连接到字段。

@Autowired
private ApplicationContext context;

或一种方法

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

最后使用

context.getBean(SomeClass.class)

36
字段注入被认为是一种不良实践。 - luboskrnac
3
是的。好奇想了解原因。 - Priyanka.Patil
9
请谷歌搜索“Field vs Constructor injection”。同时,Spring Data 团队领导 Oliver Gierke 反对使用字段注入。 - luboskrnac
2
那么,应该如何更改这个答案以使用构造函数注入? - ssimm
6
@crabbly Baeldung不是Spring文档! - zyexal
显示剩余3条评论

53

您可以使用ApplicationContextAware

ApplicationContextAware:

该接口由希望被通知其运行的ApplicationContext的任何对象实现。例如,当对象需要访问一组协作的bean时,实现此接口是有意义的。

有几种方法可以获取对应用程序上下文的引用。您可以像以下示例中那样实现ApplicationContextAware:

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;
    }
    
}

更新:

当Spring实例化bean时,它会查找ApplicationContextAware接口的实现类,如果找到了,它将调用setApplicationContext()方法。

这样,Spring就设置了当前的应用程序上下文。

来自Spring源代码的代码片段:

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

一旦您获得了应用程序上下文的引用,您可以使用getBean()获取所需的任何bean。


Spring 什么时候会调用 setApplicationContext() 方法? - Shihe Zhang
1
@ShiheZhang,请参考这个答案,我也更新了我的答案:https://dev59.com/pWEi5IYBdhLWcg3wK5rd#21553322 - Sundararaj Govindasamy

21

实际上,您想从Spring引擎获取对象,其中引擎已经在Spring应用程序启动时(初始化Spring引擎)维护所需类的对象。现在问题是,您只需要将该对象获取到一个引用。

在服务类中

@Autowired
private ApplicationContext context;

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

现在在sc的参考中,您拥有该对象。希望解释得很清楚。如果有任何疑问,请让我知道。


7

我尝试过使用SpringApplication.run(Class<?> primarySource, String... arg),成功了。例如:

@SpringBootApplication
public class YourApplication {

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

    }
}

5
作为另一种方法,您可以使用ConfigurableApplicationContext获取任何一个被@Component@Repository@Service注解的类的bean。

假设你想要获取BaseComponent这个类的bean:

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

现在您可以使用ConfigurableApplicationContext获取bean:

@Component
public class DemoComponent {
    @Autowired
    ConfigurableApplicationContext applicationContext;
    
    public BaseComponent getBeanOfBaseComponent() {
        return applicationContext.getBean(BaseComponent.class);
    }
}

4
即使添加了@Autowire,如果您的类不是RestController或Configuration类,则applicationContext对象仍然为空。尝试创建一个新类并使用以下内容即可解决问题:
@Component
public class SpringContext implements ApplicationContextAware{

   private static ApplicationContext applicationContext;

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

然后,您可以在与所需Bean相同的类中实现一个getter方法来获取Bean。例如:

    applicationContext.getBean(String serviceName,Interface.Class)

你知道你的声明是静态的,而你的方法不是静态的吗? - Domas A.
什么问题?它运行良好。 - java dev
是的,@DomasA 是正确的,你的代码中的 static 关键字确实存在问题。 - undefined

3
您可以使用ApplicationContextAware类来提供应用程序上下文。
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);
            }
        }
    }

}

1
如果你在Spring bean内部(在这种情况下是@Controller bean),就不应该使用Spring上下文实例。直接自动装配className bean即可。
顺便说一句,避免使用字段注入,因为它被认为是一种不好的做法。

不是这样的。构造函数注入大量参数是一种不好的实践。应该使用字段注入。 - T3rm1
1
@T3rm1,你能提供一些支持你说法的链接吗?如果你使用Intellij,那么默认情况下会有警告,如果你使用字段注入。我多次阅读了Spring团队成员的声明,他们认为构造函数注入是首选。所以你的说法是“不正确的”。 - luboskrnac
这是一场无休止的讨论。构造函数引入的所有样板代码真的很丑陋。你曾经使用过子类吗?如果有很多子类,请尝试向基类添加另一个依赖关系。此外,使用构造函数注入,子类必须提供所有依赖项,而不是容器。这不是IoC的理念。 - T3rm1
1
你可以使用Lombok的@AllArgsConstructor注解来避免样板代码。单例bean的继承?在我看来是设计上的瑕疵。 - luboskrnac
1
@T3rm1说过:"共享可变状态是万恶之源"。请谨慎处理。 - java-addict301

1

当我不确定Bean的名称时,我使用的一个API方法是org.springframework.beans.factory.ListableBeanFactory#getBeanNamesForType(java.lang.Class<?>)。我只需传入类类型,它就会为我检索出一系列的Beans。您可以根据需要指定特定或通用的方式来检索与该类型及其子类型相关联的所有Beans,例如:

@Autowired
ApplicationContext ctx

...

SomeController controller = ctx.getBeanNamesForType(SomeController)

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);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接