Chapter 26. xml-di-best-practices (optional)

26.1. xml-di-best-practices (optional): XML Dependency Injection Best Practices

26.2. Introduction

What you will learn:

  1. Techniques for reducing the amount of Spring configuration code
  2. How to import XML namespaces
  3. How to apply custom configuration behaviors to objects created by Spring

Specific subjects you will gain experience with:

  1. Bean Definition Inheritance
  2. Importing Configuration Files

Estimated time to complete: 30 minutes

26.3. Quick Instructions

Quick instructions for this exercise have been embedded within the lab materials in the form of TODO comments. To display them, open the Tasks view (Window >> Show View >> Tasks (not Task List)). Use the view’s small down arrow to select a Configure Contents…​ menu, you’ll find the instructions are easy to follow if you configure TODOs to display On any element in the same project.

Occasionally, TODO’S defined within XML files disappear from the Tasks view (i.e. gaps in the number sequence). To correct this, go to Preferences >> General >> Editors >> Structured Text Editor >> Task Tags pane. Check Enable searching for Task Tags and click Clean and Redetect Tasks. On the Filters tab, ensure XML content type is checked.

26.4. Detailed Instructions

26.4.1. Using Bean Definition Inheritance to reduce Configuration

Spring provides several features that help you reduce the amount of application configuration code. In this section you’ll gain experience with one of them called bean definition inheritance.

(TODO 01) Inside the rewards package, open the test class called RewardNetworkTests. As you can see Spring is being configured by using TestInfrastructureConfig in the same package.

Open TestInfrastructureConfig.java and you will see an empty @ImportResources annotation. We will use this to load our two XML configuration files: test-infrastructure-config.xml and application-config.xml. Modify it now.

[Note] Note

You can use CTRL-R in STS to locate resources like XML files (use COMMAND-R on a Mac).

Keep in mind that you should include the file paths starting from inside the classpath root folders (src/test/resources and src/main/resources are part of the classpath). And yes, these are in different locations to the first XML lab. When done, run RewardNetworkTests. It will pass if the file paths are correct.

26.4.2. Define the abstractJdbcRepository bean

Bean definition inheritance is useful when you have several beans that should be configured the same way. It lets you define the shared configuration once, then have each bean inherits from it. In the rewards application, there is a case where bean definition inheritance makes sense. Recall there are three JDBC-based repositories, and each repository needs the same dataSource.

(TODO 02) Inside src/main/java within the rewards.internal package, open application-config.xml. Note how the property tag instructing Spring to set the dataSource is currently duplicated for each repository.

Now in application-config.xml, create an abstract bean named abstractJdbcRepository that centralizes the dataSource configuration. You will not need to define the class for this bean, but you should define the dataSource property and set it with a reference to the dataSource bean.

Next, update each repository bean so it extends from your abstractJdbcRepository bean definition. The repository beans will no longer need to set their own dataSource properties since this is now defined by the abstract bean definition.

Re-run RewardNetworkTests. It should still pass.

26.4.3. Externalizing values to a Properties file

(TODO 03) In this section, you’ll gain experience with using the <context:property-placeholder>, element. Specifically, you will move the configuration of your embedded-database from test-infrastructure-config.xml into a .properties file, then declare a <context:property-placeholder> element to apply the configuration. By doing this, you’ll make it easier for administrators to safely change your configuration.

Create a file named application.properties in the root of the classpath. Add the following properties:

schemaLocation=classpath:rewards/testdb/schema.sql
testDataLocation=classpath:rewards/testdb/data.sql

Notice how these values match the current script values of the embedded dataSource in test-infrastructure-config.xml.

Within test-infrastructure-config.xml, replace each property value configured for your embedded-database with a placeholder. The placeholder name should match the respective property name in your properties file. The placeholders follow the syntax ${placeholder}

If you run the RewardNetworkTests at this point it will fail. One more step left to complete…​

In test-infrastructure-config.xml, declare an instance of the <context:property-placeholder> element. Set its location attribute to point to your properties file. Remember that this configuration will be automatically detected by Spring and called before any other bean is created. No other configuration is necessary.

Now re-run your RewardNetworkTests, it should pass.

[Tip] Tip

Even if you get green on your first attempt, try experimenting with some failure scenarios. For example, try misspelling a placeholder, property name, or property value and see what happens.

26.4.4. Using the <import/> tag to combine configuration fragments

(TODO 04) Using the <import/> tag is often a good idea when working with multiple configuration files. Return to RewardNetworkTests. Note how all the configuration files required to run the system test are listed in this file. Now suppose you added another configuration file. You would have to update your test code to accommodate this change.

The import tag allows you to create a single 'master' configuration file for each environment that imports everything else. This technique can simplify the code needed to bootstrap your application and better insulate you from changes in your application configuration structure.

Open test-infrastructure-config.xml and add an <import/> tag to import application-config.xml. Within RewardNetworkTests, remove the reference to application-config.xml from the array of configuration files. Rerun the test, it should pass.

26.4.5. Using the <jdbc/> namespace

(TODO 05) The declaration of the embedded database in test-infrastructure-config.xml can be simplified by using the <jdbc/> namespace.

Make the change and rerun RewardNetworkTests, it should still pass.