Currently learning to build spring apps. I have been quite sucessful deploying mock applications for now, but one thing has been annoying me, which is not understanding the mechanisms behind the numerous annotations we add to the code. Look, I'm not saying I don't know which purpose they serve, where they act, nor am I questioning their helpfulness.
My point is that I feel that skipping the changes that should be made (or are being made?) in the XML files actually makes me feel that at the end of the day I don't know what I am truly writing. Let me be more specific, so you could answer me with regards to the following example. This is from Spring manual.
Let’s assume we have the following configuration that defines firstMovieCatalog as the primary MovieCatalog
#Configuration
public class MovieConfiguration {
#Bean
#Primary
public MovieCatalog firstMovieCatalog() { ... }
#Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
With such configuration, the following MovieRecommender will be autowired with the
firstMovieCatalog.
public class MovieRecommender {
#Autowired
private MovieCatalog movieCatalog;
// ...
}
The corresponding bean definitions appear as follows.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog" primary="true">
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
Okay, so, I think if you can answer me 2 questions regarding this example, It would clear a lot the understanding I am lacking here.
The first thing that is not clear for me: is the annotation process a SEPARATED process from the XML configuration, or is it GENERATING this equivalent XML configuration in some hidden fashion?
Where actually IS this XML configuration file? All spring applications I generated through Initializr just generate the pom.xml file, and it does not include configuration. If I were not using the annotations, would I have to manually write an equivalent configuration in the pom?
is the annotation process a SEPARATED process from the XML configuration, or is it GENERATING this equivalent XML configuration in some hidden fashion?
Spring is not generating any XML or annotation in any case. Spring use XML and annotation processing to get info about which components (classes) are available to use and which beans (instances) to create, inject and use for processing. Then, all these beans could be retrieved by application context (not to confuse with xml of the same name).
Where actually IS this XML configuration file?
Spring first version used XML to configure your app. Later (starting in Spring 3), Spring added annotation support and processing to ease application configuration. Annotations are just another way to configure your components and beans without the hassle of maintaining big XML files (over 1000 lines or even more) or just to avoid dealing with XML at all. Current Spring versions support both configurations, you could also use a mix: using XML and using annotations.
Note that Spring's ApplicationContext has several implementations with different entry points for configuration:
AnnotationConfigApplicationContext accepts a class decorated with #Configuration.
ClassPathXmlApplicationContext accepts the path of a XML file available in application classpath.
If I were not using the annotations, would I have to manually write an equivalent configuration in the pom?
First thing first: POM files are for maven processing, not for Spring. Since you're using Maven, and you want to try using a Spring Boot application without annotations, then you can have this project structure:
- src
- main
- java
/* your Java packages and files */
- com.example
+ App <--- Main entry point
- com.example.service
+ Car <--- Component 1
+ Engine <--- Component 2
- resources
+ my-beans.xml <--- XML configuration. Name can be anything
App class:
package com.example;
import com.example.service.Car;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
#Configuration
#ImportResource("classpath:my-beans.xml")
public class App {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(App.class, args);
Car car = ctx.getBean(Car.class);
car.move();
}
}
Car.class:
package com.example.service;
public class Car {
private Engine engine;
public void move() {
engine.start();
}
}
Engine.class:
package com.example.service;
public class Engine {
public void start() {
System.out.println("engine starts");
}
}
my-beans.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean class="com.example.service.Engine" />
<bean class="com.example.service.Car" />
<property name="engine" ref="engine"></property>
</bean>
</beans>
is the annotation process a SEPARATED process from the XML configuration, or is it GENERATING this equivalent XML configuration in some hidden fashion
When you initialize a Spring application, you instantiate an ApplicationContext: it's responsible to load all the context definition (the beans, services...).
ApplicationContext is actually an interface which has several implementations depending on how your context is defined:
ClassPathXmlApplicationContext which reads an XML file
AnnotationConfigApplicationContext for annotations based approach
...
Thus you can see it as a unique process, only the datasource is different: either XML or annotations. But they describe the same thing: a context.
Related
Following the question Understanding Spring #Autowired usage I wanted to create a complete knowledge base for the other option of spring wiring, the #Configuration class.
Let's assume I have a spring XML file that looks like this:
<?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.0.xsd">
<import resource="another-application-context.xml"/>
<bean id="someBean" class="stack.overflow.spring.configuration.SomeClassImpl">
<constructor-arg value="${some.interesting.property}" />
</bean>
<bean id="anotherBean" class="stack.overflow.spring.configuration.AnotherClassImpl">
<constructor-arg ref="someBean"/>
<constructor-arg ref="beanFromSomewhereElse"/>
</bean>
</beans>
How can I use #Configuration instead? Does it have any affect on the code itself?
Migrating XML to #Configuration
It is possible to migrate the xml to a #Configuration in a few steps:
Create a #Configuration annotated class:
#Configuration
public class MyApplicationContext {
}
For each <bean> tag create a method annotated with #Bean:
#Configuration
public class MyApplicationContext {
#Bean(name = "someBean")
public SomeClass getSomeClass() {
return new SomeClassImpl(someInterestingProperty); // We still need to inject someInterestingProperty
}
#Bean(name = "anotherBean")
public AnotherClass getAnotherClass() {
return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse); // We still need to inject beanFromSomewhereElse
}
}
In order to import beanFromSomewhereElse we need to import it's definition. It can be defined in an XML and the we'll use #ImportResource:
#ImportResource("another-application-context.xml")
#Configuration
public class MyApplicationContext {
...
}
If the bean is defined in another #Configuration class we can use the #Import annotation:
#Import(OtherConfiguration.class)
#Configuration
public class MyApplicationContext {
...
}
After we imported other XMLs or #Configuration classes, we can use the beans they declare in our context by declaring a private member to the #Configuration class as follows:
#Autowired
#Qualifier(value = "beanFromSomewhereElse")
private final StrangeBean beanFromSomewhereElse;
Or use it directly as parameter in the method which defines the bean that depends on this beanFromSomewhereElse using #Qualifier as follows:
#Bean(name = "anotherBean")
public AnotherClass getAnotherClass(#Qualifier (value = "beanFromSomewhereElse") final StrangeBean beanFromSomewhereElse) {
return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse);
}
Importing properties is very similar to importing bean from another xml or #Configuration class. Instead of using #Qualifier we'll use #Value with properties as follows:
#Autowired
#Value("${some.interesting.property}")
private final String someInterestingProperty;
This can be used with SpEL expressions as well.
In order to allow spring to treat such classes as beans containers we need to mark this in our main xml by putting this tag in the context:
<context:annotation-config/>
You can now import #Configuration classes exactly the same as you would create a simple bean:
<bean class="some.package.MyApplicationContext"/>
There are ways to avoid spring XMLs altogether but they are not in the scope of this answer. You can find out one of these options in my blog post on which I'm basing my answer.
The advantages and disadvantages of using this method
Basically I find this method of declaring beans much more comfortable than using XMLs due to a few advantages I see:
Typos - #Configuration classes are compiled and typos just won't allow compilations
Fail fast (compile time) - If you forget to inject a bean you'll fail on compile time and not on run-time as with XMLs
Easier to navigate in IDE - between constructors of beans to understand the dependency tree.
Possible to easily debug configuration startup
The disadvantages are not many as I see them but there are a few which I could think of:
Abuse - Code is easier to abuse than XMLs
With XMLs you can define dependencies based on classes that are not available during compile time but are provided during run-time. With #Configuration classes you must have the classes available at compile time. Usually that's not an issue, but there are cases it may be.
Bottom line: It is perfectly fine to combine XMLs, #Configuration and annotations in your application context. Spring doesn't care about the method a bean was declared with.
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
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.
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.
Let's say I have following Spring config (version of Spring is 3.0.3):
<?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.xsd">
<bean id="theFactoryBean" class="wax.MyFactoryBean"/>
<bean id="factoryBeanUser" class="wax.FactoryBeanUser">
<!-- what should be placed here?-->
</bean>
</beans>
I have instance of FactoryBean implementation and some other instance. I need Spring to inject to other instance FactoryBean, not the object it produces.
There are two possible ways to solve it.
First one, obvious and malfunctional:
<bean id="factoryBeanUser" class="wax.FactoryBeanUser">
<property name="myFactoryBean" ref="&theFactoryBean"/>
</bean>
With this config Spring throws following exception on start:
[skipped irrelevant part]
Caused by: org.xml.sax.SAXParseException: The reference to entity "theFactoryBean" must end with the ';' delimiter.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:195)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:174)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:388)
at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1414)
I found this solution Spring: Getting FactoryBean object instead of FactoryBean.getObject(), this question is maked as answered and four people voted for it. So I assume that this solution might work, but currently there is something wrong in my code.
Second one, working but awkward:
public class FactoryBeanUser implements ApplicationContextAware{
private MyFactoryBean myFactoryBean;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
myFactoryBean = (MyFactoryBean)applicationContext.getBean("&theFactoryBean");
}
}
My question is it possible to make first approach functional or I should stick with a second way?
It seems the XML parser interprets the ampersand (&) as a start of an XML-entity. You can try using ref="&theFactoryBean".
The spring docs is not clear whether this syntax is allowed in an xml file, or only with programatic lookup. But then the xml configuration is used by the app context, so I assume the & should work (although it seems it has not been the best choice for a special symbol)
Here's why I'd suggest another thing - if you really need the factory bean rather than its product, create another bean, that does not implement FactoryBean, define a method createObject() or something like that, and use it in all factories that need it.
A sidenote - better reference the xsd with the version included:
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd