What I would like to achieve is the ability to "dynamically" (i.e. based on a property defined in a configuration file) enable/disable the importing of a child Spring XML context.
I imagine something like:
<import condition="some.property.name" resource="some-context.xml"/>
Where the property is resolved (to a boolean) and when true the context is imported, otherwise it isn't.
Some of my research so far:
Writing a custom NamespaceHandler (and related classes) so I can register my own custom element in my own namespace. For example: <myns:import condition="some.property.name" resource="some-context.xml"/>
The problem with this approach is that I do not want to replicate the entire resource importing logic from Spring and it isn't obvious to me what I need to delegate to to do this.
Overriding DefaultBeanDefinitionDocumentReader to extend the behaviour of the "import" element parsing and interpretation (which happens there in the importBeanDefinitionResource method). However I'm not sure where I can register this extension.
Prior to Spring 4, the closest you can get using standard Spring components is:
<import resource="Whatever-${yyzzy}.xml"/>
where ${xyzzy} interpolates a property from the system properties. (I use a hacky custom version of the context loader class that adds properties from other places to the system properties object before starting the loading process.)
But you can also get away with importing lots of unnecessary stuff ... and use various tricks to only cause the necessary beans to be instantiated. These tricks include:
placeholder and property substitution
selecting different beans using the new Spring expression language,
bean aliases with placeholders in the target name,
lazy bean initialization, and
smart bean factories.
This is now completely possible, using Spring 4.
In your main application content file
<bean class="com.example.MyConditionalConfiguration"/>
And the MyConditionalConfiguration looks like
#Configuration
#Conditional(MyConditionalConfiguration.Condition.class)
#ImportResource("/com/example/context-fragment.xml")
public class MyConditionalConfiguration {
static class Condition implements ConfigurationCondition {
#Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.PARSE_CONFIGURATION;
}
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// only load context-fragment.xml if the system property is defined
return System.getProperty("com.example.context-fragment") != null;
}
}
}
And then finally, you put the bean definitions you want included in the /com/example/context-fragment.xml
See the JavaDoc for #Conditional
As mentioned earlier, this can be easily accomplished with profiles if you're using Spring 3.1+
<!-- default configuration - will be loaded if no profile is specified -->
<!-- This will only work if it's put at the end of the configuration file -->
<!-- so no bean definitions after that -->
<beans profile="default">
<import resource="classpath:default.xml" />
</beans>
<!-- some other profile -->
<beans profile="otherProfile">
<import resource="classpath:other-profile.xml" />
</beans>
otherProfile can be easily activated with e.g.
mvn install -Dspring.profiles.active=otherProfile
if you're using different profiles in tests, just add -DforkMode=never to make sure that the tests will run inside same VM, therefore the param spring.profiles.active wont be lost
With Spring 3.1.x you can use bean profiles to achieve conditional resource import and bean instantiation. This is of course of no help if you are using an earlier version :)
For the record, Robert Maldon explains how to accomplish conditional definition of beans in this post: http://robertmaldon.blogspot.com/2007/04/conditionally-defining-spring-beans.html. It is a bit long to copy it here (besides, I don't think I should copy-paste his article anyway).
The end result with this approach, adapted for your example, is:
<condbean:cond test="${some.property.name}">
<import resource="some-context.xml"/>
</condbean:cond>
It is certainly not so simple as Stephen C's solution, but it is much more poweful.
Another one to consider for Spring 3.0:
<alias name="Whatever" alias=""Whatever-${yyzzy}" />
where ${xyzzy} interpolates a property from the system properties.
Another option is to have your app load a modules-config.xml file that is located in the /conf folder and edit it during the install/config phase to uncomment the modules you want loaded.
This is the solution I'm using with a web application that serves as a container for different integration modules. The web application is distributed with all the different integration modules. A modules-config.xml is placed in tomcat's /conf folder and the conf folder is added to the classpath (via catalina.properties/common.loader property). My web app webapp-config.xml has a <import resource="classpath:/modules-config.xml"/> to get it loaded.
You can override contextInitialized(javax.servlet.ServletContextEvent event) in your own ContextLoaderListener and set required System property before super.contextInitialized(event) called like this
package com.mypackage;
import org.springframework.web.context.ContextLoaderListener;
public class MyContextLoaderListener extends ContextLoaderListener {
public void contextInitialized(javax.servlet.ServletContextEvent event) {
System.setProperty("xyz", "import-file-name.xml");
super.contextInitialized(event);
}
}
And than replace ContextLoaderListener to MyContextLoaderListener in your web.xml
<listener>
<listener-class>com.mypackage.MyContextLoaderListener</listener-class>
</listener>
Now you can use in your spring.xml
<import resource="${xyz}" />
I hope this will help.
Related
I'm trying to get a better understanding of the #Autowired annotations component scanning, but all the examples I found so far use context.getBean(..) at some point to get at least one Bean to start with.
I also read that doing that is considered bad practice , but I can't seem to find any information on how to do it without context.getBean(..)
Could somebody please enlighten me with an example and information on how to do this ?
Define your bean in xml and use
<context:component-scan base-package="com" />
<mvc:annotation-driven />
Bean def
<bean id="processEngine" class="com.processEngine">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
now you can get bean as following
#Autowired
private ProcessEngine processEngine;
how it works
spring scans the bean's recipes either from xml or java configuration. then spring creates a beanDefinitions which are 'loaded' into BeanFactory. BeanFactory triggers a set of BeanPostProcessors (BPP) which are scanning classes for particular annotations like Autowired/Resource/PostProcessor and etc. and do appropriate actions. in case when your class contains #Autowired annotation, AutowiredAnnotationBeanPostProcessor would auto wire required field (dependencies), and when creation of an object is done and all BPP worked out, object is ready to be used by the app, from this point your code can get 'ready' objects from container.
there are some cases when you would need to access this beans from the code which is out of spring's control and not managed by container. in order to do so, you would need to get the ApplicationContext (container) and call #getBean specifying either name or type. using applicationContext directly is not a good practice because there are some problems that you can come to, f.ex. id of a bean might be changed and if you refer to bean by id then NPE would be thrown.
configuration
there are several approaches to configure spring to scan the classes for finding bean recipes. one would be defining component-scan, in this case classes which are located in the path that you've set and having any of valid spring annotations like #Component, #Service, #Repository, #Controller (for web container) would be considered. another way would be specifying each bean separately using <bean> or #Bean.
examples.
if you want to create a web app then you should see DispatcherServlet with ContextLoaderListener classes. this classes would boot your app and load everything according to configuration. f.ex. here,
but if you want to create a desktop app, then you would end up with something like this
From time to time (usually when not using Spring Boot), I use something along the lines of the following code:
public static <T> T autowire(ApplicationContext ctx, T bean) {
ctx.getAutowireCapableBeanFactory().autowireBean(bean);
return bean;
}
In my main, I create an instance of the main application class that contains a few #Autowired annotations for the main services / entry points to my Spring application.
I have the following scenario:
Spring project A with multiple bean configurations, including a bean named "searchHelper":
<bean name="searchHelper" class="com.test.SearchHelperImpl"/>
where SearchHelperImpl implements "SearchHelper" interface
Spring project B depends on A with a custom SearchHelperBImpl
What I was thinking of making is just copying the whole configuration into the new project and changing what needs to be changed, but that's not convenient and there must be an easier way of doing this.
My question is, how do I override the definition of the "searchHelper" bean to use SearchHelperBImpl instead of SearchHelperImpl? I want to use the same bean name in order for everything that uses this name to use the new implementation. I am using Spring 3.2.2
Thanks
You should be able to utilize the primary xml attribute on your bean element.
<bean name="searchHelper" primary="true" class="com.test.SearchHelperBImpl"/>
Alternatively, if you are using JavaConfig, you can utilize the #Primary annotation.
#Primary
#Bean
public SearchHelper searchHelper() {
return new SearchHelperBImpl();
}
One interesting "feature" (some consider it a bug) of Spring is that a bean with the same name declared later in configuration will override a bean declared earlier in configuration. So if your project B depends on A, and the configuration in A is included in B, and B defines a bean by the same name after the A configuration then the B instance will "win" and that's the instance you will get.
I would not recommend depending on this behavior, but would go with the answer regarding the Primary annotation. I just thought I would bring this up so you would be aware that even without the primary, or in case the one in project A is also primary you would know that the latest definition wins.
NOTE
This answer relates to how to avoid duplicate bean definitions. For overriding see the answer by nicholas.hauschild.
More effective solution to avoid copying is to place all the beans that are common for both projects in a separate XML configuration file, say "common-beans.xml". And in the configuration XML file for the project B (and any other project that needs those beans) you import that file like this:
<import resource="common-beans.xml" />
Simple example
example-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"
<!--
Assuming common-beans.xml is a Spring config file
that contains the definitions you need to use
in the current project.
-->
<import resource="common-beans.xml" />
<!-- Defining a bean with the implementaion you need -->
<bean name="searchHelper" class="com.test.SearchHelperBImpl"/>
<!-- Some other configurations if needed -->
</beans>
Useful reading:
Composing XML-based configuration
metadata
I have a Spring 3.1 application. Let's say it has an XML with the following content:
<context:property-placeholder location="classpath:somename.properties" />
<context:property-placeholder location="classpath:xxx.properties" />
I would like some.properties to be always loaded (let's assume it exists), but the xxx part of the second place holder to be replaced by some name depending on the active profile. I've tried with this:
<beans profile="xx1">
<context:property-placeholder location="classpath:xx1.properties" />
</beans>
<beans profile="xx2">
<context:property-placeholder location="classpath:xx2.properties" />
</beans>
Also, both files have properties with the same key but different value.
But it didn't work as some later bean that has a placeholder for one property whose key is defined in xx1.properties (and xx2.properties) makes Spring complain that the key is not found in the application context.
You can do:
<context:property-placeholder location="classpath:${spring.profiles.active}.properties" />
It works fine, but is perhaps not adapted when using multiple profiles in the same time.
When declaring 2 property placeholders, if the 1st one does not contain all the applications keys, you should put the attribute ignoring unresolvable = true, so that the 2nd placeholder can be used.
I'm not sure if it is what you want to do, it may if you want both xx1 and xx2 profiles be active in the same time.
Note that declaring 2 propertyplaceholders like that make them independant, and in the declaration of xx2.properties, you can't reuse the values of xx1.properties.
If you need something more advanced, you can register your PropertySources on application startup.
web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.xxx.core.spring.properties.PropertySourcesApplicationContextInitializer</param-value>
</context-param>
file you create:
public class PropertySourcesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourcesApplicationContextInitializer.class);
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
LOGGER.info("Adding some additional property sources");
String profile = System.getProperty("spring.profiles.active");
// ... Add property sources according to selected spring profile
// (note there already are some property sources registered, system properties etc)
applicationContext.getEnvironment().getPropertySources().addLast(myPropertySource);
}
}
Once you've done it you just need to add in your context:
<context:property-placeholder/>
Imho it's the best way to deal with spring properties, because you do not declare local properties everywhere anymore, you have a programmatic control of what is happening, and property source xx1 values can be used in xx2.properties.
At work we are using it and it works nicely. We register 3 additional property sources:
- Infrastructure: provided by Puppet
- Profile: a different property loaded according to the profile.
- Common: contains values as default, when all profiles share the same value etc...
I have decided to submit and answer to this as it has not yet been accepted. It may not be what you are looking for specifically but it works for me. Also note that i am using the new annotation driven configuration however it can be ported to the xml config.
I have a properties file for each environment(dev.properties, test.properties etc)
I then have a RootConfig class that is the class that is used for all the configuration. All that this class has in it is two annotations: #Configuration and #ComponentScan(basePackageClasses=RootConfig.class).
This tells it to scan for anything in the same package as it.
There is then a Configuration Containing all my normal configuration sitting wherever. There is also a configuration for each environment in the same package as the root configuration class above.
The environment specific configurations are simply marker classes that have the following annotations to point it to the environment specific properties files:
#Configuration
#PropertySource("classpath:dev.properties")
#Import(NormalConfig.class)
#Profile("dev")
The import tells it to bring in the normal config class. But when it gets in there it will have the environment specific properties set.
I am maintaining a large Java EE system. Most of the business logic is converted from EJB:s into POJO:s configured in several spring context configuration files. EJB:s are mostly used as Facades, that looks up the business logic spring beans from a context composed of all spring context configuration files mentioned earlier. For this we use the AbstractStatelessSessionBean provided with the spring framework.
All these configuration files have the default-lazy-init=true directive, which means that the business logic beans are not created until they are actually used by the system. This is preferable most of the time since republishing in developer mode becomes faster.
But when large merges are made, we are having problems finding all the configuration errors, such as missing dependencies.
My idea is to write some form of integration test, with the purpose of finding those errors. This means, i think, that I need to find a way to override all default-lazy-init=true declarations when creating the application context.
Is there any way of doing this programmatically, or perhaps with some test-only context file that includes all the actual context files?
Let's say currently you have a single applicationContext.xml file containing all bean definitions:
<beans default-lazy-init="true">
<!-- all your beans -->
</beans>
Rename it to applicationContext-main.xml or something and remove default-lazy-init="true" attribute. Now create two applicationContext.xml files:
<beans default-lazy-init="true">
<import resource="applicationContext-core.xml"/>
</beans>
and:
<beans default-lazy-init="false">
<import resource="applicationContext-core.xml"/>
</beans>
As you can see the only difference is the value of default-lazy-init. During development your team can use the former version of applicationContext.xml that includes all the beans with lazy-init. On staging and testing environments switch it to the latter so that all beans included in applicationContext-core.xml are created eagerly.
I believe that the best way is to control lazy init of beans is to leave the default-lazy-init out of all config files except the topmost as Tomasz Nurkiewicz suggests. I did however in this case need a quick and dirty fix to verify all bean definitions. (It is a bit of a process to change the lazy init policy.)
I came up with a simple BeanFactoryPostProcessor which seems to do the job:
public class NonLazyInitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String beanName : beanFactory.getBeanDefinitionNames()) {
beanFactory.getBeanDefinition(beanName).setLazyInit(false);
}
}
}
If included in a context file, it will override the lazy init flag set by any included context files.
<beans default-lazy-init="false">
<bean class="example.NonLazyInitBeanFactoryPostProcessor" />
<import resource="applicationContext-core.xml"/>
</beans>
If I try to create a context from the above xml, configuration errors previously hidden by lazy initialization will show up immediately as exceptions.
There is one 'but' in this PostProcessor
for (String beanName : beanFactory.getBeanDefinitionNames()) {
beanFactory.getBeanDefinition(beanName).setLazyInit(false);
}
This for loop will iterate only over top most beans not including e.g internal(local) bean defintions ...
You can't access the scanner from the context - it's completely private, but since you can step into the source code, you can see what's required to set up your own. I used Spring's own ReflectionTestUtils to set my own configured scanner in the context:
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
BeanDefinitionDefaults defaults = new BeanDefinitionDefaults();
defaults.setLazyInit(true);
scanner.setBeanDefinitionDefaults(defaults);
ReflectionTestUtils.setField(context, "scanner", scanner);
context.scan("com.some.path");
You can do this anywhere you have access to the Application Context before the component scan takes place.
How to enable the #Required annotation in Java (Spring 3.1) ? Not in a xml, but in through Java. Also under which annotation I put this enabling? Under #Feature (in #FutureConfiguration or #Bean (in #Configuration) ?
Edit:
#Feature
public MvcAnnotationDriven annotationDriven(ConversionService conversionService) {
return new MvcAnnotationDriven().conversionService(conversionService)
.argumentResolvers(new CustomArgumentResolver());
}
Does this enables all annotations?
#anubhava's answer works, but he's referenced the Spring 2.0 manual, which is 5 years old.
In XML config, Spring 3.x has a more elegant approach: <context:annotation-config/>. This also enabled a whole other bunch of features that you'll probably want, whereas RequiredAnnotationBeanPostProcessor only enables a few.
See Spring 3.x manual.
If you're using #Bean-style config, then annotations like #Required should already be enabled, since that's how #Bean works. However, it's possible that this is a bug - Spring 3.1 is still in early beta, and big chunks of it are likely to be broken.
Unless you really know what you're doing, I strongly recommend sticking to 3.0.x.
From the Spring manual:
There is one last little (small, tiny)
piece of Spring configuration that is
required to actually 'switch on' this
behavior. Simply annotating the
'setter' properties of your classes is
not enough to get this behavior. You
need to enable a component that is
aware of the #Required annotation and
that can process it appropriately.
This component is the
RequiredAnnotationBeanPostProcessor
class. This is a special
BeanPostProcessor implementation that
is #Required-aware and actually
provides the 'blow up if this required
property has not been set' logic. It
is very easy to configure; simply drop
the following bean definition into
your Spring XML configuration.
<bean class=
"org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
Please check: http://static.springsource.org/spring/docs/2.0.x/reference/metadata.html
Use AnnotationConfigApplicationContext if you don't want to use XML:
Standalone application context,
accepting annotated classes as input -
in particular #Configuration-annotated
classes, but also plain #Components
and JSR-330 compliant classes using
javax.inject annotations. Allows for
registering classes one by one
(register(java.lang.Class...)) as well
as for classpath scanning
(scan(java.lang.String...)).
In case of multiple Configuration
classes, Bean methods defined in later
classes will override those defined in
earlier classes. This can be leveraged
to deliberately override certain bean
definitions via an extra Configuration
class.
Sample Code:
ConfigurableApplicationContext applicationContext =
new AnnotationConfigApplicationContext(
"com.mycompany.package1",
"com.mycompany.package2",
"com.mycompany.package3"
// etc.
);
applicationContext.refresh();