November 24, 2014
Hot Topics:

Wiring Java Applications with Spring

  • May 12, 2005
  • By Michael Klaene
  • Send Email »
  • More Articles »

As mentioned, interfaces help to keep your design flexible. You may use JDBC today, and later move to an object-relational mapping (ORM) tool later. Likewise, you may use a different implementation approach for your facade. The impact of such changes to your system should be minimal. So, a common problem is how can you designate your implementation for the interfaces you have defined? I mentioned commonly used options at the beginning of this article, with JNDI often playing a role. With JNDI, you can specify an implementation object's name in a <resource-ref> in a descriptor file. This way, implementation changes should not require code changes. Here's an example of what looking up a DAO implementation via JNDI might look like:

try {
   Context initContext = new InitialContext();
   Context envContext  = (Context)initContext.lookup("java:/comp/env");
   SalesManagerDAO sm  =
      (SalesManagerDAO)envContext.lookup("test/salesManagerJdbcDAO");
}
catch (Exception e) { 
  throw new Exception(e); 
}

You could put this type of code in a ServiceLocator class to avoid repeating yourself every time you need an object. By doing this, you are gaining some flexibility, but you are still required to write code to obtain (as opposed to having the container retrieve it automatically for you) the instance. Also, this type of code tends to be a bit verbose. By using an ApplicationContext for the same purpose, the code would look something like this:

ApplicationContext ctx =
   new ApplicationContext(new FileInputStream("application-context.xml");
   SalesManagerDAO sm = (SalesManagerDAO)ctx.getBean
                        ("salesManagerJdbcDAO");

File applicationContext.xml is the Spring configuration file that feeds an ApplicationContext its dependency information. In the preceding example, you created an instance of the Application Context to retrieve a SalesManagerDAO bean. This is not something you would normally do because Spring provides utilities to enable you to retrieve an existing context in your code. You will look at this later. For now, move on to some ApplicationContext examples.

ApplicationContext Examples

To review, you have two domain objects: SalesRep and Client, as well as two singleton objects that implement the Facade and DAO design patterns. In applicationContext.xml, here are the entries for your facade and DAO objects:

   <!-- facade implementation -->
   <bean id="salesManager" class="examples.SalesManagerDefaultImpl">
      <!--dao member of facade-->
         <constructor-arg><ref bean="salesManagerDAO"/>
         </constructor-arg>
   </bean>

   <!--dao implementation -->
   <bean id="salesManagerDAO" class="examples.SalesManagerJdbcDAO">

      <!--dao implementation type -->
      <property name="implType" value="JDBC"/>

         <!--our Map property-->
         <property name="dbProperties">
            <map>
               <entry key="driverClass">
                  <value>org.hsqldb.jdbcDriver</value>
               </entry>
               <entry key="url">
                  <value>jdbc:hsqldb:hsql://localhost:9002</value>
               </entry>
               <entry key="username">
                  <value>sa</value>
               </entry>
               <entry key="password">
                  <value><null/></value>
               </entry>
            </map>
         </property>
   </bean>

This markup results in the creation of facade and DAO instances when the Spring ApplicationContext is created at runtime. By default, classes are created as singletons, meaning there is a single instance shared by all users. There is an attribute called 'singleton' that could have been explicitly set to "true", but it's not necessary. For each bean, properties can have initial values assigned via the configuration file, as is the case for property implType. Spring knows how to convert values specified in the markup for basic types like int, float, String, and so on. The <null/> element can be used to give an object an initial value of null.

You also can create your own custom PropertyEditors to convert initial values, specified as text in the configuration file, for more complex objects. Spring gives you the ability to initialize Java Collections as well. In the above markup, you are populating a Map, called dbProperties, with data. It should be noted that Spring gives you a more flexible alternative than what is shown here for specifying properties by reading an external properties file.

A Spring ApplicationContext can assign a bean a dependency using another object in the same file. Dependent objects defined elsewhere in the document are referenced with the <ref-bean> tag. In this example, an instance of SalesManagerJdbcDAO is created. Then, an instance of SalesManagerDefaultImpl is created and has its salesManagerDAO property assigned the SalesManagerJdbcDAO instance (<ref bean="salesManagerDAO"/>).

Essentially, there are two ways to 'inject' dependencies in Spring: through a constructor or a setter method. These mechanisms are often termed 'constructor-based dependency injection' and 'setter-based dependency injection' respectively. The setter approach seems to be the more popular as the latter can result in ugly and complex constructors. But both are equally viable options and I use both examples here. The facade class you are using is assigned a DAO in its constructor, whereas the DAO bean has properties that are set via setter methods:

//**Facade instance**
//Constructor for constructor-based dependency injection 
public setSalesManagerDAO(SalesManagerDAO salesManagerDAO) {
   this.salesManagerDAO = salesManagerDAO;
}


//**DAO instance**
//Setter Methods for setter-based dependency injection 
public void setImplType(String implType) {
   this.ImplType = implType;
}
public void setDbProperties(Map dbProperties) {
   this.dbProperties = dbProperties;
}

There are a number of global attributes that you can set in your configuration file that will apply to all beans (and then can be overridden by individual beans). Often, these properties are simply left to their defaults, and thus not specified at all. First among these is the 'dependency-check' element that will force Spring to ensure all dependencies are set when beans are created. Another global attribute is 'lazy-init', which you can use if you do not actually want create an instance of a bean, keeping it 'abstract'. Finally, there is an attribute called 'autowire'. Autowire tells Spring to attempt to resolve dependencies on its own, without specific <ref-bean> directives. There are different ways to accomplish this. For example, if auto wiring by type, if object A has a property of type Object B, and Spring has an object B listed in its configuration file, it will assume that this is the object that A needs. Autowire is not a recommended feature because it may help eliminate some code but can cause real issues for all but trivial applications.

The remainder of the applicationContext configuration file creates domain objects.

<!-- a Client instance-->
<bean id="client" class="examples.Client" singleton="false"/>

<!-- a SalesRep instance-->
<bean id="salesRep" class="examples.SalesRep" singleton="false"/>

Here, you create a SalesRep and Client bean. Their singleton attributes are specified to "false"; this means that every time you request a bean of that type, you are effectively calling ' new() ' on that object. Now that you've wired your dependencies, you can use your ApplicationContext as a 'registry' to retrieve application objects as needed:

SalesManagerFacade sm = (SalesManagerFacade)ctx.getBean("salesManager");

SalesRep salesRepBob  = (SalesRep)ctx.getBean("salesRep");
Client clientJoe      = (Client)ctx.getBean("client");

sm.assignClientToSalesRep(salesRepBob,clientJoe);

The assignClientToSalesRep facade method will in turn invoke a DAO method on the SalesManagerDAO instance that the ApplicationContext assigned to it. In practice, there is less value in having Spring manage domain objects like SalesRep and Client (and most developers opt not to), but it can be useful in some situations. For instance, it would give you the opportunity to set some default values for instance variables.





Page 2 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel