Tuesday, August 14, 2012

Using @Configurable to inject resources into objects created using "new" operator (domain objects)

I am currently developing a query language based on a context-free grammar. The library contains a parser that parses the queries accepted by the language and returns a tree of parse entries. So, these parse entries essentially form the domain objects of the application. The library also provides the evaluator framework to evaluate the state of an object against a parse tree. A parse entry contains a reference to the evaluator that should be used to evaluate the state of an object against itself.
Since the parse entries are created using the new operator during the parse process and since it is not a good idea to hard-wire the evaluators into the parse entry classes, I was looking for a way to inject them. Then I came across the @Configurable annotation provided by Spring framework to inject beans into objects of a class whose objects are instantiated using the new operator instead of getting them from the Spring framework. I followed the Load Time Weaving approach to get this working, in a sample project, as follows:
  1. Configure the maven repositories required:
        
        
            com.springsource.repository.bundles.external
            SpringSource Enterprise Bundle Repository - External Bundle Releases
            http://repository.springsource.com/maven/bundles/external
        
    
  2. Configure the maven dependencies required:
    
        
            org.springframework
            spring-aspects
            3.1.1.RELEASE
        
        
            org.springframework
            spring-context
            3.1.1.RELEASE
        
        
            org.springframework
            spring-test
            3.1.1.RELEASE
        
        
            org.aspectj
            com.springsource.org.aspectj.weaver
            1.6.12.RELEASE
        
        
            junit
            junit
            4.10
        
    
    
    
  3. Created a sample domain object--Account.java, mark it as Configurable, and add a reference to the yet to be created AccountDAO.
    @Configurable
    public class Account
    {
        protected AccountDAO accountDAO;
    
        private int accountId;
        private String accountHolderName;
        private double balance;
    
        public Account(int accountId, String holderName, double balance)
        {
            this.accountId = accountId;
            this.accountHolderName = holderName;
            this.balance = balance;
        }
    
        public AccountDAO getAccountDAO()
        {
            return accountDAO;
        }
    
        /**
         * @return the accountId
         */
        public int getAccountId()
        {
            return accountId;
        }
    
        /**
         * @return the accountHolderName
         */
        public String getAccountHolderName()
        {
            return accountHolderName;
        }
    
        /**
         * @return the balance
         */
        public double getBalance()
        {
            return balance;
        }
    
        public void setAccountDAO(AccountDAO accountDAO)
        {
            this.accountDAO = accountDAO;
        }
    
        /**
         * @param accountId the accountId to set
         */
        protected void setAccountId(int accountId)
        {
            this.accountId = accountId;
        }
    
        /**
         * @param accountHolderName the accountHolderName to set
         */
        protected void setAccountHolderName(String accountHolderName)
        {
            this.accountHolderName = accountHolderName;
        }
    
        /**
         * @param balance the balance to set
         */
        protected void setBalance(double balance)
        {
            this.balance = balance;
        }
    
        public void persist()
        {
    
        }
    }
    
    
    The setter method for accountDAO must be public for Spring to find it and inject it(I found this strange as I thought the reflection based frameworks can access protected methods as well, which is the case with Hibernate.)
  4. Create the DAO and sample DAOImpl for the Account class.
    public interface AccountDAO
    {
        public Account createAccount(Account account);
        
        public Account updateAccount(Account account);
        
        public void delete(Account account);
    }
    
    public class AccountDAOImpl implements AccountDAO
    {
        public Account createAccount(Account account)
        {
    
            System.out.println("Created....");
            return account;
        }
    
        public Account updateAccount(Account account)
        {
            System.out.println("Update....");
            return account;
        }
    
        public void delete(Account account)
        {
            System.out.println("Deleted....");
        }
    }
    
  5. The application-config.xml file:
    
    
        
        
        
    
    
  6. Write a sample test class TestAccount
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations =
    { "file:META-INF/application-config.xml" })
    public class TestAccount
    {
    
        @Test
        public void test()
        {
            Account account = new Account(-1, "Krovi Sarma", 1630000.00);
            assertNotNull(account.getAccountDAO());
        }
    
    }
    
  7. Run the test class with the JVM argument -javaagent:/path/to/org.springframework.instrument.jar

2 comments:

  1. I am trying this as well but I keep getting a failure in the assertion. Can you please post your application-config.xml for completeness?

    ReplyDelete
    Replies
    1. Updated the post with the application-config.xml

      Delete