将 bean 注入到 GraphQL 的 DataFetcher 中
Inject bean into DataFetcher of GraphQL
我正在使用 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();
}
由于在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
是静态的,你会得到初始化的一.
@Nir Levy 提供的解决方案完美运行。只是为了让它在这里更容易重用。我们可以提取一个抽象 class,它封装了 bean 查找逻辑,并使其子classes 自动装配。
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);
}
而子class可以是这样的:
@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();
}
}
虽然@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();
}
}
我正在使用 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();
}
由于在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
是静态的,你会得到初始化的一.
@Nir Levy 提供的解决方案完美运行。只是为了让它在这里更容易重用。我们可以提取一个抽象 class,它封装了 bean 查找逻辑,并使其子classes 自动装配。
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);
}
而子class可以是这样的:
@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();
}
}
虽然@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();
}
}