0

I am developing a JAR file that would be running within a container only once per day and hence it doesnt require Spring boot features.

But still to leverage spring's feature, am having an application-config this way

@Configuration
@ComponentScan("com.arun.fp")
public class ApplicationConfig {

}

and then this is my main class and It is absolultey working fine

package com.arun.fp;

import com.arun.fp.utils.JobUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.time.Instant;


public class JobMain {

    

    private ApplicationContext applicationContext;

    public JobMain(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public static void main(String... args) {
        var applicationContext = new AnnotationConfigApplicationContext(ApplicationConfig.class);
        new JobMain(applicationContext).startJob();
    }

    public void startJob() {
        var jobUtils = applicationContext.getBean(JobUtils.class);
        var jobId = jobUtils.generateJobId();
        try {
            System.out.println(" -------------- ");

            var startTime = Instant.now();
            System.out.println("Job with ID: " + jobId + " Started at " + startTime);

           /* var fileProcessingJob = new FileProcessorJob();
            fileProcessingJob.startProcess();*/

            Thread.sleep(2000);

            var endTime = Instant.now();
            System.out.println("Job with ID: " + jobId + " Ended at " + endTime);
            var duration = jobUtils.getJobDurationInSeconds(startTime, endTime);
            System.out.println("Job with ID: " + jobId + " took " + duration + " seconds to complete the job");
        } catch (Exception exception) {
            
        }


    }


}

But instead of getting the applicationContext.getBean(..) and trying to use @Autowired annotation like this

@Autowired
private JobUtils jobUtils;

but it is not autowiring and am getting null only..

Can i use @Autowired only for Spring Boot app ? not for a java app that uses spring ??

Arun
  • 3,440
  • 11
  • 60
  • 108
  • And why wouldn[t you use Spring Boot for that? The fact that you don't need web or actuator doesn't men you wouldn't use Spring Boot it works perfectly well for command line applications as well. – M. Deinum Jun 25 '21 at 11:31
  • i wouldn't use spring boot because i dont want the app to be running always.. i want the jar file to run only when i invoke it – Arun Jun 25 '21 at 11:32
  • 2
    Please read. Spring BOot works perfectly well for command line apps (i.e start, do its thing and stop). Spring Boot isn't about microservices or keeping things always running. We did this for scheduled batch jobs. Have a jar containing everything and use an the Linux crontab to launch it periodically. So it starts, does the job, and stops again. As long as you don't include the web stuff things just start, execute and stop. – M. Deinum Jun 25 '21 at 11:35
  • You can use the Spring Framework Core, which provides Dependency Injection (DI). If you use Maven or Gradle, you can exclude other parts of the Spring Framework Core, that you don't wont, e.g. JUnit. https://spring.io/projects/spring-framework – Korashen Jun 25 '21 at 11:35

1 Answers1

2

Sure you can use @Autowired but as your JobMain isn't a a Spring bean it will ofcourse not be autowired. Basically that is duplicate of this (more reasons here.

So to fix make your JobMain an @Component and don't construct a new instance yourself.

import java.time.Instant;

@Component
public class JobMain {  

  @Autowired
  private JobUtils jobUtils;

  public static void main(String... args) {
    new AnnotationConfigApplicationContext(ApplicationConfig.class);        
  }

  @PostConstruct // actually you are better of using a listener for instance.
 public void startJob() {
   var jobId = jobUtils.generateJobId();
   try {
          System.out.println(" -------------- ");

            var startTime = Instant.now();
            System.out.println("Job with ID: " + jobId + " Started at " + startTime);

           /* var fileProcessingJob = new FileProcessorJob();
            fileProcessingJob.startProcess();*/

            Thread.sleep(2000);

            var endTime = Instant.now();
            System.out.println("Job with ID: " + jobId + " Ended at " + endTime);
            var duration = jobUtils.getJobDurationInSeconds(startTime, endTime);
            System.out.println("Job with ID: " + jobId + " took " + duration + " seconds to complete the job");
        } catch (Exception exception) {
            
        }
    }
}

However, as I mentioned in the comments already, there is nothing preventing you from using Spring Boot for CLI applications. Just don't include the web stuff and it will do what you want (start the job, and when done, will stop).

Assuming your JobMain is in the com.arun.fp you can do the following.

  1. Ditch your ApplicationConfig class (that isn't needed).
  2. Modify your JobMain for Spring Boot
  3. Use spring-boot-starter as a dependency
  4. Package the app
  5. run with java -jar your-job.jar (it will now start, do the job and finish).
@SpringBootApplication
public class JobMain {  

  public static void main(String... args) {
    SpringApplication.run(JobMain.class, args);      
  }

  @Bean
  public CommandLineRunner jobRunner(JobUtils jobUtils) {

    return (args) -> {
        var jobId = jobUtils.generateJobId();
        try {
            System.out.println(" -------------- ");

            var startTime = Instant.now();
            System.out.println("Job with ID: " + jobId + " Started at " + startTime);

           /* var fileProcessingJob = new FileProcessorJob();
            fileProcessingJob.startProcess();*/

            Thread.sleep(2000);

            var endTime = Instant.now();
            System.out.println("Job with ID: " + jobId + " Ended at " + endTime);
            var duration = jobUtils.getJobDurationInSeconds(startTime, endTime);
            System.out.println("Job with ID: " + jobId + " took " + duration + " seconds to complete the job");
        } catch (Exception exception) {
            
        }
    }
}

This will start the job, due to the CommandLineRunner will execute after all the beans have started. When the CommandLineRunner finishes it work, the application will stop as there are no more processes keeping things alive.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • I can't thank you more !! Take a bow sir !! – Arun Jun 25 '21 at 12:17
  • Additionally i had to give this property in `application.properties` to shutdown after finishing the job `spring.main.web-application-type=none`. Otherwise it keeps on running and not shutting down – Arun Jun 25 '21 at 12:50
  • That only happens if you included `spring-boot-starter-web` which you shouldn't include in the first place. If you only add `spring-boot-starter` as I mentioned it will just stop. – M. Deinum Jun 25 '21 at 16:07
  • I have this depdency `implementation 'org.springframework.boot:spring-boot-starter-webflux'` basically to make use of `WebClient` and do REST Calls – Arun Jun 25 '21 at 16:28
  • Then you indeed need this (or exclude the Netty runtime) else it will indeed start the server as it will detect Netty. – M. Deinum Jun 25 '21 at 17:33