I'm trying to develop my first java Spring Bboot app that calls Strava API and gets my activities for the given period of time. I've registered my app on Strava's website and got client_id and client secret. I've generated spring-swagger-codegen-api-client and awtowired the client to the app.
@Configuration
public class StravaIntegrationConfiguration {
@Bean
public ActivitiesApi stravaApi(){
return new ActivitiesApi(apiClient());
}
@Bean
public ApiClient apiClient(){
return new ApiClient();
}
}
Then I use this bean in AdapterClass
public class Adapter {
@Autowired
private static ActivitiesApi activitiesApi;
public static void getActivities(Integer before, Integer after, Integer page, Integer perPage) {
final List<SummaryActivity> loggedInAthleteActivities = activitiesApi.getLoggedInAthleteActivities(before, after, page, perPage);
System.out.println("ВСЕГО АКТИВНОСТЕЙ" loggedInAthleteActivities.size());
}
}
@SpringBootApplication
@Import(StravaIntegrationConfiguration.class)
public class App {
public static void main(String[] args) throws SQLException {
SpringApplication.run(App.class);
Adapter.getActivities(1636130496, 1635529296, 1, 30);
}
}
When I run this code I get NPE, because activitiesApi
is null
.
What is the problem? Please kindly advise.
Does it concern authentication? Could you advise also any code sample on how to make Strava authentication in my app?
CodePudding user response:
You can't use autowire with static fields. More info can be found in this link: Can you use @Autowired with static fields?
CodePudding user response:
It has nothing to do with Strava authentication. It is related to Spring context and Spring Beans and how to inject them. As already mentioned you can't autowire Spring-managed beans in static fields (it makes no sense actually). Having said that you need to fix that first:
@Component
public class Adapter {
@Autowired
private ActivitiesApi activitiesApi;
public void getActivities(Integer before, Integer after, Integer page, Integer perPage) {
final List<SummaryActivity> loggedInAthleteActivities = activitiesApi.getLoggedInAthleteActivities(before, after, page, perPage);
System.out.println("ВСЕГО АКТИВНОСТЕЙ" loggedInAthleteActivities.size());
}
}
Also, note that the method changed from a static one to an instance one and that the annotation @Component
was added to the class. The reason is that a Spring-managed bean can only be injected into other Spring-managed beans.
Additionally, it seems to me that you are trying to do something after the Spring context has been initialized. One possible way to do this is creating a bean that implements the ApplicationListener interface:
@Component
public class StartupApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private Adapter adapter;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
adapter.getActivities(1636130496, 1635529296, 1, 30);
}
}
This means that you can and you should remove the line Adapter.getActivities(1636130496, 1635529296, 1, 30);
from your main class:
@SpringBootApplication
@Import(StravaIntegrationConfiguration.class)
public class App {
public static void main(String[] args) throws SQLException {
SpringApplication.run(App.class);
}
}
Finally, and as a side note, please consider using constructor injection instead of field injection. It has a couple of advantages over field injection: making the class easier to unit test, allowing the objects to be immutable, explicitly definition of which dependencies are mandatory, etc... (you can read more at https://reflectoring.io/constructor-injection/).