Upon starting my webapp within Tomcat 6.0.18, I bootstrap Spring with only what is necessary to initialize the system -- namely, for now, database migrations. I do not want any part of the system to load until the migrations have successfully completed. This prevents the other beans from having to wait on the migrations to complete before operating, or even instantiating.
I have a startup-appcontext.xml configured with a dbMigrationDAO, a startupManager which is a ThreadPoolExecutor, and lastly, a FullSystemLauch bean. I pass a list of configuration locations to the FullSystemLaunch bean via setter injection. The FullSystemLaunch bean implements ServletContextAware, gets a reference to the current WebApplicationContext and thus I can have a ConfigurableListableBeanFactory. Unfortunately, this bean factory isConfigurationFrozen() returns true, so by calling beanFactory.setConfigLocations(configLocations) has no effect.
Can I accomplish this or is Spring preventing me from doing so because it's a bit out of the ordinary? It seems reasonable if understood, but also a bit dangerous. And yes, I'm willing to blow away the current context b/c the currently loaded Singletons are not needed once initialization is complete.
Thank you for the help.
My opinion would be to allow Spring to initialise your beans is it sees fit - in the order of their declared dependencies.
If you need database migrations there are a couple of patterns to have them run first:
if you're using Hibernate/JPA make your sessionFactory/persistenceManager depend-on the migration beans;
if you're using plain JDBC create a wrapper DataSource and in its init-method invoke the migrations ( code sample)
The advantage is clear: simplicity.
You could use the existing context as parent context for the other contexts, although I doubt that you could replace the existing WebApplicationContext.
If you use EAR - WAR packaging, you get this out-of-the-box (sort of) by loading an application context from the EAR and then adding one in the WAR.
Not sure whether this is applicable in your situation.
Could lazy-initialization be an alternative for what you are trying to achieve?
Possible XmlBeanDefinitionReader can help you?
you can upcat the WebApplicatonContext to ConfigurableWebApplicationContext
then use the setConfigurations method.
dont forget refresh;
There was the same task and I created two contexts: startUpContext.xml and applicationContext.xml. In startUpContext.xml there is a bean, which triggers loading of appliationContext.xml. (application context location is configured in startUpContext.xml as a property of a trigger). And finally the trigger replaces locations of the current context and refreshes it:
applicationContext.setConfigLocations(locations);
applicationContext.refresh();
(startUpContext.xml is loaded with a standard spring context loader listener)
Related
I have dependency in my spring boot project which fetches values of some properties using Spring's EnvironmentPostProcessor.
Now these properties are database credentials and not everyone has access to the credential since there is no dev environment for the db in question. I just want to change the configuration that the credentials don't get fetched on dev or local environment on application startup as that would result in a error and the application will fail to start.
Class A implements EnvironmentPostProcessor{}
I tried to use #Lazy annotation on the Class Annoteted with #ConfigurationProperties. I also tried using my own BeanFactoryPostProcessor (with #Order(HighestPrecedence) to programmatically set the A to lazy load, but it gets called before my BeanFactoryPostProcessor's postProcessBeanFactory method.
Is what I'm trying to achieve possible and am I going about it the wrong way?
#Lazy is only to be used with #Bean or #Component (Or any #Component-based annotations ex. #Service)
Take note: You can also add it to a #Configuration class, but that just means that all Beans in the class are annotated with #Lazy
#Lazy is a bit of a weird annotation in general; it should be seen as an IF possible then lazy load. If some other bean needs the lazy bean, the lazy bean will be initialized. (It's like the Pirate code, more of a guideline than an enforced rule)
Finally, marking #ConfigurationProperties with #Lazy seems a bit odd. As Spring will need these Configuration property "beans" to create the Spring Context.
However, the common use case for #Lazy is a failing database connection, preventing the application from starting. See the question if that is what you are running into.
Summary:
You can configure your repositories to be lazy-loaded with:
spring.data.jpa.repositories.bootstrap-mode=lazy
Last remark (Me just guessing)
If you wish to change properties once your application is already running, I would look at the following tutorial. It goes into manually reloading configuration and also #RefreshScope.
According to documentation EnvironmentPostProcessors must be registered via META-INF/spring.factories:
Allows for customization of the application's Environment prior to the
application context being refreshed. EnvironmentPostProcessor
implementations have to be registered in META-INF/spring.factories,
using the fully qualified name of this class as the key.
Implementations may implement the Ordered interface or use an #Order
annotation if they wish to be invoked in specific order.
Annotated spring beans in my spring-cloud application are being created twice. I assume this is because they get constructed into the bootstrap context and then into a child application context.
For me this is undesirable because some are annotated with #Scheduled to perform periodic refreshes of the data that they provide from a backend data source and this is happening twice in quick succession once for each context.
If it's not otherwise harmful then can I disable all of the application beans from being created in the bootstrap context? If not then can I detect in code when I'm running in the bootstrap context? I use entirely annotation-based beans with component scanning enabled on the Camden SR4 release.
OK I solved this myself. It was down to two different issues in the code and nothing to do with multiple contexts.
Firstly my long held assumption that an #PostConstruct method is only ever called once was false. Moving my one-off bean initialisation code to an implementation of ApplicationListener<ApplicationReadyEvent> solved that one.
Secondly I got bit by multiple initialisation of a bean with an #Scheduled annotation causes the scheduler to run multiple times. To be fair, this behaviour is noted in the documentation. This was easily solved by moving the scheduled task creation to a regular java ScheduledExecutorService set up in the ApplicationReadyEvent implementation.
I need to provide support for external preperties decryption in Spring application. I planned to use a mechanism from spring-cloud-config, which is triggered after the Environment is ready and add decrypted properties with higher precedence. Unfortunately it heavily relies on Spring Boot bootstrap mechanism which emits ApplicationEnvironmentPreparedEvent. Looking into the Spring Framework code the environment and context creation is highly coupled and it would be rather hard to run my own code between that. The application I am working with is a large, multi module "standard" Spring MVC application and I would not like to convert it into Spring boot application right now.
Question:
How could I execute my code after the environment was created and before the context creation (to modify properties before they will be injected into "normal" beans) in Spring (not Spring Boot) application?
Alternative question:
Is there any other way to get control over properties being injected into beans (for modify values originally resolved by Spring)?
You can create a custom ApplicationContextInitializer which adds decryption or whatever to the PropertySources of your choice.
We do something similair in one of the application I currently develop. After loading some custom properties from files and databases we wrap all the available PropertySources in a EncryptablePropertySource because several properties are encrypted (We use the Jasypt library for that).
Use #Value("${propname}") annotation on a setter method, instead of using on the field.
You can write code to handle transform/validate the property in the setter method, and then assign to the field.
In the mean time I have found customizeContext method in ContextLoader which reads defined ApplicationContextInitializers. It is executed after the environment was created and before the context is reloaded, so decryption in an initializer should work (at least in the base case):
ConfigurableEnvironment env = wac.getEnvironment();
(...)
customizeContext(sc, wac);
wac.refresh();
Our Java Web application loads with over 1000 plugins that are all registered by us as Spring beans using the ApplicationContext#registerBeanDefinition() method. These beans often have other dependencies which we also register as spring beans using the same method (for a total of about 7,000 Spring bean definitions...not including our core application code).
The problem is that the startup time is long (approximately 6.5 minutes of just plugin bean definition loading). We would prefer to spread this load time out over a much longer period while our app is actually processing other requests utilizing plugins that have already had their bean definitions registered. Most of the plugins are seldom used. Thus, we would really like to lazily register our bean definitions (this is different from lazy-init of singleton beans which we already do today). However, this seems costly with any existing Spring ApplicationContext that supports 'hot' refresh() calls (as the Spring documentation refers to it).
The Spring ApplicationContext classes that support 'hot' refresh start by destroying all of the singleton beans. Most of our plugins are singletons, so each call to refresh() would cause most of our plugins to be destroyed and then recreated...costly. If we don't call refresh, then our newly loaded plugin beans will not be post-processed (e.g., AOP, etc...).
We can guarantee that when we are forced to load another plugin, we will also load any of its dependencies that are not already loaded. So, we would never ben in a situation where a loaded bean definition is invalid.
It seems to me that this calls for a new type of Spring ApplicationContext that supports 'hot' refresh, but only for the purpose of adding new bean definitions. Preexisting bean definitions are not removed/reloaded, and not re-processed by BeanFactoryPostProcessors on subsequent refresh() calls, and pre-existing singletons are not destroyed!
Does this already exist?. Is there a better solution that I'm overlooking?
This sounds like you're looking for #Lazy.
4.4.4 Lazy-initialized beans
A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.
I am a bit familiar with Spring framework but am still having lots of question concerning use of spring from project architectural view point. Now I am setting up Spring 3 and a Maven web application and am willing to try out all the the fancy component-scan's and autowiring features however this is where I get confused.
I am trying to break the project into sub-modules. And at some point these sub-modules may include something-context.xml in classpath*:resource/META-INF, like for instance when I will want to define a datSource related stuff in a separate module. So that's fine spring let's you load context files from within class-paths of all of the jars.
But here is where it gets vague - say I am using component scan. I am obviously using spring DispatcherServlet and it needs a servlet context to be loaded, and then there is a global application context parameter specified in web.xml contextConfigLocation.
So now servlet context config has a component-scan feature enabled for com.mycom.project.controllers and context loaded in the global contextConfigLocation has a context loaded with component scan feature for package com.mycom.project also searches for classpath*:META-INF/spring/*-context.xml.
So my question is - does this load controller's twice given that component scan is used for a for com.mycom.project.controllers and com.mycom.project? Or is it all loaded into one huge container and the contextConfigLocation parameter for either DispatcherServlet or global declaration is sort of access issue ? As in DispatcherServlet will reach only what's defined in servlet-context.xml but won't be able to use anything else?
And if my assumption is wrong, could I have a suggestion on how to manage multi-module project issues?
Thanks.
Yes, you might run into trouble. See this link for how to solve your problem.
#Service are constructed twice
The way you proceed when creating modules seems valid to me. You have a context.xml file for each module and all will get loaded once you load the application. Your modules are self-contained and can also be used in different environments. That's pretty much the way I'd also do it.