【问题标题】:Inject bean into DataFetcher of GraphQL将 bean 注入 GraphQL 的 DataFetcher
【发布时间】:2017-05-17 08:09:00
【问题描述】:

我在我的项目中使用 Spring 和 graphql-java (graphql-java-annotation)。 对于检索数据部分,我使用 DataFetcher 从服务(从数据库)获取数据。

奇怪的是:myService 总是 null。有人知道原因吗?

DataFetcher

@Component
public class MyDataFetcher implements DataFetcher {

    // get data from database
    @Autowired
    private MyService myService;

    @Override
    public Object get(DataFetchingEnvironment environment) {
        return myService.getData();
    }
}

架构

@Component
@GraphQLName("Query")
public class MyGraphSchema {

    @GraphQLField
    @GraphQLDataFetcher(MyDataFetcher.class)
    public Data getData() {
        return null;
    }
}

我的服务

@Service
public class MyService {

    @Autowired
    private MyRepository myRepo;

    @Transactional(readOnly = true)
    public Data getData() {
        return myRepo.getData();
    }
}

主要测试

@Bean
public String testGraphql(){
    GraphQLObjectType object = GraphQLAnnotations.object(MyGraphSchema.class);
    GraphQLSchema schema = newSchema().query(object).build();
    GraphQL graphql = new GraphQL(schema);

    ExecutionResult result = graphql.execute("{getData {id name desc}}");;
    Map<String, Object> v = (Map<String, Object>) result.getData();
    System.out.println(v);
    return v.toString();
}

【问题讨论】:

  • 你也可以发布 MyService.java 吗?
  • 我已经发布了 MyService.java @jobin
  • 启动应用时是否出现错误?

标签: java spring graphql graphql-java


【解决方案1】:

虽然@Nir 的方法有效(我经常在 JPA 事件侦听器中使用它),但 DataFetcher 对象是单例,因此通过静态属性注入有点麻烦。

但是,GraphQL 的 execute 方法允许您将对象作为上下文传递,然后可以在 DataFetcher 内的 DataFetchingEnvironment 对象中使用(请参见下面的 graphql.execute() 行):

@Component
public class GraphQLService {

    @Autowired
    MyService myService;

    public Object getGraphQLResult() {
        GraphQLObjectType object = GraphQLAnnotations.object(MyGraphSchema.class);
        GraphQLSchema schema = newSchema().query(object).build();
        GraphQL graphql = new GraphQL(schema);

        ExecutionResult result = graphql.execute("{getData {id name desc}}", myService);

        return result.getData();
    }
}

public class MyDataFetcher implements DataFetcher {

    @Override
    public Object get(DataFetchingEnvironment environment) {
        MyService myService = (MyService) environment.getContext();

        return myService.getData();
    }
}

【讨论】:

    【解决方案2】:

    @Nir Levy 提供的解决方案完美运行。只是为了让它在这里更可重用。我们可以提取一个抽象类来封装 bean 查找逻辑并使其子类自动装配。

    public abstract class SpringContextAwareDataFetcher implements DataFetcher, ApplicationContextAware {
    
        private static ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    
        @Override
        public final Object get(DataFetchingEnvironment environment) {
            return applicationContext.getBean(this.getClass()).fetch(environment);
        }
    
        protected abstract Object fetch(DataFetchingEnvironment environment);
    }
    

    而子类可以是这样的:

    @Component
    public class UserDataFetcher extends SpringContextAwareDataFetcher {
    
        @Autowired
        private UserService userService;
    
        @Override
        public String fetch(DataFetchingEnvironment environment) {
            User user = (User) environment.getSource();
            return userService.getUser(user.getId()).getName();
        }
    }
    

    【讨论】:

      【解决方案3】:

      由于graphql-java-annotation中的data fetcher是由annotation定义的,它是由框架构造的(使用反射获取构造函数),所以它不能是bean。

      我为此找到的解决方法是将其设置为ApplicationContextAware,然后我可以初始化一些静态字段而不是 bean。不是最好的东西,但它有效:

      @Component
      public class MyDataFetcher implements DataFetcher, ApplicationContextAware {
      
          private static MyService myService;
          private static ApplicationContext context;
      
          @Override
          public Object get(DataFetchingEnvironment environment) {
              return myService.getData();
          }
      
          @override
          public void setApplicationContext(ApplicationContext applicationContext) throws BeansExcepion {
              context = applicationContext;
              myService = context.getBean(MyService.class);
          }
      }
      

      基本上,您仍然会得到一个由 graphQL 初始化的数据获取器的新实例,但 spring 也会对其进行初始化,并且由于 myService 是静态的,因此您将获得初始化的实例。

      【讨论】:

      • 我认为这不是一个好的解决方案。 MyDataFetcher 是一个单例并且 setApplicationContext 被调用一次。为什么 myService 必须是静态的?
      猜你喜欢
      • 2017-06-21
      • 2019-06-18
      • 2018-05-21
      • 2020-07-20
      • 2018-02-01
      • 1970-01-01
      • 2013-08-31
      • 2015-04-15
      • 1970-01-01
      相关资源
      最近更新 更多