将bean注入GraphQL的DataFetcher

时间:2017-01-03 09:15:16

标签: java spring graphql graphql-java

我正在使用 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;
    }
}

为MyService

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

3 个答案:

答案 0 :(得分:5)

由于在graphql-java-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是静态的,你将得到初始化的。

答案 1 :(得分:1)

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

答案 2 :(得分:0)

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

但是,GraphQL的execute方法允许您将对象作为上下文传入,然后在DataFetchingEnvironment内的DataFetcher对象中可以使用该对象(请参阅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();
    }
}