Announcement Announcement Module
Collapse
No announcement yet.
Concerning javaConfig and afterPropertiesSet() in Spring 3.1 Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Concerning javaConfig and afterPropertiesSet() in Spring 3.1

    Hi,

    I just started a new project and gave JavaConfig a try. Maybe that's just my impression, but it seems that I found helpful information on how to do the setup mostly in blogs and not in the spring documentation.

    One thing I found to be particularly annoying though was setting up JNDI. In xml, the configuration looked like this:
    Code:
    <bean id="dataSource"
          class="org.springframework.jndi.JndiObjectFactoryBean">
         <property name="jndiName">
             <value>java:comp/env/jdbc/appDataSource</value>
         </property>
    </bean>
    Thus I assumed, that the corresponding JavaConfig would be this:
    Code:
    @Bean
    public DataSource dataSource() {
       JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
       jndiObjectFactoryBean.setJndiName("java:comp/env/jdbc/appDataSource");
    }
    Unfortunately, this did not work - I only got NPEs when the datasource was injected. After googling some more, I found the advice to call afterPropertiesSet(), so I ended up with:
    Code:
    @Bean
    public DataSource dataSource() {
        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiName("java:comp/env/jdbc/appDataSource");
        try {
            jndiObjectFactoryBean.afterPropertiesSet();
        } catch (Exception ex) {
            throw new RuntimeException("JNDI DS lookup failed!");
        }
        return (DataSource) jndiObjectFactoryBean.getObject();
    }
    So it appears to me, that even though JndiObjectFactoryBean implements InitializingBean, afterPropertiesSet() is not called automatically, but that I have to do it manually. Is this true? If so, what is the recommended approach for figuring out, when one needs to call it and when it can be omitted?

    One last questions about exception handling: Is there a best practice on how to handle them? Local try/catch or "throws Exception" on every @Bean method?

    Regards,
    Tom

  • #2
    One last questions about exception handling: Is there a best practice on how to handle them? Local try/catch or "throws Exception" on every @Bean method?
    You shouldn't catch exceptions as they are basically bean definitions and if the context cannot start it needs to be visible so let the spring container handle it.

    So it appears to me, that even though JndiObjectFactoryBean implements InitializingBean, afterPropertiesSet() is not called automatically, but that I have to do it manually. Is this true? If so, what is the recommended approach for figuring out, when one needs to call it and when it can be omitted?
    You return a DataSource not a factory bean so spring knows nothing about the factory bean (or whatever the actual bean was) and as such doesn't know anything about the interfaces, annotations on that bean.

    Simply autowire your beans into your configuration instead of messing around with getObject etc. yourself...

    Code:
    @Configuration
    public class MyConfiguration {
    
      @Autowired
      private DataSource dataSource
    
      @Bean
      public FactoryBean dataSource() {
        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiName("java:comp/env/jdbc/appDataSource");    
        return jndiObjectFactoryBean;
      }
    
      @Bean 
      public MyDao myDao() {
        return new MyDao(dataSource);
      }
    
    }
    This will let spring call the factorybean and manage the lifecycle, the datasource will be injected (you could move the configuration needing the datasource to another bean) and you can simply use it.

    Comment


    • #3
      Hi Marten,

      thanks for your reply. Your suggestions make a lot of sense and appear to be working. However I am still a bit unhappy. I started using Spring when version 1.2.4 was available and have always been a fan of explicit XML configuration. Thus, using @Autowired does not really make me happy. I've looked more closely at the Spring documentation (http://static.springsource.org/sprin...tml#beans-java), more specifically at this:
      Code:
      @Configuration
      public class ServiceConfig {
        private @Autowired RepositoryConfig repositoryConfig;
      
        public @Bean TransferService transferService() {
            // navigate 'through' the config class to the @Bean method!
            return new TransferServiceImpl(repositoryConfig.accountRepository());
        }
      }
      Here I still have to use the dreaded @Autowired for the RepositoryConfig , but then I can controll the injection again, which is nice. Unfortunately I've run into the next problem then: FactoryBeans. In XML I can do the following
      Code:
      <bean   id="dataSource"
                  class="org.springframework.jndi.JndiObjectFactoryBean">
          <property name="jndiName">
                  <value>${app.datasource.jndiName}</value>
          </property>
      </bean>
      
      <bean   id="transactionManager"
                  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
          <property name="dataSource" ref="dataSource"/>
      </bean>
      Even though the datasource bean is a FactoryBean, I can inject it into the TransactionManager and spring calls getObject() internally. How can I achieve the same with JavaConfig, without using @Autowired?

      Regards,
      Tom

      Comment


      • #4
        Even though the datasource bean is a FactoryBean, I can inject it into the TransactionManager and spring calls getObject() internally. How can I achieve the same with JavaConfig, without using @Autowired?
        You cannot... Unless you call the afterPropertiesSet method etc. yourself... This isn't something spring can help you with as that is the way java (methods) work.

        Also there is nothing with holding you from still using XML over Java or Annotations. I personally still prefer XML for the main part of my applications, in general I only use annotations in the web layer (@Controller with @Autowired) the remainder of the application I like to wire with xml. But that is my personal preference (if you ask 10 developers you probably get 20 answers ).

        Comment


        • #5
          Thanks for replying so quickly. I just thought that after years of using XML, I'd try something new and take a look at JavaConfig. For the time being, I guess I'll stick with XML...

          Thanks for your help and clarifications.
          Regards,
          Tom

          Comment


          • #6
            Hi Tom,

            Given the situation and preferences that you've described already, the following is the best way to handle the situation:

            Code:
            @Configuration
            public class MyConfiguration {
            
              @Bean
              public JndiObjectFactoryBean dataSource() {
                JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
                jndiObjectFactoryBean.setJndiName("java:comp/env/jdbc/appDataSource");    
                return jndiObjectFactoryBean;
              }
            
              @Bean 
              public MyDao myDao() {
                return new MyDao((DataSource)dataSource().getObject());
              }
            
            }
            Returning the FactoryBean directly from the @Bean method ensures that #afterPropertiesSet and other lifecycle methods will be called, and then you dereference the bean from the factory yourself by calling dataSource().getObject() when performing the injection into your DAO.

            You can apply this same advice to any Spring FactoryBean that you need to deal with in a @Configuration class. However, in this particular case, you may want to consider foregoing JndiObjectFactoryBean altogether and simply doing a lookup using the standard JNDI API, i.e. a new InitialContext() and a .lookup(...) call, or, sticking with Spring APIs, you could use JndiTemplate. JndiObjectFactoryBean exists principally as a mechanism to allow this kind of lookup to happen from within Spring XML. You probably don't need it at all within a Spring @Configuration class!
            Last edited by Chris Beams; Aug 22nd, 2012, 03:26 AM. Reason: correct return type and casting

            Comment


            • #7
              Hi Chris,

              thanks for your input. Although the getObject() call is bothering me a little, this approach looks sound. One question though, I've seen, that getObject() used to return different instances (https://jira.springsource.org/browse/SPR-6602), so I assume that now (starting from Spring 3.0.1) all FactoryBean.getObject() calls are intercepted and a cached instance is returned? Is this correct? I am just asking to be sure that I can call getObject() as often as needed without creating new instances all the time.

              Regards,
              Tom

              Comment


              • #8
                Originally posted by de_tom View Post
                Hi Chris,

                thanks for your input. Although the getObject() call is bothering me a little, this approach looks sound.
                Right. In any case, dealing with the FactoryBean contract within a @Configuration class is not ideal, fundamentally because @Bean methods *are* factories. i.e. it's awkward to call a factory from within a factory. We've tried to eliminate the necessity of this where possible (See LocalSessionFactoryBean vs. its LocalSessionFactoryBuilder variant in Spring 3.1). There are other cases, like EmbeddedDatabaseFactoryBean where the FactoryBean is really just a thin layer designed to adapt use to Spring XML, and the underlying object -- EmbeddedDatabaseBuilder in this case -- can be used directly within @Bean methods. In cases like yours with JNDI, we haven't provided any specific new functionality, simply because plain JNDI calls are perfectly appropriate, or JndiTemplate for added ease-of-use.

                One question though, I've seen, that getObject() used to return different instances (https://jira.springsource.org/browse/SPR-6602), so I assume that now (starting from Spring 3.0.1) all FactoryBean.getObject() calls are intercepted and a cached instance is returned? Is this correct? I am just asking to be sure that I can call getObject() as often as needed without creating new instances all the time.
                Your assumptions are correct. You can inject the result of #getObject into as many other beans as you like, and you're guaranteed that scoping semantics will be respected.

                Comment

                Working...
                X