Spring doesn't inject beans to bean with #Scheduled method - java

Im using Spring #Scheduled annotation for the first time. I've enabled scheduling in configuration, and schedule method works perfectly.
My problem is, that Spring doesn't inject required beans into my bean with #Scheduled annotated method. What I actually get is an LockService proxy object, which is useless in this case.
Is there any way to inject Spring beans into a bean with scheduled task?
I've tried with #Lazy annotation under #Autowired annotation, but result was the same. Getting bean from injected ApplicationContext has no result also.
Here is my bean with not injected dependencies:
#Component
public class LockScheduler extends AbstractScheduler {
private final LockService lockService;
#Autowired
public LockScheduler(LockService lockService) {
this.lockService = lockService;
}
#Scheduled(fixedDelay = 15000)
public void removeAbandonedLocks() {
if (!isSchedulingEnabled()) {
return;
}
LOG.info("Execute abandoned lock remove");
lockService.removeAbandonedLocks();
}
}
I guess it is something with Aspects, that I cant reach ApplicationContext from bean with scheduled tasks.
UPDATE:
I've posted screen from debug mode. Instead of LockService object I get proxy with null reference to LockRepository, therefore breakpointed method execute has no effect.

Related

Is there any way to create a #ConditionalOnMissingBean but only after spring is fully configured?

I'm building a Spring Boot Starter for a college project on Java Reflection and Bytecode alteration.
The Reflection/Bytecode is done now, but it will scan for Spring #Controllers/#RestControllers so it can detect certain annotations to run the process.
My question here is what's the best approach? Seems to me that an annotation processor doesn't quite work nicely, and my idea is to create a #Configuration class. Now I need to ensure that all #Controller beans have been booted before I actually process them and I also need to put the result of this processing in a bean that could already exist.
So for example:
#Configuration
public class TestConfig {
#Autowired //I want to autowire but it may not exist, if the user doesn't define I need to create it
private ExternalAnnotatedRequestsModel model;
#Autowired // needed for the framework to acess spring controllers
private ConfigurableApplicationContext ctx;
#Bean // this can also be overriden since the definitions can be done via yaml
public ExternalRequestsProvider() {
return new AnnotationExternalRequestsProvider(ctx);
}
}
Now I also want that when the ExternalRequestsProvider bean is started, it runs the process method and saves the result in the object in the "model" variable.
Using #EventListener for ApplicationReadyEvent to run your process after Spring is fully configured.
#Configuration
public class ExternalRequestsConfig {
#Autowired
private ExternalAnnotatedRequestsModel model;
#Autowired
private ExternalRequestsProvider provider;
#EventListener(ApplicationReadyEvent.class)
public void onApplicationReady(ApplicationReadyEvent event) {
// do your process
}
}

Spring why #MockBean can't autowire an interface using profile

I've an interface with two implementations. Which implementaton is to be used depends of the environment (production, development, test, ...). I therefore use Spring profiles. I'm using a configuration file to instantiate the correct implementation.
#Configuration
public class BeanConfiguration {
#Profile({"develop","test-unit"})
#Bean(name = "customerEmailSender")
public CustomerEmailSender emailSenderImpl_1(){
return new EmailSenderImpl_1();
}
#Profile({"prod"})
#Bean(name = "customerEmailSender")
public CustomerEmailSender emailSenderImpl_2(){
return new EmailSenderImpl_2();
}
}
When the Spring container starts (with a specific profile), the correct bean is autowired into the class, and all works fine.
#Component
public class CustomerEmailProcessor {
#Autowire
private CustomerEmailSender customerEmailSender;
...
}
I also have a test class in which I want to autowire the bean. I'm using #Mock for autowiring.
The profile is set to "test-unit" in the test class. So, I'm expecting the spring container to lookup in the config class for the correct bean to be instantiated. But this doesn't happen.
Instead, an Exception is thrown :
Caused by: java.lang.IllegalStateException: Unable to register mock bean .... expected a single matching bean to replace but found [customerEmailSender, emailSenderImpl_1, emailSenderImpl_2]
When using #Autowire annotation, all goes fine. But of course the bean is not mocked anymore and that's what I need to have.
#RunWith(SpringRunner.class)
#ActiveProfiles(profiles = {"test-unit"})
#Import(BeanConfiguration.class)
public class CustomerEmailResourceTest {
#MockBean
private CustomerEmailSender customerEmailSender;
}
I've put a breakpoint in the config class, and I can see that when using #Autowire in the test class, the correct bean is instantiated (breaks at the line "return new EmailSenderImpl_1();".
When using #Mock, no bean at all is instantiated. Spring doesn't break at the line "return new EmailSenderImpl_1();"
Why is it that Spring can find the correct bean when using the #Mock annotation.
The #Mock annotation must be the reason that Spring doesn't use the config class "BeanConfiguration.java". That makes sence after all.

Spring #ConditionalOnBean in combination with #Component

Having the next situation:
#ConditionalOnBean(ServiceD.class)
#Component
class ServiceAImpl implements ServiceA {
private ServiceB serviceB;
private ServiceC serviceC;
private ServiceD serviceD;
public ServiceA(ServiceB serviceB, ServiceC serviceC, ServiceD serviceD) {
this.serviceB = serviceB;
this.serviceC = serviceC;
this.serviceD = serviceD;
}
}
interface ServiceD {
void doStuff();
}
#Component
class ServiceDImpl implements ServiceD {
private ServiceE serviceE;
public ServiceD(ServiceE serviceE) {
this.serviceE = serviceE;
}
#Override
public void doStuff() {
//...
}
}
I need serviceA to be loaded only if there exists a serviceD implementation.
From #ConditionalOnBean documentation: "The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only.".
My question is:
As ServiceD is a dependency of ServiceA, will this guarantee that when #ConditionalOnBean is evaluated, ServiceD will be already processed by the application context?
If not, is there any other way to acomplished that without using autoconfiguration classes?
This is a known issue. There is no guarantee that ServiceD bean would be created before ServiceA, as you cannot change the order of bean registration from #ComponentScan. It's why the documentation says to use the condition on auto-configuration classes only. So you either have to make ServiceA auto-configured, so that all your other defined beans are defined before ServiceA bean, or use a different annotation such as #ConditionalOnClass.
By default Spring manages beans' lifecycle and arranges their initialization order.
But, we can still customize it based on our needs. We can choose either the SmartLifeCycle interface or the #DependsOn annotation for managing initialization order.
You can try #DependsOn By using this annotation for specifying bean dependencies. Spring guarantees that the defined beans will be initialized before attempting an initialization of the current bean. Please refer the documentation

Spring 4.3.1 scheduled task run twice

I have a problem while scheduling a task into an application scoped bean using spring 4.3.1. My code is something like this one:
#Component
#ApplicationScope
public class MyClassImpl implements MyClass {
#Scheduled(fixedDelayString = "60000")
#Transactional
public void method() {
do something...
}
}
The scheduled method is runned twice. I debugged application context creation and it seems that the bean is loaded only one time as expected.
Do somebody has some suggestions?
I added a BeanPostProcessor and I noticed that spring framework instantiate 2 beans one named MyClassImpl and one named scopedTarget.MyClassImpl any idea on how to avoid this behaviour?
Solved creating a new class not implementing any interface (#Component annotated) containing the #Scheduled method.

IllegalStateException thrown by Spring during first Quartz job execution

During the first execution of the first job in Quartz scheduler, the following exception is thrown by Spring. Note that the job makes an explicit call to applicationContext.getBean(...) in its execution.
Can someone explain the cause of this exception, and, maybe, the way to avoid it ?
Spring version : 4.1.5.RELEASE
Quartz version : 2.1.6
Thanks in advance
2015-07-24 09:20:27,416 ERROR be.citobi.mediquality.schedulers.A4MCubeJob - a4MCubeJob in error
java.lang.IllegalStateException: About-to-be-created singleton instance implicitly appeared through the creation of the factory bean that its bean definition points to
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:374)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1111)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1006)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getSingletonFactoryBeanForTypeCheck(AbstractAutowireCapableBeanFactory.java:860)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:790)
at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:542)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:436)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:412)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:398)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:337)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:331)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:968)
at be.citobi.mediquality.schedulers.A4MCubeJob.execute(A4MCubeJob.java:26)
at org.quartz.core.JobRunShell.run(JobRunShell.java:213)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:557)
I'm not sure if my solution is correct, but here is the way how I'm autowiring spring beans to my jobs. SchedulerFactoryBean creation:
//Note: MySpringBean will be automatically autowired here.
//Another possible approach is to use #Autowired and inject necessary bean one level higher
#Bean
public SchedulerFactoryBean scheduler(MySpringBean mySpringBean) {
SchedulerFactoryBean sfb = new SchedulerFactoryBean();
//default configuration goes here
Map<String, Object> schedulerContext = new HashMap<>();
schedulerContext.put("mySpringBean", mySpringBean);
schedulerContext.setSchedulerContextAsMap(schedulerContext);
return sfb;
}
And here is my Job code which suppose to use this spring bean:
public class MyJob extends QuartzJobBean {
private MySpringBean mySpringBean;
public void setMySpringBean(MySpringBean mySpringBean) {
this.mySpringBean = mySpringBean;
}
#Override
public void executeInternal(JobExecutionContext jobContext) throws JobsExecutionException {
//Job logic with usage of mySpringBean
}
}
In my application Quartz jobs didn't require the whole application context - just 3 beans, so I decided to autowire certain beans instead of autowiring whole context
Hope this helps
I find out what the problem was, in Spring config, several beans implementing FactoryBean were declared without using generics. Adding the generics solved the issue.
This was most likely non related to Quartz.

Categories