【问题标题】:Using @Autowired outside of Component在组件之外使用 @Autowired
【发布时间】:2020-07-09 18:48:29
【问题描述】:

我想在我的应用程序的不同位置使用一项服务:

@Service
public class ActionListMain  { /* .... */ }

首先我想在实体上下文中使用:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "myTable")
public class myTable {
    @Autowired
    @Transient
    private static ActionListMain allActions;
    /* .... */
}

我还想在其他非注释类中使用它,例如:

public class Action {
    @Autowired
    private ActionListMain actionListMain;
}

另一方面,我有一个 StartupComponent,它按预期连接:

@Component
public class StartupComponent {
    @Autowired
    private ActionListMain actionListMain;
}

为什么它在所有其他类中都是 NULL?

【问题讨论】:

    标签: spring-boot autowired spring-annotations spring-bean


    【解决方案1】:

    Spring 只能将 bean 自动装配到 Spring 管理的类中。由于 Action 和 MyTable 类不是由 Spring 管理的,因此 ActionListMain 不能在那里自动装配。

    有一个(hackish)解决方法,包括创建一个 Spring 管理的 bean 并在其中自动装配 applicationContext,然后从静态 applicationContext 获取 bean。

    @Component
    public class SpringContext implements ApplicationContextAware {
    
        //Has to be static to have access from non-Spring-managed beans
        private static ApplicationContext context;
    
        public static <T extends Object> T getBean(Class<T> beanClass) {
            return context.getBean(beanClass);
        }
    
        @Override
        // Not static
        public void setApplicationContext(ApplicationContext context) throws BeansException {
            SpringContext.context = context;
        }
    }
    

    【讨论】:

      【解决方案2】:

      正如 @Janar 所述,Spring 只能将 bean 自动装配到 Spring 管理的类中。

      您可以添加这个棘手的实现,它允许您在@Component 之外获得ActionListMain

      我们将把所有 bean 注入(Spring + Custom)集中在BeansManager

      专业版/续版

      • 所有新服务都会自动实例化
      • 集中启动解决Circular Dependencies in Spring
      • 您需要拨打.getBean手动启动

      依赖关系

      <!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
      <dependency>
          <groupId>org.reflections</groupId>
          <artifactId>reflections</artifactId>
          <version>0.9.12</version>
      </dependency>
      

      实现Injectable的类需要有没有参数的构造函数

      Injectable 接口获取您的 @Service / @Component 实例

      public interface Injectable {
      
          /**
          * Initiate all beans dependencies.
          * @param manager Beans manager allows to get singleton instances
          */
          void init(BeansManager manager);
      }
      

      BeansManager 类

      import java.lang.reflect.Method;
      import java.util.List;
      import java.util.Set;
      
      import javax.annotation.PostConstruct;
      
      import org.reflections.Reflections;
      import org.springframework.beans.factory.NoSuchBeanDefinitionException;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Component;
      
      @Component
      public class BeansManager {
      
          @Autowired
          private List<Injectable> injectables;
      
          /**
           * This method will make sure all the injectable classes will get the
           * BeansManager in its steady state, where it's class members are ready to be
           * set.
           */
          @PostConstruct
          protected void inject() throws Exception {
      
              // Init all Spring @Component classes
              for (Injectable injectableItem : injectables) {
                  injectableItem.init(this);
              }
      
              //Setup your package to scan
              Reflections reflections = new Reflections("com.example.demo");
              Set<Class<? extends Injectable>> classes = reflections.getSubTypesOf(Injectable.class);
      
              for (Class<? extends Injectable> clazz : classes) {
      
                  // Avoid calling twicely if clazz already initialized by Spring
                  if (getBean(clazz, Boolean.FALSE) == null) {
                      Method init = clazz.getDeclaredMethod("init", BeansManager.class);
                      init.invoke(clazz.newInstance(), this);
                  }
      
              }
          }
      
          /**
           * Get singleton from BeansManager.
           * 
           * @param <T>   Spring service / component class
           * @param clazz singleton class
           * @return Singleton / throws exception
           * @throws NoSuchBeanDefinitionException If bean not found (required=true)
           */
          public <T> T getBean(Class<T> clazz) {
              return getBean(clazz, Boolean.TRUE);
          }
      
          /**
           * Get singleton from BeansManager.
           * 
           * @param <T>      Component service / component class
           * @param clazz    singleton class
           * @param required If bean not found, it throw exception (true) or returns null
           *                 (false)
           * @return Singleton / null / throws exception
           * @throws NoSuchBeanDefinitionException If bean not found (required=true) 
           */
          public <T> T getBean(Class<T> clazz, boolean required) {
              Object bean = null;
              for (Injectable injectableItem : injectables) {
                  if (clazz.isInstance(injectableItem)) {
                      bean = clazz.cast(injectableItem);
                      return clazz.cast(bean);
                  }
              }
      
              if (required) {
                  throw new NoSuchBeanDefinitionException(clazz);
                  
              } else {
                  return null;
              }
          }
      }
      

      为您的课程实现Injectable

      @Service
      public class ActionListMain implements Injectable
      ...
      

      public class MyTable implements Injectable
      ...
      
          @Override
          public void init(BeansManager manager) {
              allActions = manager.getBean(ActionListMain.class);
          }
      ...
      

      public class Action implements Injectable
      
          private static ActionListMain actionListMain;
      
          @Override
          public void init(BeansManager manager) {
              actionListMain = manager.getBean(ActionListMain.class);
          }
      

      @Component
      public class StartupComponent implements Injectable
      ...
          @Override
          public void init(BeansManager manager) {
              actionListMain = manager.getBean(ActionListMain.class);     
          }  
      

      初始化您的应用程序

      @SpringBootApplication
      public class DemoApplication implements CommandLineRunner {
      
          @Autowired
          ActionListMain actionListMain;
          
          @Autowired
          StartupComponent startupComponent;
          
          Action action   = new Action();
          MyTable myTable = new MyTable();
          
          public static void main(String[] args) {
              SpringApplication.run(DemoApplication.class, args);
          }
      
          @Override
          public void run(String... args) throws Exception {
              actionListMain.sayHello();
              startupComponent.sayHello();
              action.sayHello();
              myTable.sayHello();
          }
      }
      

      com.example.demo.ActionListMain:准备就绪
      com.example.demo.StartupComponent 类 com.example.demo.ActionListMain@d28c214
      com.example.demo.Action 类 com.example.demo.ActionListMain@d28c214
      com.example.demo.MyTable 类 com.example.demo.ActionListMain@d28c214

      【讨论】:

        猜你喜欢
        • 2020-12-27
        • 2020-08-25
        • 2020-08-22
        • 2011-10-06
        • 2020-04-26
        • 1970-01-01
        • 1970-01-01
        • 2017-07-21
        • 2010-12-10
        相关资源
        最近更新 更多