Wednesday, November 11, 2015

Notes on using the Spring annotation

Assuming that Spring 3.2.1.Release or above is used in the following notes.
  1. You can use <context:annotation-config/>. It will look for annotations on the properties and methods of a bean, but not the bean class itself.
  2. You use <context:component-scan base-package="org.example" /> to automatically detect classes and register bean definitions. The use of this implicitly enables the functionality of <context:annotation-config>. Also be aware of the following.
    • When <context:component-scan/> is used, the java classes involved need to be annotated with @Component, @Service, etc. Otherwise they would not be recognized.
    • The annotated classes are put into the Spring IoC container. You need to get a bean from this container to get the beans with all the injected properties. Outside this container, the instances of those classes are just plain instances. The annotations have no effect on them.
    • An interesting case is about the servlet. Below is an example.
              @Component
              public class myServlet extends HttpServlet {
                 private @Autowired FooService fooService;
              }
              
      Even though this class is annotated with @Component, the fooService will still be null when this servlet is called in the web application. I think this is because the servlet instance used here is from the Web container. It is NOT from the Spring IoC container. The Spring IoC container may have a copy of this class with fooService injected. But it is not used. So how to still use annotation in a servlet? First of all, the @Component annotation can be removed on servlet. It is not needed since the servlets follow the standard Web container life cycle. The servlet that gets called is from the Web container instead of Spring IoC container. Inside the servlet, you can make annotation work by overriding the init() method as in the following:
         public void init(ServletConfig config) throws ServletException {
            super.init(config);
            SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,config.getServletContext());
        }
      

      Or you can just declare fooService as a plain non-annotated field:

            private FooService fooService;
          
      Then in the code inside the servlet, you can get this object from Spring IoC container as below:
            ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
      
            FooService service = (FooService)applicationContext.getBean("fooService");
        
  3. Annotation injection is performed before XML injection. So the latter configuration will override the former for properties wired through both approaches. You can inject some properties using annotations and other properties using XML. For example, you can have the following:
      public class Foo{
        private @Autowired  Bar bar;
        private DataSource dataSource;
    
      }
    
    
    You can use XML to inject dataSource.
  4. You can use @Configuration class so no XML configuration file is needed. But a cost is that you have to create new java classes with the @Configuration annotation.
  5. To use a @Configuration class, you use AnnotationConfigApplicationContext to include the class. Say that AppConfig is annotated with @Configuration, you can use the following when the application is started.
         public static void main(String[] args){
             ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
             MyService myService = ctx.getBean(MyService.class);
             myService.doStuff();
         }
    
    AnnotationConfigApplicationContext actually is more versatile than that. It can also be used to scan annotated classes as this: ctx.scan("com.sample"). For more information, see Spring documentation.

    In the Web application, you use AnnotationConfigWebApplicationContext. The web.xml file can be configured as below:

      <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext instead of
            the default XmlWebApplicationContext -->
     <context-param>
         <param-name>contextClass</param-name>
         <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
     </context-param>
     
     <!-- Configuration location must consist of one or more comma- or space-delimited fully-qualified @Configuration classes. 
     Fully-qualified packages may also be specified for component-scanning -->
     <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>com.myapp.AppConfig</param-value>
     </context-param>
     
     <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
     </listener>
    
    If you look at the above code snippet, you may wonder how the parameter name "contextClass" is used. It is declared but not referenced anywhere. I think the secret lies in the comment above that parameter. This parameter is implicitly used by the Spring ContextLoaderListener class. As that comment says, ContextLoaderListener uses XmlWebApplicationContext as the contextClass by default. Now we are not using the Spring XML bean definition files, so we change this to AnnotationConfigWebApplicationContext!
  6. There are debates about which way is better, annotation or XML? Both actually have their own pros and cons. Spring's @Configuration class support does not aim to be a 100% complete replacement for Spring XML. You can choose to use "XML-centric" way or java "Annotation-centric" way.

References

  • https://www.mkyong.com/spring3/spring-3-and-jsr-330-inject-and-named-example
  • http://stackoverflow.com/questions/18387993/spring-jsf-integration-how-to-inject-a-spring-component-service-in-jsf-managed
  • http://www.beyondjava.net/blog/integrate-jsf-2-spring-3-nicely
  • No comments:

    Post a Comment