【问题标题】:How to pass Spring context as a method argument?如何将 Spring 上下文作为方法参数传递?
【发布时间】:2018-10-11 15:29:09
【问题描述】:

我需要一些有关 Spring 的帮助(SpringBoot 1.3.2.RELEASE,Spring 4.3.11.RELEASE)。 用例比较特殊,需要说明。

我有一个 Spring 应用程序管理客户发送的请求。为了处理这些请求,我们需要使用带有@Autowired 注解声明的服务。非常经典。

最近,我们决定处理来自其他国家/地区的新型请求。 关键是面对不同的情况和类型的请求,我们决定实现一个策略模式。 -> 根据请求的类型,我们执行在运行时选择的策略。每个策略都包含在一个具体的类中,所有策略共享同一个接口。

所以,我有:

  • 一个主类,其中请求处理已完全完成。为了完成它的工作,这个类过去常常调用一些使用@Autowired 注解声明的服务。

现在我有:

  • 仅初始化请求处理的主类。去做 它的作用是,此类将在运行时根据某些标准实例化一个策略。
  • 一个 IStrategy(接口)有 2 个方法将被实现 通过我的 2 个具体课程
  • 2 个具体的类(以及更多的类)可以完成这项工作。 非常重要的一点是,这些具体的类将在运行时实例化,在 Spring 上下文加载之后。

问题是Spring上下文加载后,就不能再使用@Autowired注解了。 我想在我的具体策略类中使用的所有服务都不能再被@Autowired 调用并保持为空。

我通过将我需要的服务作为参数传递给我的具体策略类找到了一种解决方法,但是我必须作为参数传递的服务数量因一种策略而异。

我认为我应该传递整个 Spring 上下文,但我不知道该怎么做。而且我也不知道如何从上下文中访问所有带注释的服务。

PS:我没有显示代码行,因为我认为实际上没有必要。如果您认为使用代码会更明确,我会发送一些。

提前致谢。

【问题讨论】:

  • 您可能想要定义一个自己的注册服务,在该服务中注册新的实现并且可以在运行时查找。您无需直接注入策略,而是注入可用于检索实际策略的注册表服务。注册服务如何维护策略取决于您和实施细节。
  • 创建类似ServiceProvider 的东西,其中包含所有可能的服务(地图、列表等)。然后将provider注入到控制器中,调用得到相关服务使用:serviceProvider.getService(requestContext).execute(...);
  • 也许这个答案对你有帮助stackoverflow.com/a/6596801/7634201

标签: java spring spring-boot


【解决方案1】:

您应该声明一个工厂来映射依赖关系,而不是将服务声明为 bean,它会在将服务的具体实例返回给注入器之前检查请求。 看看这里:

https://grokonez.com/spring-framework/spring-core/use-spring-factory-method-create-spring-bean

【讨论】:

    【解决方案2】:

    您可以使用下面的类来静态获取应用程序上下文和 Beans

    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Service;
    
    @Service
    public class BeanUtil implements ApplicationContextAware {
    
        private static ApplicationContext context;
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            context = applicationContext;
        }
        public static <T> T getBean(Class<T> beanClass) {
            return context.getBean(beanClass);
        }
    
    
    
    }
    

    【讨论】:

      【解决方案3】:

      大家好,很快就回复了我。 首先,我必须为这么晚才回复你们所有的 cmets 表示歉意。上一个 sprint 是一个很大的负担,新的也不是更好^^

      我需要在 Spring 上下文完成创建和加载应用程序的所有部分后创建对象。 作为策略模式的一部分,我必须在运行时实例化一个类,具体取决于我必须处理的请求文件中的一些值。此类需要使用 @Autowired 注释声明的许多服务,但所有自动装配的对象仍然是“null”,因为在加载上下文后调用。

      这是我最初想使用的代码。 没有 Spring 也没关系。

      Function<Document, IStrategy> func = doc -> {
              String strategyToApply = "";
              IStrategy strategy = null;
              switch(doc.getPlace()) {
                  case "Paris":
                      strategyToApply = "strategies_classes.ProcessParis";
                      break;
                  case "New York":
                      strategyToApply = "strategies_classes.ProcessNewYork";
                      break;
                  }
                  case "Roma":
                      ...
              try {
                  **Class<?> classToUse = Class.forName(strategyToApply);
                  strategy = (IStrategy) classToUse.newInstance();**
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              } catch (InstantiationException e) {
                  e.printStackTrace();
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              }
              return strategy;
      };
      
      Consumer<Document> consumerStrategy = doc -> {
          IStrategy strategy = func.apply(doc);
          strategy.bookRequest(doc);
      };
      documents.stream()
          .forEach(consumerStrategy);
      

      我终于找到了魔法物品。当 Spring 对象的生命周期不符合我们自己的概念时,这是一个很好的解决方法。

      要使用它,您只需使用 @Autowired 声明它:

      @Autowired
      private AutowireCapableBeanFactory autowireBeanFactory;
      

      请注意,AutowireCapableBeanFactory 是一个 Spring 对象,您无需在其他任何地方声明!

      然后,使用它,很简单(我设计了一个全新的服务,与您在上面看到的非常不同,但它的作用相同):

      public <T> T getStrategyToUse(Entity bookingCtr, Funder funder, StrategyEnum strategy) throws FunctionalException {
              String strategyToApply = null;
              strategyToApply = strategyDao.getClassToApply(bookingCtr, funder, strategy);
              Class<?> classToUse;
              try {
                  classToUse = Class.forName(strategyToApply);
                  T strat = (T) **autowireBeanFactory.getBean**(classToUse);
                  return (T) strat;
              } catch (ClassNotFoundException e) {
                  LOGGER.error("The indicated Strategy class was not found", e);
              }
              return null;
          }
      

      在运行时加载时,所选类将毫无问题地实例化,并且其所有自动装配的对象将不再为空。

      我希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-10-14
        • 2015-03-10
        • 2015-01-22
        • 2019-06-12
        • 1970-01-01
        • 1970-01-01
        • 2010-11-16
        相关资源
        最近更新 更多