2017-01-03 13 views
5

Używam Wiosna & graphql-Java (graphql-java-adnotacji) w moim projekcie. Do pobierania części danych używam modułu DataFetcher do pobierania danych z usługi (z bazy danych).wstrzyknąć fasoli w DataFetcher z GraphQL

Dziwne jest to, że: myService jest zawsze null. Ktoś zna przyczynę?

DataFetcher

@Component 
public class MyDataFetcher implements DataFetcher { 

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

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

Schemat

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

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

Moja_usługa

@Service 
public class MyService { 

    @Autowired 
    private MyRepository myRepo; 

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

Test główny

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

Czy możesz również opublikować MyService.java? – Jobin

+0

Wysłałem MyService.java @jobin – JasonS

+0

Czy wystąpił błąd podczas uruchamiania aplikacji? – Jobin

Odpowiedz

5

Ponieważ w graphql-Java-adnotacją fetcher dane określone przypisaniem, jest skonstruowany przez ramy (przy użyciu odbicia, aby uzyskać konstruktora), a tym samym nie może być fasoli.

Rozwiązaniem, które znalazłem w tym celu jest ustawienie go na ApplicationContextAware, a następnie mogę zainicjować pewne pole statyczne zamiast komponentu bean. Nie najmilsza rzecz, ale to działa:

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

Zasadniczo będziesz wciąż dostać nową instancję pobierania poczty danych inicjowane przez graphQL, ale także wiosną będzie go zainicjować, a ponieważ myService jest statyczna, będziesz weź zainicjalizowany.

+0

Naprawdę doceniam, działa! @Nir Levy – JasonS

+0

Nie sądzę, że to dobre rozwiązanie. MyDataFetcher jest singleton i setApplicationContext jest wywoływany raz. Dlaczego usługa myService musi być statyczna? –

0

Rozwiązanie dostarczone przez @Nir Levy działa idealnie. Tylko po to, żeby było tu trochę więcej do ponownego użytku. Możemy wyodrębnić klasę abstrakcyjną, która enkapsuluje logikę wyszukiwania fasoli i sprawi, że praca z autowiringami będzie dla podklas.

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

A podklasa może być tak:

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

ThoughtWorks podejście @ Nir (oraz często używam go wewnątrz Słuchaczy JPA Event), obiekty DataFetcher są Singletons, więc wstrzykiwanie poprzez właściwości statycznych jest trochę hacky.

Jednak execute metoda GraphQL pozwala na przechodzą w obiekcie jako kontekst, które następnie będą dostępne w danym obiekcie DataFetchingEnvironment wewnątrz swojej DataFetcher (patrz graphql.execute() linię poniżej):

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