I'm creating the following bean using xml config:
<int-ip:tcp-outbound-gateway id="gate"
request-channel="input"
reply-channel="clientBytes2StringChannel"
connection-factory="factory"/>
How can I configure this based on a condition, eg a value included in application.properties?
I spring 4 I could use #ConditionalOnExpression("SpEL"), but how could I achieve the same in xml?
You can use Spring Profiles to conditionally create beans. For e.g.
<beans profile="dev">
<bean id="devDatasourceConfig" class="" />
</beans>
<beans profile="production">
<bean id="productionDatasourceConfig" class=" " />
</beans>
You can activate profiles as follows:
JVM system parameter:
-Dspring.profiles.active=dev
or
Environment variable:
export spring_profiles_active=dev
Related
I am using Spring Boot 2.1.
I have some mixed configuration in my project : XML files and java classes with annotations.
We have this current configuration which works :
application.properties :
spring.profiles.active=dev, component1, component2
applicationContext-file.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" 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"
profile="component1">
<beans>
<bean id="myserviceimpl"
class="org.blabla.MyServiceImpl">
<property name="mydao">
<ref bean="mydao"></ref>
</property>
</bean>
</beans>
</beans>
We want to extract the component values from the spring.profiles.active property since they have nothing to do with the environment :
application.properties :
spring.profiles.active=dev
component1=true
component2=true
How can i condition the instantiation of the myserviceimpl bean inside the applicationContext-file.xml ?
I can no longer rely on the profile attribute since the spring.profiles.active property no longer includes
the values of the components.
Thanks for helping.
I haven't tried it by myself but I suggest to check the following:
Step 1
Create one XML file per profile (say, dev and prod for the sake of example):
app-config-dev.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" 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"
profile="dev">
<beans>
<!-- here define only beans that you want to load in __dev__ environment -->
<bean id="myserviceimpl"
class="org.blabla.MyServiceImpl">
<property name="mydao">
<ref bean="mydao"></ref>
</property>
</bean>
</beans>
</beans>
app-config-prod.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" 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"
profile="prod">
<beans>
<!-- here define only beans that you want to load in __production__ environment -->
<bean id="myserviceimpl"
class="org.blabla.MyServiceProdImpl">
<property name="mydao">
<ref bean="mydao"></ref>
</property>
</bean>
</beans>
</beans>
Step 2
Now in your "primary" applicationContext-file.xml instead of defining beans directly, include all the xml files that you've created during the step 1:
<import resource="classpath:app-config-dev.xml" />
<import resource="classpath:app-config-prod.xml" />
Read this thread for more details on this step if needed
Step 3
Remove the component1=true and component2=true from aplication properties, you don't need it anymore, the profile itself defines which beans should be loaded.
Update 1
Based OP's first comment:
You probably can try another option but I consider it a "low-level" solution and it requires deep understanding of how does spring loading process work under the hood.
So when spring starts it finds the definitions of beans (in xml, java config, annotations like #Component and so forth) and creates out of all this information a BeanDefinition - a metadata object that aggregates all the information about the bean (for example whether its singleton or prototype). At this point no beans are not created yet. Lets call this point in time a "Bean Definitions Done" point" (for the sake of explanations, its my term out of my head...
Then spring starts to build a graph of dependencies and starts creating beans, initializing them (Autowiring, post-construct methods, and so on) and placing them onto application context (if they're singletons).
Now, in spring there is an option to provide a hook called BeanFactoryPostProcessor that is invoked exactly at the "Bean Definitions Done" point. This is basically a java code that implements some interface and by itself is a spring bean that spring treats in a special way.
So you can implement this interface and manipulate the results of Bean factory.
Namely you can access every bean definition that spring has opened and if you think that the bean of this definition should not be created (here comes your custom logic that would check properties, whatever) - remove the bean definition from the registry:
For technical method of removing bean definitions see this thread
Here is an example of Bean Factory Post processor that actually adds a new bean although it wasn't registered
I was wondering if it's possible to somehow conditionally include spring beans depending on some property.
In my applicationContext.xml I have a list of beans that I setup:
<bean id="server1Config" class="... />
<bean id="server2Config" class="... />
<bean id="server3Config" class="... />
...
Then I include them in a list:
<bean class="java.util.ArrayList">
<constructor-arg>
<list>
<ref bean="server1Config"/>
<ref bean="server2Config"/>
<ref bean="server3Config"/>
...
</list>
</constructor-arg>
</bean>
I want to conditionally include server1Config, server2Config, server3Config, etc depending on whether ${includeServer1} == true, ${includeServer2} == true etc and if possible, only initialize those beans if they are marked for inclusion.
To clarify, it's a ping service checking if servers are online or not, each bean contains special urls. If I have 5 servers running, I'd like to set in my config includeServer1=true ... includeServer5=true ... includeServer6=false, if I shutdown server2, I'd like to change includeServer2=false before shutting down the server to not get bombarded with SMSe telling me server2 is offline.
As your names refer to different stages or enviroments, spring profiles might be helpful to use. You can define beans like this inside your context.xml
<beans profile="dev">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
In the example [1] you see the usage of two profiles called "dev" and "production".
Spring will always load every bean without a profile
Depending on the profiles (yes, you can load multiple profiles at once) all the related beans will be loaded
Loading a profile in Java:
ctx.getEnvironment().setActiveProfiles("dev");
Loading two profiles
ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
Loading from CMD Line declaratively:
-Dspring.profiles.active="profile1,profile2"
Usage in web.xml (can be comma-separated)
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>spring.profiles.active</param-name>
<param-value>production</param-value>
</init-param>
</servlet>
#Comment: If you want to do it using properties and you are able to use newer spring elements, annotations etc. please have a look at this tutorial [2] to make it work with properties file as you commented below.
[1] http://spring.io/blog/2011/02/11/spring-framework-3-1-m1-released/
[2] http://kielczewski.eu/2013/11/setting-active-profile-and-property-sources-in-spring-mvc/
This is almost an add-on to #swinkler's answer.
He gave the first part of the solution which is usage of Spring 3.1+ profiles.
The second part would be to use a kind of automatic registration :
<bean class="java.util.ArrayList" id="serverConfigList"/>
<beans profile="server1">
<bean id="server1Config" class="... />
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject"><ref local="serverConfigList"/></property>
<property name="targetMethod"><value>add</value></property>
<property name="arguments"><ref local="server1Config/></property>
</bean>
</beans>
That way you create an empy list and only add relevant configs to it.
Your code shouldn't change based on environment. If your aim is to use different settings for each environment then load them as properties at start up.
Refer this for 'external configuration'
This can be done in Spring framework 3.1 onwards using a built in Spring environment profiles.
Here's a few resources:
http://java.dzone.com/articles/spring-31-environment-profiles
Hope this helps.
I'm having a problem with RESTlet Framework configuration with Spring. I want to have one global filter for all requests and responses. I guess I can use Filter class and it's methods beforeHandle, afterHandle and setNext like this:
Filter.beforeHandle() -> Router -> Filter.afterHandle()
The problem is, that I'm using Spring configured RESTlet and I don't know if the regular Filter will work correctly with SpringRouter from org.restlet.ext.spring package. My current restlet configuration is as follows:
<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="root" class="org.restlet.ext.spring.SpringRouter">
<property name="attachments">
<map>
<entry key="/login">
<bean class="org.restlet.ext.spring.SpringFinder">
<lookup-method name="create"
bean="loginResource" />
</bean>
</entry>
</map>
</property>
</bean>
</beans>
I'm thinking about adding a bean with id root and class that extends class Filter, and pass to it as property next a bean with id router (which currently is called root). What do you think about this solution?
Mixing Restlet classes from the Spring extension with other one shouldn't be an issue. Spring* classes only provide additional configurability.
I'd like to know, using Spring XML profiles, if it is possible to declare Spring beans for all profiles, except the dev profile.
My usecase is that I would like to declare my JDBC datasource as being JNDI provided for profile "dev", while it is created for other profiles.
I'd like to have something like:
<beans profile="dev" >
... DataSource JNDI ...
</beans>
<beans profile="!dev" >
... DataSource creation ...
</beans>
Is there a solution? Or the only way is to do:
<beans profile="dev" >
... DataSource JNDI ...
</beans>
<beans profile="integration,valid,preprod,prod" >
... DataSource creation ...
</beans>
Thanks
You can use !, for spring version higher than Spring 3.2 M1.
So this way is ok:
<beans profile="!dev" >
... DataSource creation ...
</beans>
A simpler way would be like this:
General datasource creation for any profile:
<bean name="datasource" ...>
then specific for dev:
<beans profile="dev" >
<bean name="datasource" ...>
</beans>
In spring, how would I go about loading an XML file once such that I can reference the file in my controller actions and not have to load it up again and again.
Create a bean whose sole purpose is to read your XML file and has the desired accessor methods, and inject it into your controller.
In Spring , all beans are initialized as Singleton unless specified . You could inject the properties from your XML file as follows
The xml file format for the properties is as below.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="key1">Value 1</entry>
<entry key="key2">Value 2</entry>
</properties>
you can use
<context:property-placeholder
location="classpath:/com/myProject/spring_prop.xml" />
<bean id="bean" class="org.MyBean">
<property name="key1" value="${key1}" />
</bean>
Or read the XML files in an #PostConstruct method via a bean as #Jens has mentioned .