從Spring 應用上下文獲取 Bean 的常用姿勢

  • 2019 年 12 月 9 日
  • 筆記

1. 前言

通常,在Spring應用程式中,當我們使用 @Bean,@Service,@Controller,@Configuration 或者其它特定的註解將 Bean 注入 Spring IoC 。然後我們可以使用 Spring 框架提供的 @Autowired 或者 JSR250、JSR330 規範註解來使用由 Spring IoC 管理的 Bean 。

2. 從應用程式上下文中獲取 Bean

今天我們將來學習如何從 ApplicationContext 中獲取 Bean 。因為有些情況下我們不得不從應用程式上下文中來獲取 Bean 。

2.1 獲取所有的 Bean

ApplicationContext 提供了獲取所有已經成功注入 Spring IoC 容器的 Bean 名稱的方法 getBeanDefinitionNames() 。然後我們可以藉助於其 getBean(String name) 方法使用 Bean 名稱獲取特定的 Bean。我們使用之前文章中介紹的 CommandLineRunner 介面來列印一下結果。

 import org.springframework.beans.factory.annotation.Autowired;   import org.springframework.boot.CommandLineRunner;   import org.springframework.boot.SpringApplication;   import org.springframework.boot.autoconfigure.SpringBootApplication;   import org.springframework.context.ApplicationContext;     import java.util.stream.Stream;     /**    * @author Felordcn    */   @SpringBootApplication   public class WarSpringBootApplication implements CommandLineRunner {       @Autowired       private ApplicationContext applicationContext;         public static void main(String[] args) {           SpringApplication.run(WarSpringBootApplication.class, args);           }         @Override       public void run(String... args) throws Exception {           String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();             Stream.of(beanDefinitionNames).forEach(beanName->{               System.out.println("beanName : " + beanName);                 Object bean = applicationContext.getBean(beanName);                 System.out.println("Spring bean : " + bean);           });         }   }

運行應用會輸出:

 2019-11-05 22:15:54.392  INFO 6356 --- [           main] cn.felord.war.WarSpringBootApplication   : Started WarSpringBootApplication in 4.663 seconds (JVM running for 7.58)   beanName : org.springframework.context.annotation.internalConfigurationAnnotationProcessor   Spring bean : org.springframework.context.annotation.ConfigurationClassPostProcessor@6c44052e   beanName : org.springframework.context.annotation.internalAutowiredAnnotationProcessor   Spring bean : org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@5c371e13   beanName : org.springframework.context.annotation.internalCommonAnnotationProcessor   Spring bean : org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@530a8454   beanName : org.springframework.context.event.internalEventListenerProcessor   Spring bean : org.springframework.context.event.EventListenerMethodProcessor@1e34c607   beanName : org.springframework.context.event.internalEventListenerFactory   Spring bean : org.springframework.context.event.DefaultEventListenerFactory@5215cd9a   beanName : fooController   Spring bean : cn.felord.war.controller.FooController@31198ceb   beanName : IServiceImpl   Spring bean : cn.felord.war.controller.IServiceImpl@51671b08    <more...>

2.2 通過名稱獲取特定的 Bean

從上面列印的資訊我們也能看出來一些端倪。

  • 有的 beanName 是類全限定名。
  • @Component、@Repository、@Service、@Controller等註解建 Bean 時,如果不指定bean名稱,名稱的默認規則是類名的首字母小寫,如 cn.felord.war.controller.FooController 為 fooController。如果類名前兩個或以上個字母都是大寫,那麼名稱與類名一樣如 cn.felord.war.controller.IServiceImpl 為 IServiceImpl
  • @Bean 標識的 Bean 默認 為方法名稱。
  • 配置類相關註解 @Configuration 一般使用類全限定名。

但是請注意:如果你在聲明 Bean 的時候指定了名稱就只是你指定的名稱 。如果我們熟悉這些規則,使用上面提到的getBean(String name) 方法不失為一種好辦法。

2.3 通過類型來獲取 Bean

如果我們不清楚我們想要的特定類型 Bean 的名稱,我們可以根據類型來獲取 Bean 。ApplicationContext 提供了可以載入特定類型的 Bean 的所有 Bean 的方法getBeansOfType()。它將返回 Map <String,Object> 其中鍵是 Bean 名稱,而值是 Bean 的實際對象。

我們修改 2.1 章節 例子中的 run 方法:

     @Override       public void run(String... args) throws Exception {           Map<String, FooController> beansOfType = applicationContext.getBeansOfType(FooController.class);               beansOfType.forEach((beanName,bean)->{               System.out.println("beanName :" + beanName);               System.out.println("bean :" + bean);           });       }

再次運行,控制台列印出:

 beanName :fooController   bean :cn.felord.war.controller.FooController@545f80bf

2.4 獲取特定 Bean 聲明註解標記的 Bean

ApplicationContext 的 getBeansWithAnnotation() 方法可以讓我們獲取 @Service,@Controller或任何其它可以用來創建 Bean 的註解創建的 Bean 。

     @Override       public void run(String... args) throws Exception {           Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(Controller.class);             beansWithAnnotation.forEach((beanName,bean)->{               System.out.println("beanName :" + beanName);               System.out.println("bean :" + bean);           });       }

列印出:

 beanName :fooController   bean :cn.felord.war.controller.FooController@18ca3c62   beanName :basicErrorController   bean :org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController@2c0f7678

3. 總結

在本文中,我們學習如何從 Spring 應用上下文中獲取所有 Bean 的列表。有時我們需要檢查我們期望的 Bean 是否在 Spring 上下文中載入,或者我們需要檢查 Spring IoC 聲明的特定的 Bean 。當然你可以開啟Spring Boot Actuator 的 beans 端點來獲取所有的 Bean 資訊。

Tags: