Testing the below Spring profile :
<?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.1.xsd">
<bean id="bean1"
class="com.Test2">
</bean>
<beans profile="DEV">
<bean id="bean1"
class="com.Test1">
</bean>
</beans>
<bean id="bean1"
class="com.Test2">
</bean>
</beans>
It seems that com.Test2 is loaded if a Spring profile is not set. Is this the expected behavior ?
I'm just trying to understand how Spring loads classes if profiles are set/unset. It seems that if a profile is not set then Spring will create the class if it exists outside the profile, if a profile is set then Spring will create the class for the profile. If the class also exists outside the profile it is not loaded when the profile is loaded.
So, in the above example if DEV profile is set then com.test1 is loaded for bean id bean1, if no profile is set then com.test2 is loaded for bean1. Is this the expected behavior.
The behavior you described is the expected one.
Usually, in Spring there is a rule of thumb related with bean loading: every bean that is loaded with the same name as another one, and that is processed later, will override the older one.
The key term here is being processed later.
In your specific use case, first, every bean that is not defined in any profile will be included, at first glance, in the Spring context.
When you activate a profile, as in your example, you are making visible a new piece of configuration: if this configuration contains a bean with the same name as other one already processed, as indicated, it will override this bean in the Spring context.
This fact is always true independently of the mechanism, Java, XML configuration, or both, you use to define the beans.
It is important to note that the order in which Spring process the different configurations that can be found across the code and different libraries is not deterministic. In your specific use case, when using XML configurations, you can safely assume that it will load the different configurations in the order in which they are imported in your main configuration files (the ones configured for the context load mechanism you choose) and, for every one of them, in the order in which the beans are defined within the same XML file, if that is the case.
This general override rule is always true except in the case you use Spring Boot 2: in this case, if you override a bean by name, by default, an exception will be raised indicating that a bean with this name is already defined in the Spring context. You can restore the usual override behavior by specifying the following configuration property:
spring.main.allow-bean-definition-overriding=true
In addition to profiles, Spring Boot will allow you to load a bean or not depending on several types of conditions. This mechanism is applied usually when loading #Configurations in the auto configuration process.
Yes it is the expected behaviour, Any bean that does not specify a profile belongs to the "default" profile. All the beans with no #profile annotation (or for XML) specified belongs to the default profile.
If you don't specify explicitly a profile then "default" profile is loaded that belong to all beans that are not in scope of any profiles.
So yes, if you run your application without any profile bean1 will be loaded as an instance of com.Test2, in other hand by configuring a profile all beans that are not in scope of a profile ("default") will be replaced with that once that matches definition (in your case com.Test1).
For more details check:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-definition-profiles
https://www.baeldung.com/spring-profiles
Related
I have an issue in the process of migrating an existing spring web application to Spring Boot 1.5.13. I've handled almost everything, but I can't managed to have the good persistence unit injected to WebMvcAutoConfiguration. I actually have 3 different entity manager factory that are imported from the classpath (provided as .xml file by 3 different internal libraries, I have no way to change them). Each of them is splitted in this way:
<sqe-db:jpa-emf database-definition-name="db-name" embedded-datasource="false"/>
and
<bean id="transaction-mnanager-name" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="jpa-lib-name" />
</bean>
The datasources configuration is provided by tomcat at the moment, but I will externalize them to application.properties for the dev environment
After my migration, I didn't find any way to specify the right bean to WebMvcAutoConfiguration, and this is leading to the following error:
Method requestMappingHandlerMapping in org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration required a single bean, but 3 were found:
- jpa-lib1-name1: defined in null
- jpa-lib1-name2: defined in null
- jpa-lib1-name3: defined in null
My goal at this point is to have the following configuration in place:
have the application both runnable as a SpringApplication and deployable as a WAR (and I think I already did it properly)
having a way to use Spring Boot autoconfiguration for web apps, even with multiple entity factory in the classpath
use my own datasources for the "dev" profile, but leave the tomcat one for the other environment.
my requisites are, more or less:
- minimize the changes needed to the existing application
- continue to use the .xml provided by the libraries
An easy option would be to rewrite the configuration, using a more modern annotation driven configuration, but I would prefer to stick to the existing implementation, so that I don't have to change it when a new library is released.
Any way to declare the #Primary bean without touching the existing xml file?
Any ideas, or different approaches are very welcome
After a good night of sleep, I was able to find the easiest of the solution.
I added this to my configuration class:
#Autowired
#Qualifier("name-of-default-factory")
private EntityManagerFactory entityManagerFactory;
#Bean
#Primary
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
And it will now have the primary bean correctly injected.
Is there a decent way to read a spring application context from xml without initializing the beans right away, so they can be mocked or not, before they are actually created?
Yes, I know about lazy-init, but the actual applicationContext.xml is taboo for me.
The situation is that I have to create some JUnit tests on an application which has been created in a way that puts some difficulties in the way:
the configuration file must not be altered, nor the code that is to be tested
there are a lot of beans, some of them rather complex and hard to mock
part of the test is to use as many of the beans un-mocked as possible
some of the beans implement InitializingBean, verifying the environment on initialization and throw errors when Jenkins tries to build.
new FileSystemXmlApplicationContext("config.xml") immediately initializes the beans and throws errors if not in an appropriate environment.
What I have tried:
I have tried Powermock whenNew to mock the offending beans but to do that I would have to know the class which actually creates the beans. As this class belongs to the spring framework, it may change with future versions. When using #PrepareEverythingForTest it results in an StackOverflow exception. The application is real life, not a small piece of code from some tutorial.
I also searched for something like ForceLazyFileSystemXmlApplicationContext but didn't find anything.
Pleas don't start nagging about bad design, I know about that.
You can write your own applicationContext for your testing purpose. There you need to write your own BeanFactory. In that factory you can replace some of beans with mocks.
I just remembered about an option. Evaluate if you can use spring profiles. It will allow to choose a different implementation based on profiles.
Example:
<!-- This is the default myBean -->
<beans>
<bean id="myBean" class="mypackage.MyBean" />
</beans>
<!-- This is the mocked myBean for testing purposes, it will take place when testingProfile is active -->
<beans profile="testingProfile">
<bean id="myBean" class="mypackage.MyBeanMock" />
</beans>
You can indicate which profile to use via properties or environment variables. Example, if you are using maven in your project you could run the tests as:
mvn test -Dspring.profiles.active="testingProfile"
Take a look at:
http://www.baeldung.com/spring-profiles
https://spring.io/blog/2011/02/11/spring-framework-3-1-m1-released/
My spring-boot application has another library project included as a dependency. This library project has a spring.xml file where a number of beans defined. One of these beans has another external dependency injected which I don't need in my project. Hence this is throwing an error when I start my application. I want to define the same bean in my application as a java config and make spring-boot ignore the specific bean from spring.xml file. However I want all the other beans in spring.xml to be read.
Define a bean in your local java config with the same name and type as the one inherited in the spring.xml file.
Annotate your bean with #Primary which will make yours used over the imported one.
Your application will still use the definitions of all the other beans you inherit.
To prevent other defined beans loading that you do not actually need you have to change the bean creation to lazy configuration, that is, they only get created when explicitly used.
To do this in your main Spring boot class where the application is created, most likely annotated with #SpringBootApplication/ #Configuration/ #EnableAutoConfiguration/ #ComponentScan you should add #Lazy above it.
Usually you would explicitly annotate the Bean in question but here it cannot be done as it is originating in a spring.xml file in a 3rd party jar. The idea here is to cleanly state all beans are lazy from the highest point in the spring configuration.
It seems that you want to include spring.xml from other jar and exclude a bean in the xml.
I don't know spring framework provides some way.
I prefer to copy the spring.xml file to new project and remove the unnecessary bean.
A colleague of mine and I are playing around with the following spring configuration:
<beans>
<context:property-placeholder location='classpath:/configuration.properties'/>
<bean id="myBean" class="${type}" />
</beans>
We want to be able to provide a environment specific implementation of myBean. On a developers system the value of type would be a lightweight implementation of whatever myBean does. And in a production environment we would use a full-blown version.
When my colleague runs the code, everything works. When I run the code, I get a ClassNotFoundException, because spring tries to instantiate ${type}.class. And it is not like it sometimes works and sometimes does not. On my machine it always fails and on my colleagues machine it always works.
Does anybody knows what the problem is?
Thx in advance,
Yevgeniy
UPDATE
as requested, here is how we load the application context:
ClassPathXmlApplicationContext("application-configuration.xml");
the content of the properties file is pretty simple:
type=foobar.TestServiceImpl
Instead of trying to override the class with a placeholder, I would like to suggest an alternative approach for your problem. You could use the Profile functionality of Spring.
It would be simplier and safer to change the class depending of the environment.
<beans>
<beans profile="dev">
<bean id="myBean" class="dev.impl.MyBean" />
</beans
<beans profile="prod">
<bean id="myBean" class="prod.impl.MyBean" />
</beans
</beans>
You can then activate a given profile in development by adding the following system property to your server -Dspring.profiles.active="dev".
You can define a default profile which will be used by adding the following to your web.xml:
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>prod</param-value>
</context-param>
The following assumes Spring 3.1+.
I can tell you this much for sure. For Spring to fail with a ClassNotFoundException for class
${type}
means that it did not resolve the placeholder.
When you specify
<context:property-placeholder location='classpath:/configuration.properties'/>
Spring uses a PropertyPlaceholderBeanDefinitionParser to register either a PropertySourcesPlaceholderConfigurer or a PropertyPlaceholderConfigurer bean which will do the placeholder resolution.
PropertySourcesPlaceholderConfigurer is a BeanFactoryPostProcessor. This mean that it can modify bean definitions. Without going into much detail, if it cannot resolve a placeholder, the process fails with an IllegalArgumentException that states that the placeholder could not be resolved.
If you're saying that the ${type} wasn't resolved, then no PropertySourcesPlaceholderConfigurer or PropertyPlaceholderConfigurer beans were created. This probably means your context does not have
<context:property-placeholder location='classpath:/configuration.properties'/>
With the information you've shown us, that is what I think is going on. If you can prove otherwise, I'll ask you to provide a small reproducible example. Ideally, you would show the contents of your compiled project.
As far as I remember property placeholder fails mutually. This means that if it cannot locate file configuration.properties on your machine the property is just not initialized.
To approve this assumption try to "break" the application on your colleague's machine: change the location to something wrong, e.g. classpath:/configuration12345.properties. I believe that the problem will appear on his machine too.
Now, check what's wrong in your environment and why this file cannot be found there.
BUT: do you have something against profiles? Spring provides a cool feature that is attended exactly for your use-case: spring profiles.
Is there any way to enable or disable a java bean definition in application context?
<bean id="enBean" classs="com.en.bean.BeanName">
<property name="prop1"/>
</bean>
Or, is there any way to load the bean conditionally defined in application context?
There is a new feature #Profile in spring 3.1 that would do the job
From here
Spring 3.1 introduces the concept of environment profiles. A common
use case is the setting up of beans that are different between
development, QA and production environments. A typical example is
going against a standalone DataSource in development versus looking up
the DataSource from JNDI in production. Another example is a beans
profile for profiling that can easily be turned on or off. You can add
a profile attribute on a beans element in XML or add #Profile
annotation in code. Note that a Spring bean can be assigned to
multiple profiles.
<beans profile="dev">
...
</beans>
#Profile("dev")
public class Bean {
...
}
These profiles can be activated through the spring.profiles.active
property which may be specified through an environment variable, a JVM
system property, a Servlet in web.xml or JNDI. These
profiles can also be activated through code using
Environment.setActiveProfiles(String ...). To make bean profiles work,
nested beans elements are now allowed in the Spring XML, although
constrained only at the end of the file. Note that it's recommended to
keep your bean topology as close as possible between environments, so
your application gets properly tested across environments. You also
use the Environment.containsProperty() method to search for properties
across the different property sources. This property resolution also
works for ${placeholder} variables in XML bean definitions.