When do I have to create new instance while using #Autowired [duplicate] - java

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 4 years ago.
Until today I was 100 percent sure I don't have to create new instance of class when defining it as a bean.
Today I got a bit confused.
I will try to explain part of it in words as I think uploading all the code will make it hard to understand.
I created new REST project using intellij and Spring.
I created new class mapped it and added #RestController to it.
In this class I added property of another class that I created myself and added #Autowired to it.
I never created new instance of this class BUT I did add a bean configuration.
until now all worked fine.
I wanted to add ThreadPoolTaskScheduler logic so I opened new class, added new property ThreadPoolTaskScheduler and marked it with #Autowired.
I added a Bean for it:
#Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
ThreadPoolTaskScheduler threadPoolTaskScheduler
= new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(5);
threadPoolTaskScheduler.setThreadNamePrefix(
"ThreadPoolTaskScheduler");
return threadPoolTaskScheduler;
}
Now in the main class if I don't send new instance of the class if will throw me null exception.
so this code is working:
public static void main(String[] args) {
SpringApplication.run(RestApiApplication.class, args);
TaskScheduler taskScheduler = new TaskScheduler(new ThreadPoolTaskScheduler());
taskScheduler.setTaskScheduler();
}
and this code is not:
public static void main(String[] args) {
SpringApplication.run(RestApiApplication.class, args);
TaskScheduler taskScheduler = new TaskScheduler();
taskScheduler.setTaskScheduler();
}
this is the TaskScheduler class:
#Controller
public class TaskScheduler {
#Autowired
ThreadPoolTaskScheduler threadPoolTaskScheduler;
TaskScheduler(){}
TaskScheduler(ThreadPoolTaskScheduler threadPoolTaskScheduler){
this.threadPoolTaskScheduler = threadPoolTaskScheduler;
}
public void setTaskScheduler(){
threadPoolTaskScheduler.schedule(
new ScheduledTask(),
new Date());
}
}
I can't figure out the reason get NULL for threadPoolTaskScheduler at setTaskScheduler, any idea?
If I definde TaskScheduler also as a bean it works ok, why do I have to? spring can handle everthing or nothing?
If you want me to add more code just tell me.

When you instantiate a bean itself, it does not make it managed anymore and thus all of the #Autowired dependencies will not be processed. In order to make sure that dependencies are injected properly, you will need to instantiate the bean through the Spring context. Spring needs to instantiate the whole beans trail in order to perform the dependency injection properly. Please check the IoC details in this link: https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-factory-class-ctor
Looking at your code, there are two ways to do this
Using Spring Boot
The class that will have the main method will look like this:
#SpringBootApplication
public class RestApiApplication implements CommandLineRunner {
#Autowired
private TaskScheduler ts;
public static void main(String[] args) {
SpringApplication.run(RestApiApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
ts.<your-code>
}
}
The other way is to use the ApplicationContext to get the bean instance directly which works well if you need to perform some integration/unit testing.

You can get your bean object like:
public class RestApiApplication{
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(RestApiApplication.class, args);
TaskScheduler ts = context.getBean(TaskScheduler.class);
ts.setTaskScheduler()
}
}
But it's bad idea in real project use ApplicationContext like this example!
If you need REST you should use #RestController annotation.
Check this guide

Related

To call Strava API from SpringBoot application

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?
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/).

How to update row value using JPA repository outside the controller?

I was trying to update the table row data from outside the controller (Inside some threads) and getting 'NullPointerException' always.
Thread code:
public class S3Thread implements Runnable {
#Autowired
private IAutomationService automationService;
#Override
public void run() {
Automation config = new Automation("user1","success");
automationService.updateAutomation(config);
}
}
NullPointer exception thrown on below line:
automationService.updateAutomation(config);
Note: I was able to create/update from the controller class.Only in Thread.
Well, this is the classical Why is my Spring #Autowired field null case. You create the S3Thread instance by yourself, and thus, no beans are injected into it.
Considering you're trying to just do something in a separate thread, you can consider using #Async:
#Async
public void updateAutomationConfiguration() {
Automation config = new Automation("user1", "success");
automationService.updateAutomation(config);
}
Notes:
You have to add the #EnableAsync annotation to any configuration class (eg. your main class) to make this work.
Spring uses proxying by default, which means that you can't add this updateAutomationConfiguration() class to your controller itself. Direct calls to methods within the same bean bypass the proxied logic. The solution is to put this method in a separate bean which can be autowired and invoked from within the controller. I've provided more detailed answers about alternative solutions in this answer.
Spring also has a getting started guide for creating asynchronous methods.
Alternatively, there are also some ways to execute asynchronous calls within controllers, for example by using CompletableFuture within a controller:
#PutMapping("/automation/configuration")
public CompletableFuture<String> updateAutomationConfiguration() {
return CompletableFuture.supplyAsync(() -> {
Automation config = new Automation("user1", "success");
return automationService.updateAutomation(config);
});
}
Related: How to create a non-blocking #RestController webservice in Spring?
Spring does not scan your runnable as it is not annotated with #Component.Try annotating it with #Component/#Service.
Don't forget to set scope required scope though!
There are 2 potential solutions to your problem:
Either you need to make S3Thread class a service by annotating it with #Service or #Component and autowiring it on the calling class, or you can alternatively use the constructor for initializing your automationService, e.g. private IAutomationService automationService = new AutomationService();
Since your thread class is not managed by spring you will not be able to inject the spring managed beans in the S3Thread class.
In order to do that you need to create a class or factory which should be hooked into the spring life cycle.
Once you have the hold of that class you can get the appropriate bean and pass the reference onto/or used in the S3Thread class directly. Something like this
#Component
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext appContext)
{
ctx = appContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
}
public class S3Thread implements Runnable {
#Override
public void run() {
Automation config = new Automation("user1","success");
IAutomationService automationService=
ApplicationContextUtils.getApplicationContext().getBean(IAutomationService .class);
automationService.updateAutomation(config);
}
}

Returning values from #Bean methods [duplicate]

Why can't I use #Autowired in this case?
#SpringBootApplication
public class Application {
#Autowired
BookingService bookingService;
public static void main(String[] args) {
bookingService.book("Alice", "Bob", "Carol");
}
}
but can use #Bean
#SpringBootApplication
public class Application {
#Bean
BookingService bookingService() {
return new BookingService();
}
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
BookingService bookingService = ctx.getBean(BookingService.class);
bookingService.book("Alice", "Bob", "Carol");
}
}
Aren't the two ways to generate BookingService the same thing?
#Bean and #Autowired do two very different things. The other answers here explain in a little more detail, but at a simpler level:
#Bean tells Spring 'here is an instance of this class, please keep hold of it and give it back to me when I ask'.
#Autowired says 'please give me an instance of this class, for example, one that I created with an #Bean annotation earlier'.
Does that make sense? In your first example, you're asking Spring to give you an instance of BookingService, but you're never creating one, so Spring has nothing to give you. In your second example, you're creating a new instance of BookingService, telling Spring about it, and then, in the main() method, asking for it back.
If you wanted, you could remove the two additional lines from the second main() method, and combine your two examples as below:
#SpringBootApplication
public class Application {
#Autowired
BookingService bookingService;
#Bean
BookingService bookingService() {
return new BookingService();
}
public static void main(String[] args) {
bookingService.book("Alice", "Bob", "Carol");
}
}
In this case, the #Bean annotation gives Spring the BookingService, and the #Autowired makes use of it.
This would be a slightly pointless example, as you're using it all in the same class, but it becomes useful if you have the #Bean defined in one class, and the #Autowired in a different one.
#Bean
BookingService bookingService() {
return new BookingService();
}
Annotating #Bean only registers the service as a bean(kind of an Object) in the spring application context. In simple words, it is just registration and nothing else.
#Autowired
BookingService bookingService;
Annotating a variable with #Autowired injects a BookingService bean(i.e Object) from Spring Application Context.
(i.e) The registered object with #Bean annotation will be injected to the variable annotated with #Autowired.
Hope this clears your doubt!
great answer by #DaveyDaveDave
In the example instead of
#Bean
BookingService bookingService() {
return new BookingService();
}
You can use #Service annotation on BookingService class
Contrary to what the highest voted answer here claims, they are NOT two very different things. #Bean and #Autowired and interchangeable in most cases.
Suppose you have a #Bean method that returns an instance of a Car. You can literally get rid of that bean method and add #Component on the Car class and then autowire it.
And vice versa. Whatever class you have instantiated using #Autowired, you can instantiate it inside a class with #Configuration annotation using #Bean on the method.
Places where you will use #Bean instead of #Autowired
1>You do not have access to change the class to add #Component annotation, hence you cannot autowire it.
2>You want to customize the instantiation of the class.
For example if you are instantiating a Resilience4J Circuit breaker class, if you do it inside a method with #Bean, you have the option of setting all the config using code like this
#Bean
public CircuitBreaker fooCircuitBreaker() {
CircuitBreakerConfig.Builder builder = CircuitBreakerConfig.custom().
slidingWindowSize(xxx).
failureRateThreshold(xxx).
waitDurationInOpenState(xxx)).
ignoreException(e -> {
if (e instanceof HttpStatusCodeException) {
HttpStatusCodeException httpStatusCodeException = (HttpStatusCodeException) e;
if (httpStatusCodeException.getStatusCode().is4xxClientError()) {
return true;
}
}
return false;
});
circuitBreakerRegistry.addConfiguration(xxx, builder.build());
return circuitBreakerRegistry.circuitBreaker(xxx, xxx);
}
Here's good article about #Autowired annotation: http://www.baeldung.com/spring-autowire
The #Autowired annotation can instantiate your injectables by defining #ComponentScan("namespace.with.your.components.for.inject") on config class
#Configuration
#ComponentScan("com.baeldung.autowire.sample")
public class AppConfig {}
All components must be marked by #Component annotation. It replaces the #Bean annotation.
#Bean is just for the metadata definition to create the bean(equivalent to tag). #Autowired is to inject the dependancy into a bean(equivalent to ref XML tag/attribute).

SpringBoot - Register a bean before #Component's get scanned

I have a component Login that depends on ValidatorService. ValidatorService is being injected/autowired in the Login constructor. ValidationServiceImpl is provided by an external API, so I can't just annotate it as #Service.
#Component
class Login {
#Autowire
public Login (ValidatorService validator) {
}
}
#SpringBootApplication
public class Starter {
public static void main(String[] args)
{
SpringApplication.run(Starter.class, args);
}
}
I'm looking for a way to register ValidatorService as a bean before #Components get scanned. Is there a way to get ApplicationContext instance before starting the application?
SpringBoot 2.0.4.RELEASE
UPDATE
I need to pass a validationId that I'll get from main(args) to this external API.
public static void main(String[] args) {
String validationId = args[0];
ValidatorService service = ExternalValidationAPI.getValidationServiceImp(validationId);
}
You should be able to declare it as a bean as such in one of your configuration classes:
#Bean
public ValidatorService validatorService(){
return new ValidatorServiceImpl();
}
This will then autowire in the ValidatorService implementation class at the point it is needed. This method needs to go in an #Configuration class (your Starter class is one).
There's a good example of how to do this here.
I believe you can solve your problem with the help of the #Configurable annotation.
Annotate your Login class with #Configurable instead of #Componenet, and when the ValidatorService object becomes available, you can initiate the Login object with it.
You need to define a ValidationService bean :
#Configuration
public class ValidationServiceConfig {
#Bean
public ValidationService validationService(#Value("${validationId}") String validationId) {
return new ValidationServiceImpl(validationId);
}
}
and run the program this way : java -jar program.jar --validationId=xxx
I solved my problem creating a Configuration class and declaring a Bean to handle the instantiation of the external service that will be injected later (as some people have suggested). In order to retrieve the program arguments I autowired DefaultApplicationArguments to retrieve program arguments with getSourceArgs():
#Configuration
public class ValidatorConfig {
#Autowired
DefaultApplicationArguments applicationArguments;
#Bean
public ValidatorService validatorService()
{
String validationId = applicationArguments.getSourceArgs()[0];
return ExternalValidationAPI.getValidationServiceImp(validationId);
}

Spring annotations, read properties

I have small test project to test Spring annotations:
where in nejake.properties is:
klucik = hodnoticka
and in App.java is:
#Configuration
#PropertySource("classpath:/com/ektyn/springProperties/nejake.properties")
public class App
{
#Value("${klucik}")
private String klc;
public static void main(String[] args)
{
AnnotationConfigApplicationContext ctx1 = new AnnotationConfigApplicationContext();
ctx1.register(App.class);
ctx1.refresh();
//
App app = new App();
app.printIt();
}
private void printIt()
{
System.out.println(klc);
}
}
It should print hodnoticka on console, but prints null - String value is not initialized. My code is bad - at the moment I have no experience with annotation driven Spring. What's bad with code above?
You created the object yourself
App app = new App();
app.printIt();
how is Spring supposed to manage the instance and inject the value?
You will however need
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
to make the properties available. Also, because the App bean initialized for handling #Configuration is initialized before the resolver for #Value, the value field will not have been set. Instead, declare a different App bean and retrieve it
#Bean
public App appBean() {
return new App();
}
...
App app = (App) ctx1.getBean("appBean");
You need to access the property from a Spring bean, and you need to properly wire in the properties. First, add to your config class this:
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {
PropertySourcesPlaceholderConfigurer props = new PropertySourcesPlaceholderConfigurer();
props.setLocations(new Resource[] { new ClassPathResource("com/ektyn/springProperties/nejake.properties") }); //I think that's its absolute location, but you may need to play around with it to make sure
return props;
}
Then you need to access them from within a Spring Bean. Typically, your config file should not be a bean, so I would recommend you make a separate class, something like this:
#Component //this makes it a spring bean
public class PropertiesAccessor {
#Value("${klucik}")
private String klc;
public void printIt() {
System.out.println(klc);
}
}
Finally, add this to your config to make it find the PropertiesAccessor:
#ComponentScan("com.ektyn.springProperties")
Then you can access the PropertiesAccessor bean from your app context and call its printIt method.

Categories