0

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.

Celinio Fernandes
  • 163
  • 1
  • 4
  • 12
  • anyway you could define a component scan context for the bean instead? That way you could rely on the conditionalonproperty annotation. https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/condition/ConditionalOnProperty.html – Taugenichts Feb 05 '20 at 19:22
  • I know about the ConditionalOnProperty annotation but we have no intention to migrate that XML file to an annotated bean for now. Does anyone have another idea ? Thanks – Celinio Fernandes Feb 05 '20 at 19:32

1 Answers1

0

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

Community
  • 1
  • 1
Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • Thanks but that will not do for 2 main reasons : 1) that would duplicate files. And there are about 30 XML files. 2) the properties component1 and component2 are essential. There are some beans that are instantiated based on the environment (dev, prod ...) and there are other beans that are instantiated based on the values of other properties located in application.properties (such as component1 and component2). – Celinio Fernandes Feb 05 '20 at 21:08
  • @CelinioFernandes, noted - please see another option (Update 1). – Mark Bramnik Feb 06 '20 at 05:28