I have one bean which is define in the SpringConfiguration and which gets initialised at the startup.
This bean(Map) is populated by querying the database at startup.
Now the database gets update frequently and I have implemented the ApplicationListener and was trying to implement a cache using TimerTask.
The code of timertask runs fine and in that i am accessing the bean using ApplicationContext but not able to refresh/reinitialize the bean with the new database results.
The #Resource and #Autowired beans still shows the old value.
I want to refresh/Reinitialize the #Autowired / #Resource bean at Runtime. Please advise
If you are using an ORM it should handle it for you.
Otherwise, if you are doing it on you own, you can annotate the bean with #RefreshScope and when you detect a change (wherever method you are using like cron or listener) then just refresh the context from the actuator like:
http://localhost:8080/actuator/refresh
Cheers!
pd: Actuator should be enabled and accesible.
Related
I have a Spring boot application that consumes data from Kafka topic and send email notifications with a data received from Kafka,
#Bean
public EmailService emailService() {
return new EmailServiceImpl(getJavaMailSender());
}
it works perfectly,
but after I added #ConditionalOnBean:
#Bean
#ConditionalOnBean(KafkaTemplate.class)
public EmailService emailService() {
return new EmailServiceImpl(getJavaMailSender());
}
application failed to start:
required a bean of type 'com.acme.EmailService' that could not be
found.
And I can't find any explanation, how it is possible, because KafkaTemplate bean automatically created by Spring in KafkaAutoConfiguration class.
Could you please give me an explanation?
From the 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. If a candidate bean may be created by another
auto-configuration, make sure that the one using this condition runs
after.
This documentation clearly says what might be wrong here. I understand KafkaTemplateConfiguration creates the KafkaTemplate.class. But it may not be added in the bean context while the condition was being checked. Try to use autoconfiguration for KafkaTemplate or make sure the ordering of different configuration classes so that you can have the guarantee of having the KafkaTemplate in bean registry before that conditional check.
We're using Spring Boot 1.5.10 with Spring Data for Apache Cassandra and that's all working well.
We've had a new requirement coming along where we need to connect to a different keyspace while the service is up and running.
Through the use of Spring Cloud Config Server, we can easily set the value of spring.data.cassandra.keyspace-name, however, we're not certain if there's a way that we can dynamically switch (force) the service to use this new keyspace without having to restart if first?
Any ideas or suggestions?
Using #RefreshScope with properties/repositories doesn't work as the keyspace is bound to the Cassandra Session bean.
Using Spring Data Cassandra 1.5 with Spring Boot 1.5 you have at least two options:
Declare a #RefreshScope CassandraSessionFactoryBean, see also CassandraDataAutoConfiguration. This will interrupt all Cassandra operations upon refresh and re-create all dependant beans.
Listen to RefreshScopeRefreshedEvent and change the keyspace via USE my-new-keyspace;. This approach is less invasive and doesn't interrupt running queries. You'd basically use an event listener.
#Component
class MySessionRefresh {
private final Session session;
private final Environment environment;
// omitted constructors for brevity
#EventListener
#Order(Ordered.LOWEST_PRECEDENCE)
public void handle(RefreshScopeRefreshedEvent event) {
String keyspace = environment.getProperty("spring.data.cassandra.keyspace-name");
session.execute("USE " + keyspace + ";");
}
}
With Spring Data Cassandra 2, we introduced the SessionFactory abstraction providing AbstractRoutingSessionFactory for code-controlled routing of CQL/session calls.
Yes, you can use the #RefreshScope annotation on a the bean(s) holding the spring.data.cassandra.keyspace-name value.
After changing the config value through Spring Cloud Config Server, you have to issue a POST on the /refresh endpoint of your application.
From the Spring cloud documentation:
A Spring #Bean that is marked as #RefreshScope will get special treatment when there is a configuration change. This addresses the problem of stateful beans that only get their configuration injected when they are initialized. For instance if a DataSource has open connections when the database URL is changed via the Environment, we probably want the holders of those connections to be able to complete what they are doing. Then the next time someone borrows a connection from the pool he gets one with the new URL.
From the RefreshScope class javadoc:
A Scope implementation that allows for beans to be refreshed dynamically at runtime (see refresh(String) and refreshAll()). If a bean is refreshed then the next time the bean is accessed (i.e. a method is executed) a new instance is created. All lifecycle methods are applied to the bean instances, so any destruction callbacks that were registered in the bean factory are called when it is refreshed, and then the initialization callbacks are invoked as normal when the new instance is created. A new bean instance is created from the original bean definition, so any externalized content (property placeholders or expressions in string literals) is re-evaluated when it is created.
I wanted to introduce #Async methods (for sending mails in parallel) in my SpringBoot application.
But when I put the #EnableAsync annotation on our application's main #Configuration class (annotated with #SpringBootApplication), the Flyway DB migrations are executed before the DataSourceInitializer (which runs schema.sql and data.sql for my tests) executed.
The first operation involving a 'should-be-migrated' database table fails.
Removing the #EnableAsync puts everything back to normal. Why does this happen and how could I fix this (or work around the issue)?
Update Some more findings: #EnableAsync(mode = AdviceMode.ASPECTJ) keeps the original order of DB setup, but the #Async method runs on the same thread as caller thread then. I also saw that the Bean 'objectPostProcessor' is created early (3rd bean) when #EnableAsync is not present, or #EnableAsync(mode = AdviceMode.ASPECTJ) is used. When only #EnableAsync is used, this bean is created much later.
Update 2 While I wasn't able to create a minimal project which reproduces the problem yet, I found out that the proper DB setup order is restored in my affected application when I comment out the #EnableWebSocketMessageBroker in the following:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer
{
...
}
Bean 'webSocketConfig' is the first bean created (as per INFO-level console output) if #EnableWebSocketMessageBroker is present.
It turned out that having both #EnableAsync and #EnableWebSocketMessageBroker present in my application caused the described effect.
Removing one of it, restored the expected behavior, in which case the DataSourceInitializerPostProcessor created the DataSourceInitializer which triggered execution of schema.sql and data.sql, before flyway migrations took place.
When both annotations were present, the registration of the BeanPostProcessor named internalAsyncAnnotationProcessor happened before the DataSourceInitializerPostProcessor was registered.
The cause of the problem was that the registration of internalAsyncAnnotationProcessor caused the creation of the dataSource bean as a side effect. This side effect was caused by spring looking for a TaskExecutor bean to use, for the #Async method execution. spring unexpectedly picked up the clientInboundChannelExecutor bean which was present because of the #EnableWebSocketMessageBroker. Using this bean caused the instantiation of WebSocketMessagingAutoConfiguration which created the objectMapper bean (for json-serialization) which uses services that use DAO-repositories which depend on dataSource. So all those beans got created.
Because DataSourceInitializerPostProcessor wasn't even registered at that time, DataSourceInitializer was created much later, after the flyway migration took place.
The javadoc for #EnableAsync says the following:
By default, a SimpleAsyncTaskExecutor will be used to process async method invocations. Besides, annotated methods having a void return type cannot transmit any exception back to the caller. By default, such uncaught exceptions are only logged.
I assumed, that a SimpleAsyncTaskExecutor will be created to run the #Async methods, but instead spring picked up an existing bean with a matching type.
So the solution for this issue was to implement AsyncConfigurer, and provide my own Executor. This is also suggested in the javadoc of #EnableAsync:
To customize all this, implement AsyncConfigurer and provide:
* your own Executor through the getAsyncExecutor() method, and
* your own AsyncUncaughtExceptionHandler through the getAsyncUncaughtExceptionHandler() method.
With this tweak the DB setup is again executed as expected.
I know I can not use #Autowired annotation in a non managed spring class. But I notice I do able to get a bean instance inside a non managed spring class if I am retrieving it through ApplicationContext getBean method.
can someone explain me why is that ? what is the different between the options ? in both spring doesn't know the non managed class
1) You can use #Autowired in a non-managed spring class if you use #Configurable -with the usage of internal aspect weaving spring will manage to autowired the referenced beans into your "Configurable" class when it is constructed with the "new" operator. Here is an article how this works: http://www.javacodegeeks.com/2013/09/spring-configurable-magic.html
If a class is not configurable spring cannot notice when a new instance is created to autowire its references.
2) The ApplicationContext is "Central interface to provide configuration for an application." Here you have access to the whole spring managed beans etc. That's why you can get everything due to accessing it via ApplicationContext. http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/ApplicationContext.html
ok, so here's the major points:
the "bean declaration" (either in xml or java) is just a recipe of how to instantiate the object (not a object itself).
when spring application boots, the beanFactory receives this recipes from beanDefinitionReader, instantiates objects according to them (recipes) and then pass them (objects) to a list of beanPostProcessors (several times) that are "injecting" dependences to the instantiated objects and then puts the objects into hashMap.
roughly saying applicationContext is a class exposing access to this beans;
and that's how you can access this beans out of spring application using applicationContext.
another thing is that, actually you can inject beans into non managed beans through #Configurable. in this case the AspectJ would be used for making this work
I want to retrieve from my spring context all beans that are of a certain class (or subclass). But this only detects beans that are specifically defined by xml. Beans that are defined by annotations, such as #Serviceare not detected here. (Although inside the app they are detected, initialized, and autowired perfectly).
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("*-context.xml");
Map<String, DataUpdater> beans = ctx.getBeansOfType(MyClass.class, true, true);
// why are beans missing?
I have seen similar problems and I never got getBeansOfType() to work correctly. My solution:
#Autowired
public void setMyClasses( List<MyClass> beans ) {
...
}
Spring will collect the list somehow and inject it. If you don't need to know when the list is injected, you can also inject it as a field:
#Autowired
private List<MyClass> beans;
You must search your beans also in
AnnotationConfigApplicationContext
If you want search in XmlContext and AnnotationContext you must combine them with
#ImportResource("classpath:xmlcontext.xml")
In annotated config
Or you can try implement ApplicationContextAware interface and search in Context provided by it.
Sorry, some stupid error... I was not properly loading my xml files, however beans were being instantiated as some background process was creating a parallel xml context with the correct files.