First time looking at Spring - and I have a question regarding configuration of a bean that I also would like to schedule.
Using Spring Boot, I've created a Application class, and a TaskClass that I would both like to Schedule and configure using a configuration class.
Application Class
#SpringBootApplication
#EnableScheduling
#ComponentScan("mmmi.pdws.cetrea")
public class Application {
public static void main(String[] args) {
SpringApplication.run(PdwsBackend.class, args); //Leftover from Boot project
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
taskClass task = ctx.getBean(taskClass.class);
}
}
TaskClass
#Component
public class TaskClass {
private String taskName;
#Scheduled(fixedRate=1000)
public void lifeSign(){
System.out.println("My name is " + taskName);
}
public void setName(String name){this.name = name}
}
AppConfig class
#Configuration
public class AppConfig {
#Bean
public TaskClass taskClass(){
TaskClass task = new TaskClass();
task.setName("SpringTask");
return task;
}
}
The instantiated object from the Scheduler and from the AppConfig class are naturally not the same...but is there a way to configure the Scheduled object?
Looking at Springs scheduling reference it seems to be doable with XML configuration? But what if I would like to do it with a Java configuration class?
If the value that you want to set is constant, you can put it in your application.properties file and inject it to using the #Value annotation directly into the scheduler bean.
application.properties
task.name=SpringTask
TaskClass
#Component
public class TaskClass {
private final String taskName;
public TaskClass(#Value("${task.name}") String taskName) {
this.taskName = taskName;
}
#Scheduled(fixedRate=1000)
public void lifeSign(){
System.out.println("My name is " + taskName);
}
public void setName(String name){this.name = name}
}
You can also inject the value directly to the field if you don't like constructor injection.
Of course the bean declared in the AppConfig is redundant since the TaskClass is annotated with #Component and will be found by #ComponentScan.
From the same spring ref link, you can read the following:
Make sure that you do not use #Configurable on bean classes which are annotated with #Scheduled and registered as regular Spring beans with the container: You would get double initialization otherwise, once through the container and once through the #Configurable aspect.
What you can do is simply use #PostConstruct method (called only once after bean creation) in your TaskClass to set taskName as shown below and then you don't need your AppConfig class:
#Component
public class TaskClass {
private String taskName;
#Postconstruct
public void init() {
taskName= "SpringTask";
}
#Scheduled(fixedRate=1000)
public void lifeSign(){
System.out.println("My name is " + taskName);
}
public void setName(String name){this.name = name}
}
Related
Since according to the docs #Component registers beans for the Spring container I'm trying to create a simple example of dependency injection using the following code:
package pl.playground;
//...
#SpringBootApplication
public class PlaygroundApplication {
#Autowired
private static Building building;
public static void main(String[] args) {
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
package pl.playground.facade;
//...
#Component
public class Building {
private HeatingService service;
private Long buildingSize;
#Autowired
public Building(HeatingService service) {
this.service = service;
}
public Double monthlyHeatingCost() {
return service.getMonthlyHeatingCost(buildingSize);
}
// getters & setters...
}
package pl.playground.service;
public interface HeatingService {
Double getMonthlyHeatingCost(Long size);
}
package pl.playground.service;
//...
#Component
public class HeatingServiceImpl implements HeatingService {
private final Double CUBIC_PRICE = 2.3;
public HeatingServiceImpl() {}
#Override
public Double getMonthlyHeatingCost(Long size) {
return size * CUBIC_PRICE;
}
}
It builds and runs, but there is a NullPointerException at building.setBuildingSize(12L);. However the one below works without any issues:
//PlaygroundApplication.java
package pl.playground;
//...
#SpringBootApplication
public class PlaygroundApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Building building = context.getBean(Building.class);
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
package pl.playground.config;
//...
#Configuration
public class Config {
#Bean
public Building building(HeatingService service) {
return new Building(service);
}
#Bean
public HeatingServiceImpl heatingServiceImpl() {
return new HeatingServiceImpl();
}
}
The rest is the same as before.
Why is #Component not creating Beans?
It is working the way I think it should when used inside a #Controller of a web app, does that make a difference? How does exactly #Bean and #Component differ?
What am I failing to understand?
EDIT
Consider the following scenario:
package pl.playground;
//...
#SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
package pl.playground.controller;
//...
#Controller
public class Controller {
private Facade facade;
#Autowired
public Controller(Facade facade) {
this.facade = facade;
}
#GetMapping("/")
public String getIndexPage(Model model) {
return "index";
}
}
package pl.playground.facade;
//...
#Component
public class Facade {
private PostsService postService;
private UserService userService;
private TagService tagService;
#Autowired
public Facade(PostsService retrieve, UserService user, TagService tag) {
this.postService = retrieve;
this.userService = user;
this.tagService = tag;
}
//...
}
I don't need #Configuration here for it to work. That's my concern.
The problem with your code is that you are trying to #Autowire on a static field. You simply cannot do that. Look here: Can you use #Autowired with static fields?
It fails to work because the PlaygroundApplication class is not being created and managed by spring. The injection works only inside instances managed by spring. You can treat class annotated with #SpringBootApplication as configuration classes. Spring creates instances of those classes and injection works inside them but only on instance fields.
The second example shows the correct way to access spring beans from main method of the application.
Well. I used your original question and is working without any issues. #cezary-butler pointed out in the comments you can autowire into PlaygroundApplication but you can get hold of it easily in the static main method using context.getBean(Building.class)
#SpringBootApplication
public class PlaygroundApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(PlaygroundApplication.class);
Building building = context.getBean(Building.class);
building.setBuildingSize(12L);
System.out.println(building.monthlyHeatingCost());
}
}
Here is the sample repo https://github.com/kavi-kanap/stackoverflow-63072236
TLDR;
A Spring context needs to be created before any bean can be injected. In the first scenario, just the fact of having a #SpringBootApplication decorator does not ensure a context in the scope of the class it decorates.
SpringApplication.run(ExampleApplication.class, args); instantiates a context (and e.g. a web server among other things)
var context = new AnnotationConfigApplicationContext(Config.class); instantiates a scoped context
Thus the first example had null inside of Building as there was no context with the bean to inject.
Are there ways to override properties of DefaultListableBeanFactory in Spring Boot application?
For example, I want to set the DefaultListableBeanFactory.allowBeanDefinitionOverriding property to false.
EDIT
Use case.
I have pretty plain class:
#Data
#AllArgsConstructor
class MyTempComponent {
private String value;
}
Which I want use as #Bean in my application but this bean can be defined several times, for example:
#Configuration
class TestConfiguration1 {
#Bean
MyTempComponent myTempComponent() {
return new MyTempComponent("Value 1");
}
}
#Configuration
class TestConfiguration2 {
#Bean
MyTempComponent myTempComponent() {
return new MyTempComponent("Value 2");
}
}
Also there is a consumer of that bean:
#Component
class TestConfiguration3 {
private MyTempComponent myTempComponent;
#Autowired
public TestConfiguration3(MyTempComponent myTempComponent) {
this.myTempComponent = myTempComponent;
}
#PostConstruct
public void print() {
System.out.println(this.myTempComponent.getValue());
}
}
When an application starts it prints "Value 2" message, i.e. initializes myTempComponent bean from TestConfiguration2.
I want to prohibit creation of that bean (and any other beans) if there are two or more candidates.
As I realized I can reach this goal via setting DefaultListableBeanFactory.allowBeanDefinitionOverriding to false.
But how to set this property in Spring Boot application? Could you provide an example please?
You can set
private static class CustomAppCtxInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
#Override
public void initialize(GenericApplicationContext applicationContext) {
applicationContext
.getDefaultListableBeanFactory()
.setAllowBeanDefinitionOverriding(false);
}
}
and then have
public static void main(String[] args) {
try {
final SpringApplication springApplication = new SpringApplication(Application.class);
springApplication.addInitializers(new CustomAppCtxInitializer());
I need to add a dynamically created bean when my 'normal' bean gets created. I tried this so far:
//generate a health bean dynamically, and register it
#PostConstruct
public void init(){
solrhealth = new SolrHealthIndicator(solr);
//context.??
}
I build a SolrHealthIndicatior bean programatically, as I am not using Spring Solr Data. Now I want it registered so it shows up in /health.
I have my context wired, but cannot find how to register the newly created bean in there...
You should be able to programatically define your bean by using the #Bean annotation within a #Configuration class.
#Bean
public SolrHealthIndicator solrHealthIndicatior() {
//you can construct the object however you want
return new SolrHealthIndicator();
}
Then you can just inject it like any other bean(#Autowired constructor, field, setter injection, etc.), if there are multiple beans with the same type you can use #Qualifier to distinguish between them.
You need to use #Lookup annotation.
#Component
public class SolrHealthIndicator {
public SolrHealthIndicator(Solr solr) {
}
}
public class BeanInQuestion {
#PostConstruct
public void init() {
solrHealthIndicator = getHealthIndicatorBean();
}
#Lookup
public SolrHealthIndicator getHealthIndicatorBean() {
//Spring creates a runtime implementation for this method
return null;
}
}
You could make the class containing your #PostConstruct implement BeanDefinitionRegistryPostProcessor. Then you'd then be able to register your beans programmatically:
#Bean
public class MyBean implements BeanDefinitionRegistryPostProcessor {
private BeanDefinitionRegistry registry;
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.registry = registry;
}
#PostConstruct
public void init(){
registry.registerBeanDefinition("solrHealthIndicator", new SolrHealthIndicator(solr));
}
}
In my spring boot application, i'am trying to inject variable's value from the config file application.properties to my java class and i'm getting a null value.
here is the configuration of my application.properties file:
myapp.username=user#user.com
myapp.password=user
here is where i call the configuration entries:
#Component
public class MyClass{
#Value("${myapp.username}")
public String username;
#Value("${myapp.password}")
public String password;
public static void main(String[] args) {
System.out.println(password);
}
}
I hope there someone how did deal with the same problem, thanks.
you can use this example add bean to your config like this :
#Configuration
#ComponentScan(basePackages = "youpackagebase")
#PropertySource(value = { "classpath:application.properties" })
public class AppConfig {
/*
* PropertySourcesPlaceHolderConfigurer Bean only required for #Value("{}") annotations.
* Remove this bean if you are not using #Value annotations for injecting properties.
*/
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
and in your bean :
#Component
public class NetClient {
#Value("${bigwater.api_config.url.login}")
public String url_login;
Best Regards
You are not even letting the Spring Boot container to boot (initialize) as you are are writing the code directly under main.
You should have an Application class as shown below to launch the Spring boot container properly, look here.
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
As far as my understanding you wanted to execute your code once the container is started, so follow the below steps:
Add the above Application class and then in your NetClient component class add a #Postconstruct method & this method will be called automatically once the bean is ready, refer the code below:
#Component
public class NetClient {
#Value("${bigwater.api_config.url.login}")
public String url_login;
#Value("${bigwater.api_config.url.ws}")
public static String url_ws;
#Value("${bigwater.api_config.username}")
public String username;
#Value("${bigwater.api_config.password}")
public String password;
#Postconstruct
public void init() {
//place all of your main(String[] args) method code here
}
//Add authentification() method here
}
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.
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();
}
}
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
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?
You need a few steps to get your standalone app working:
A class with main() method.
A #SpringBootApplication annotation to your main class.
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
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 ??