What you will learn:
Specific subjects you will gain experience with:
Estimated time to complete: 30 minutes
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.
(TODO-01) The project features an integration test that verifies
the system’s behavior. It’s called RewardNetworkTests
and lives in the rewards
package. Run this test by
right-clicking on it and selecting 'Run As…' followed
by 'JUnit Test'. The test should run successfully.
This test illustrates a great advantage of using automated tests to verify that the refactoring our application is successful. We will run this test again after we make changes to the application to verify that our system functions the same as it did originally.
(TODO 02) Open the rewards-config.xml file located in the src/main/resources/config
folder. This will
serve as our main application configuration file, and will replace
the configuration instructions currently in the RewardsConfig.java
class.
Our first step will be to add the context namespace to this configuration file. This can be done manually via copy / paste, but STS provides a quicker alternative. On the bottom of the editor you should see a "Namespaces" tab. Within this tab you will see a set of checkboxes, each one represents a namespace that you can add to the XML root element. Check the "context" box (you may be prompted that this will add an element to your configuration file, which is exactly what we want, so click OK). Return to the "Source" tab and note that the XML namespace "context" has been added to your XML root element. This means you can now take advantage of the context: namespace.
(TODO 03) Now that we have added the context: namespace, we can
add the element to do component scanning. If you enter "context:"
and press CTRL+Space
, the editor will
prompt you for the possible entries in the context namespace that
can be used. Select "context:component-scan".
Within the component-scan tag it is important to set the
"base-package" element; this tells Spring which packages and
sub-packages should be included in the scanning process. If you
look at the RewardsConfig.java
class,
you can see the value presently used by the JavaConfiguration:
"rewards". Use this same value.
(TODO 04) At this point, we have an XML configuration file
equivalent of our RewardsConfig.java
class, and we also have a test-infrastructure-config.xml
that is
already prepared for us. Our test class is still coded to load the
TestInfrastructureConfig.java
Java
configuration class. We need to change this configuration class to
import our XML files.
Open TestInfrastructureConfig.java
.
Remove/comment out the existing @Import
annotation and the entire dataSource()
bean method. Instead add an
@ImportResource
annotation, and pass
it an array of Strings indicating the XML configuration files to
load. The first is the file you just modified, rewards-config.xml
located in the src/main/resources/config
folder. The second
is an existing XML file already prepared for you, test-infrastructure-config.xml
located in
the src/test/resources/rewards
folder.
Note that both of these are classpath resources, so @ImportResource
should find these as long as
we indicate the correct folder locations.
Once you have finished this modification, save all your work and
rerun the RewardNetworkTests
. The test
should pass at this point. If it does not, take a look at the test
output to see if you can determine why. The most likely issue is
the file/path literals of the configuration files.
At this point, we are using Annotation-based configuration (via
component scanning) to define the application components
(RewardNetwork and the three repositories) and XML configuration to
define the DataSource. In this next section, we will demonstrate
how to use a 100% XML configuration. Return to rewards-config.xml
and perform the
following.
(TODO 05) Define a bean element to instantiate the JdbcAccountRepository
. It is good practice
to give beans an ID using the "id" attribute, so give this bean the
ID "accountRepository", or any other ID you like. Use the "class"
attribute to specify the the fully-qualified classname of what we
want to instantiate, the JdbcAccountRepository. STS provides a
great feature here to quickly determine the packaging: within the
class attribute value type "JAR" (all caps) and press CTRL+Space
. STS will prompt you with all
known classes that match the camel-case pattern. Simply choose
JdbcAccountRepository from the list.
Next, within the bean element start and end tags, place a property sub-element to set the dataSource property. The autocomplete feature is very useful here, using it you can discover that the "name" of the property we want to set is called "dataSource". We want to set this to a "ref" to another bean named "dataSource". Note that this other bean is defined elsewhere, so in this case the autocomplete feature can’t help us. Also note that the editor may give you a warning that this bean is unknown for the same reason; you can safely ignore this warning for now.
(TODO 06) Define a bean element to instantiate the JdbcRestaurantRepository
. The procedure is
exactly the same as the last step, except you should select a
different ID value (suggest: "restaurantRepository") and the
fully-qualified classname.
You may remember that this class has a special method within it
that must be called at startup time in order to pre-populate its
cache. The method is named populateRestaurantCache
and you should use
the init-method
attribute to specify
it.
(TODO 07) Define a bean element to instantiate the JdbcRewardRepository
. The procedure is
exactly the same as the the previous two steps, except a different
ID should be used (suggest: "rewardRepository") and the
fully-qualified classname should be different. Note there is no
need for any init-method on this bean.
(TODO 08) Define a bean element to instantiate the RewardNetworkImpl
. The ID for this bean
should be "rewardNetwork" to allow our existing test code to work.
This bean has three constructor arguments that must be populated:
an AccountRepository, a RestaurantRepository, and a
RewardRepository. These happen to be the beans defined in the
previous three steps so use the constructor-arg sub-elements with
ref attributes to specify these dependencies.
Now that we have defined XML bean definitions for our beans, we can remove the annotations on the classes themselves:
(TODO 09) Open RewardNetworkImpl
and remove the @Service
and @Autowired
annotations.
(TODO 10) Open JdbcAccountRepository
and remove the @Repository
and @Autowired
annotations.
(TODO 11) Open JdbcRestaurantRepository
and remove the
@Repository
and @Autowired
annotations.
(TODO 12) While in the JdbcRestaurantRepository
remove the @PostConstruct
annotation from the populateRestaurantCache
method. Our XML
configuration instructions will ensure that this method is called
during startup.
(TODO 13) Open JdbcRewardRepository
and remove the @Repository
and @Autowired
annotations.
At this point, we have removed all of the annotation-based
configuration. Save all your work, and re-run the RewardNetworkTests
. It should pass - Spring
is now using XML-based bean definitions. Congratulations, you have
completed this lab.
(TODO 14) Now that we are using XML configuration and have
removed all the stereotype and DI annotations, is there any reason
for the component-scanning element to remain? Remove this element
and rerun the test, It should pass. You can also experiment with
removing the RewardsConfig
class since
it is no longer used.