3

I'm trying to figure out how to build a Spring Boot standalone app. Of course to have things autowired requires some initial context starting point. If I just try to Autowire a class to run a job it is null even if I make it static.

Is there a way to use Spring @Services in a standalone non-web app?

@SpringBootApplication
public class MyApplication {

    @Autowired
    private static JobRunnerService job;

    public static void main(String[] args) {
         SpringApplication.run(MyApplication.class, args);

         job.send();   //job is null !

    }
}

So first wired in a static JobRunnerService to the main running MyApplication the JobRunner(Service) Class has a non-static SshSessionService wired into it. the SshSession(Service) finally just has a no-arg constructor.

@Service("jobRunnerService")
public final class JobRunner implements JobRunnerService{


    @Autowired
    private SshSessionService ssh;      

    @Autowired
     public JobRunner(SshSessionService ssh){
        this.ssh = ssh;

     }

     public void sendToAgent() { ....
}

@Service("sshSessionService")
public class SshSession implements SshSessionService {

    public SshSession() {

    }
}

It starts off being null at the JobRunnerService job reference.

JohnnyO
  • 527
  • 9
  • 21
  • Autowiring on static fields doesn't work. If you want to run a method on startup, create a bean which implements the interface CommandLineRunner or ApplicationRunner. Beans which implement these interfaces are automatically executed by Spring Boot as soon as the application is ready. – dunni Mar 17 '16 at 21:42
  • Dunni is right, @JohnnyO – Ralph Rimorin Mar 18 '16 at 11:30

5 Answers5

4

Several different solutions comes to mind:

If you take a look at the SpringApplication.run() method you will notice that it returns a ApplicationContext. From that, you can fetch the JobRunnerService, e.g.

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
        JobRunnerService job = ctx.getBean(JobRunnerService.class);
        job.send();
    }
}

Another solution is to use @PostConstruct annotation for the send() method:

@Service("jobRunnerService")
public class JobRunner implements JobRunnerService {

    @PostConstruct
    public void send() { ... }
}

However in your case, I would implement the ApplicationRunner interface, either as a separate bean which autowires the JobRunnerService and then calls its send() method

@Component
public class SendRunner implements ApplicationRunner {

    @Autowired
    private JobRunnerService job;

    @Override
    public void run(ApplicationArguments args) {
        job.send();
    }
}

or let the JobRunner implement the ApplicationRunner interface directly:

@Service("jobRunnerService")
public class JobRunner implements JobRunnerService, ApplicationRunner {

    @Override
    public void send() { ... }

    @Override
    public void run(ApplicationArguments args) {
        send();
    }
}
matsev
  • 32,104
  • 16
  • 121
  • 156
0

You haven't provided the code for JobRunnerService but I am assuming it has a default constructor and that it is annotated by @Component for Spring to figure it out as a bean before you can actually autowire it. your job is null probably because it's not able to find an autowired bean for JobRunnerService and that's probably because you don't have an identifier for Spring to scan and create bean of type JobRunnerService

shahshi15
  • 2,772
  • 2
  • 20
  • 24
0

You can use @Servicesor @Component to the JobRunnerService class then add annotation @ComponentScan("package of JobRunnerService") below @SpringBootApplication, see this link:

How to scan multiple paths using the @ComponentScan annotation?

Community
  • 1
  • 1
Ralph Rimorin
  • 329
  • 2
  • 10
0

You need a few steps to get your standalone app working:

  1. A class with main() method.
  2. A @SpringBootApplication annotation to your main class.
  3. And a call to the SpringApplication.run() method.

package com.example.myproject;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan
public class Application {

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

}

As noted, the @SpringBootApplication is a composite annotation which consist of @Configuration @EnableAutoConfiguration and @ComponentScan. In other words, it can be replaced by the three latter annotations. Alternatively, you can use the alias scanBasePackage or scanBasePackageClasses to customize which directories that should be used for component scanning.

The example is copied from the @SpringBootApplication paragraph in the Spring Boot reference docs (see link above). If you would like to quick start your project, complete with build scripts (Maven or Gradle), dependencies, etc, you can generate a project skeleton using the Spring Initializr

matsev
  • 32,104
  • 16
  • 121
  • 156
  • I have this already but what could go below the run call in the main method to refer to the next call in my case job.send() if you look at my code above you'll see it's a problem with initializing beans which are @Services... – JohnnyO Mar 17 '16 at 20:22
0

I'm trying to run as Thread/runnable now as mentioned in the Spring document 3. Task Execution and Scheduling..

import org.springframework.core.task.TaskExecutor;

public class TaskExecutorExample {

private class MessagePrinterTask implements Runnable {

    private String message;

    public MessagePrinterTask(String message) {
        this.message = message;
    }

    public void run() {
        System.out.println(message);
    }

}

private TaskExecutor taskExecutor;

public TaskExecutorExample(TaskExecutor taskExecutor) {
    this.taskExecutor = taskExecutor;
}

public void printMessages() {
    for(int i = 0; i < 25; i++) {
        taskExecutor.execute(new MessagePrinterTask("Message" + i));
    }
}

}

So in my case I'm trying...

@Service("jobRunnerService")
@Component
public class JobRunner implements JobRunnerService, ApplicationRunner{

@Autowired
public TaskExecutor taskExecutor;

@Autowired
private SshSessionService ssh;

        private class JobTask implements Runnable{

            public void run(){

                Boolean success = connectToAgent();

                if(success){
                    log.debug("CONNECTED!!!");
                }

            }

        }



/**
 * Construct JobRunner with TaskExecutor
 * @param taskExecutor
 */
@Autowired
public JobRunner(TaskExecutor taskExecutor, SshSessionService ssh) {
    this.taskExecutor = taskExecutor;
    this.ssh = ssh;
}

private Map<String, String> sessionParams; 



private final Logger log = LoggerFactory.getLogger(this.getClass());


@Override
public void run(ApplicationArguments args) {

    /**
     * Starting point of application
     * 
     */
    taskExecutor.execute(new JobTask());
}

just getting org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.core.task.TaskExecutor] found for dependency

How can i get the imported lib to be accepted as a TaskExecutor Bean ??

JohnnyO
  • 527
  • 9
  • 21