I am trying to build a DiscoveryClient and I want it to fire an event when there is a change to the routes. I am using
publisher.publishEvent(new InstanceRegisteredEvent<>(this, "serviceName"));
However, the event does not actually fire even if it is the same object. I am suspecting it is because it is a different thread, but #Scheduled also run from a different thread and it fires successfuly.
The circumstance that I hit was the fact that I was using the ApplicationEventPublisher that was provided during the BootstrapAutoConfiguration phase in the application. Because I was using that, the events I publish do not get propagated as expected.
To get around that I had to make sure to change the ApplicationEventPublisher that was have put in during bootstrap with something after by adding in another AutoConfiguration executed during the AutoConfiguration phase and not in Bootstrap phase.
I added (but it is optional) ApplicationEventPublisherAware to the class in my case DockerSwarmDiscovery
#Configuration
#ConditionalOnBean(DockerSwarmDiscovery.class)
#Slf4j
public class DockerSwarmDiscoveryWatchAutoConfiguration {
#Autowired
private DockerSwarmDiscovery dockerSwarmDiscovery;
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
#PostConstruct
public void injectPublisher() {
dockerSwarmDiscovery.setApplicationEventPublisher(applicationEventPublisher);
}
}
Related
Maybe I have some outdated knowledge but it is the same as described here
https://stackoverflow.com/a/2657465/2674303
But now I noticed that this example works without any exceptions:
#Service
#EnableScheduling
public final class MyService {
#PostConstruct
public void init(){
System.out.println("MyService started");
}
#Scheduled(fixedDelay= 1000)
public void scheduleCall() {
System.out.println("scheduleCall");
}
}
Could you pease provide how does it work ?
#Scheduled annotation does not require proxy creation. The mechanism is different. After bean initialization Spring called post-processor ScheduledAnnotationBeanPostProcessor. Post processor searches for all methods annotated with #Scheduled and registers them to TaskScheduller for execution. Method execution will be performed via reflection.
See ScheduledAnnotationBeanPostProcessor source code.
#Scheduled
Processing of #Scheduled annotations is performed by registering a
ScheduledAnnotationBeanPostProcessor. This can be done manually or,
more conveniently, through the task:annotation-driven/ XML element
or #EnableScheduling annotation.
ScheduledAnnotationBeanPostProcessor
Bean post-processor that registers methods annotated with #Scheduled
to be invoked by a TaskScheduler according to the "fixedRate",
"fixedDelay", or "cron" expression provided via the annotation. This
post-processor is automatically registered by Spring's
task:annotation-driven XML element, and also by the
#EnableScheduling annotation.
Autodetects any SchedulingConfigurer instances in the container,
allowing for customization of the scheduler to be used or for
fine-grained control over task registration (e.g. registration of
Trigger tasks). See the #EnableScheduling javadocs for complete usage
details.
#PostConstruct also implemented via post-processor InitDestroyAnnotationBeanPostProcessor when dependency injection performed for bean, method which marked #PostConstruct will be executed thru reflection without proxy.
See InitDestroyAnnotationBeanPostProcessor source code
Summary:
In your example, Spring will create bean without proxy.
In case you will add a proxy-specific annotation, for example, #Transactional you will get an exception that proxy can not be created due to final class java.lang.IllegalArgumentException: Cannot subclass final class com.test.services.MyService
#Service
#EnableScheduling
public final class MyService {
#PostConstruct
public void init(){
System.out.println("MyService started");
}
#Scheduled(fixedDelay= 1000)
#Transactional
public void scheduleCall() {
System.out.println("scheduleCall");
}
}
But the current problem you also can solve to force use JDK dynamic proxy. We need to create an interface for class and set property spring.aop.proxy-target-class = false according to Proxying mechanisms
I have a rest service that is supposed to call a function using #Scheduled. The other functions in MyService work as intended locally and in live, just not the scheduled function. Any ideas on what I am missing here that would cause it to fail only in live?
#Configuration
#EnableScheduling
#EnableAsync
#ComponentScan(basePackages = "elsewhere.yyy.yyy")
public class ApplicationConfiguration {
}
#RestController
public class MyController {
#Autowired
MyService service;
}
#Service
public class MyService {
#Async
#Scheduled(fixedRate = 30000, initialDelay = 10000)
public void myTask() {
// Do stuff...
}
}
I turned on debug logging, but am seeing no org.springframework.scheduling.* logs, my theory should be that spring just isn't seeing #Scheduled for some reason and is thus not running it, but it is working perfectly fine locally! My other theory is that #Async might be causing an unexpected interaction, but it doesn't make sense to me that it would cause the function to disappear in live only... The good(?) news is that is consistently not working in live and consistently working locally.
I'm using Spring Boot version 2.5.1
The problem was caused by an ID-10T error. //Do Stuff... early on looks for a file that may or may not be there, and short circuits as intended when the file isn't there. The file was provided and the silent short circuit in a live environment made it appear as if the scheduled task was simply never running. The issue was that I provided the file to the incorrect subdirectory, putting said file into the correct directory solved the issue.
I have defined the following Spring boot application:
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run(args);
}
public void run(String... args) throws Exception {
Thread.currentThread().join();
}
}
I also have a package of workers (i.e. could be classes which implements Runnable). They are supposed to run indefinitely.
What is the "Spring way" to run them? (and doing so automatically, without explicitly knowing them)
Thanks
and doing so automatically, without explicitly knowing them
There's no mechanism to automatically run some Runnables from a certain place. You need to find a way to inform Spring about these classes.
Three common scenarios:
Execute some code during the application startup: ApplicationRunner and CommandLineRunner.
You either congregate Runnables and wrap them up into an [Application|CommandLine]Runner which should be a bean (e.g. #Bean, #Component, etc) or make each Runnable a separate [Application|CommandLine]Runner.
Execute some code at some point of time: TaskExecutor.
You inject a TaskExecutor and give it previously gathered Runnables.
Execute some code repeatedly: TaskScheduler.
You inject a TaskScheduler and give it previously gathered Runnables, plus a trigger.
More details: Task Execution and Scheduling
You could (1) have your classes that implement Runnable be annotated with #Component, so that Spring can find them. You can then (2) write a WorkManager, annotated with #Service, with an #Autowired List - which Spring would initialize with a list instances of all your classes from (1). And could (3) write a #PostConstruct method in your WorkManager that would iterate over that list of Runnables, and pass each one to a TaskExecutor to run...
It is my understanding that when you use Spring Cloud's RefreshScope annotation, a Proxy to the data is injected, and the proxy is automatically updated if the backing information is changed. Unfortunately, I need to find a way to be alerted when that refresh occurs, so that my code can re-read the data from the refresh-scoped bean.
Simple example: A scheduled task whose schedule is stored in Cloud Config. Unless you wait until the next execution of the task (which could take a while) or regularly poll the configuration (which seems wasteful), there's no way to know if the configuration has changed.
EnvironmentChangeEvent is fired when there's a change in Environment. In terms of Spring Cloud Config it means it's triggered when /env actuator endpoint is called.
RefreshScopeRefreshedEvent is fired when refresh of #RefreshScope beans has been initiated, e.g. /refresh actuator endpoint is called.
That means that you need to register ApplicationListener<RefreshScopeRefreshedEvent> like that:
#Configuration
public class AppConfig {
#EventListener(RefreshScopeRefreshedEvent.class)
public void onRefresh(RefreshScopeRefreshedEvent event) {
// Your code goes here...
}
}
When the refresh occurs EnvironmentChangeEvent would be raised in your config client, as the documentation states:
The application will listen for an EnvironmentChangedEvent and react
to the change in a couple of standard ways (additional
ApplicationListeners can be added as #Beans by the user in the normal
way).
So, you can define your event listener for this event:
public class YourEventListener implements ApplicationListener<EnvironmentChangeEvent> {
#Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
// do stuff
}
}
I think an approach can be to annotate with #RefreshScope all your bean that have properties externalized by the configuration and annotated within #Value ( "${your.prop.key}" ) annotation.
These properties are updated when they changed on configuration.
More specifically, after the refresh of properties and application context under scope RefreshScope, an event RefreshScopeRefreshedEvent is triggered. You can have a listener for this given the understanding that the properties has finished updates (you can be sure to capture updated values only).
related to the commit in spring framework https://github.com/spring-projects/spring-framework/commit/5aefcc802ef05abc51bbfbeb4a78b3032ff9eee3
the initialisation is set to a later stage from afterPropertiesSet() to afterSingletonsInstantiated()
In short:
This prevents the caching to work when using it in a #PostConstruct use case.
Longer version:
This prevents the use case where you would
create serviceB with #Cacheable on a methodB
create serviceA with #PostConstruct calling serviceB.methodB
#Component
public class ServiceA{
#Autowired
private ServiceB serviceB;
#PostConstruct
public void init() {
List<String> list = serviceB.loadSomething();
}
This results in org.springframework.cache.interceptor.CacheAspectSupport not being initialised now and thus not caching the result.
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
// check whether aspect is enabled
// to cope with cases where the AJ is pulled in automatically
if (this.initialized) {
//>>>>>>>>>>>> NOT Being called
Class<?> targetClass = getTargetClass(target);
Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass);
if (!CollectionUtils.isEmpty(operations)) {
return execute(invoker, new CacheOperationContexts(operations, method, args, target, targetClass));
}
}
//>>>>>>>>>>>> Being called
return invoker.invoke();
}
My workaround is to manually call the initialisation method:
#Configuration
public class SomeConfigClass{
#Inject
private CacheInterceptor cacheInterceptor;
#PostConstruct
public void init() {
cacheInterceptor.afterSingletonsInstantiated();
}
This of course fixes my issue but does it have side effects other that just being called 2 times (1 manual and 1 by the framework as intended)
My question is:
"Is this a safe workaround to do as the initial commiter seemed to have an issue with just using the afterPropertiesSet()"
As Marten said already, you are not supposed to use any of those services in the PostConstruct phase because you have no guarantee that the proxy interceptor has fully started at this point.
Your best shot at pre-loading your cache is to listen for ContextRefreshedEvent (more support coming in 4.2) and do the work there. That being said, I understand that it may not be clear that such usage is forbidden so I've created SPR-12700 to improve the documentation. I am not sure what javadoc you were referring to.
To answer your question: no it's not a safe workaround. What you were using before worked by "side-effect" (i.e. it wasn't supposed to work, if your bean was initialized before the CacheInterceptor you would have the same problem with an older version of the framework). Don't call such low-level infrastructure in your own code.
Just had the exact same problem as OP and listening to ContextRefreshedEvent was causing my initialization method to be called twice. Listening to ApplicationReadyEvent worked best for me.
Here is the code I used
#Component
public class MyInitializer implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
//doing things
}
}
Autowire ApplicationContext and invoke method call using :
applicationContext.getBean(RBService.class).getRawBundle(bundleName, DEFAULT_REQUEST_LANG);
where getRawBundle is Cacheable method.