Wednesday, January 25, 2012

Annotation-Based Spring Application context




Our goal is to get rid of manual wiring in the configuration files (Java or XML)

Annotation based application context

Define configuration bean as application context 

Define configuration java class having @Configuration, this java class is called Annotation based application context
1.                     @Configuration
     public class ApplicationConfig{
   
    }

    Register you bean


  • Register your java bean using@ComponentScan, This will auto-detect your classes as spring bean in package com.sudhir 
  @Configuration
     @ComponentScan(basePackages="com.sudhir", excludeFilters {@ComponentScan.Filter(Configuration.class)})
  public class ApplicationConfig{
   
}

There are 4 steriotype annotation
@Component Indicates that an annotated class is a "component".
@Repository Indicates that an annotated class is a "Repository" (or "DAO").
@Service Indicates that an annotated class is a "Service" (e.g. a business service facade).
@Controller Indicates that an annotated class is a "Controller" (e.g. a web controller).

Here is an example of DAO classes 


@Repository
public class BeerDaoImpl implements BeerDao{
}

Inject the bean as dependency

Inject the required bean using @autowired , example bean dataSource is injected using autowiring

@Repository
public class BeerDaoImpl implements BeerDao{
            JdbcTemplate jdbcTemplate;

            @Autowired
            public void setDataSource(DataSource dataSource) {
                        this.jdbcTemplate = new JdbcTemplate(dataSource);
            }        
}

Register third party bean

   DataSource is not your bean how do you inject the data source bean?, what if the bean are third party classes, we need to define such bean in configuration file, configuration for datasource.
a.       We don’t want to hard code the properties in datasource and want to use the properties file
b.      Add  @PropertySource annotation to get the properties files values in Environment class
c.       Inject Environment class using autowired
Java based configuration file for data source


@Configuration
@ComponentScan(basePackages = "cybage", excludeFilters = {@ComponentScan.Filter(Configuration.class)})
@PropertySource("classpath:jdbc.properties")
public class ApplicationConfig{
   
            @Autowired
    Environment env;
           
            @Bean
            public DataSource dataSource() {
                        com.mchange.v2.c3p0.ComboPooledDataSource dataSource = new com.mchange.v2.c3p0.ComboPooledDataSource();
                        try {
                                    dataSource.setDriverClass(env.getProperty("driverClass"));
                        } catch (PropertyVetoException e) {
                                    // TODO Auto-generated catch block
                                    e.printStackTrace();
                        }
                        dataSource.setJdbcUrl(env.getProperty("jdbcUrl"));
                        dataSource.setUser(env.getProperty("user"));
                        dataSource.setPassword(env.getProperty("password"));
                        return dataSource;
            }

}


Write the test class

Because your context is AnnotationConfigAplicationContext Loader should be AnnotationConfigContextLoader.class And Configuratin class should be the class which has @Configuation. If you have more than one configuration files then use

classes = {ApplicationConfig.class,ApplicationConfig1.class }

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = ApplicationConfig.class)
public class BeerDaoImplTest extends AbstractJUnit4SpringContextTests {

            private BeerDao beerDao;

            @Autowired
            protected void setBeerDao(BeerDao beerDao) {
                        this.beerDao = beerDao;
            }

           
}

Spring IOC without xml


Since Java adopted annotations, most of application has started using it to cut down the xml configuration, spring 3 also introduce the annotation driven configuration, let’s explore.
Here is the Martin Fowler’s famous dependency injection example of moviLister and moveFinder

MovieFinder

public interface MovieFinder {
    List findAll();
}

Implementation of MovieFinder
public class ColonMovieFinder implements MovieFinder {
            Resource filename;
           
            public Resource getFilename() {
                        return filename;
            }

           
            public void setFilename(Resource filename) {
                        this.filename = filename;
            }

            @Override
            public List<Movie> findAll() {
                        List<Movie> movies=null;
                        try {
                                    movies = inputStreamAsString(this.filename.getInputStream());
                        } catch (IOException e) {
                                    // TODO Auto-generated catch block
                                    e.printStackTrace();
                        }
                        return movies;
            }
           
}      

MovieLister needs MovieFinder dependency

public class MovieLister {

            private MovieFinder finder;

            public void setFinder(MovieFinder finder) {
                        this.finder = finder;
            }

           
           
            public Movie[] moviesDirectedBy(String arg) {
                        List allMovies = finder.findAll();
                        for (Iterator it = allMovies.iterator(); it.hasNext();) {
                                    Movie movie = (Movie) it.next();
                                    if (!movie.getDirector().equals(arg))
                                                it.remove();
                        }
                        return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
            }
           
           
}

Setup the dependency
1.       Configuration java class need to be written to define the dependency.
2.       Add @Configuration to tell the spring about your configuration class
3.       @Bean should be define to tell the spring, they are spring bean
4.       initMethod and distroyMethod attribute should be used in @Bean annotation to define the life cycle.
5.       @Scope should be used to define the scope is singleton or prototype or request ect

@Configuration
public class AppConfig {

            public
            @Bean(destroyMethod="distroy",initMethod="init")
             @Scope("prototype")
            MovieLister movieLister() {
                        MovieLister moviLister = new MovieLister();
                        moviLister.setFinder(movieFinder());
                        return moviLister;
            }

            public @Bean
            MovieFinder movieFinder() {
                        ColonMovieFinder colonMovieFinder = new ColonMovieFinder();
                        Resource resource = new ClassPathResource("examples/movies.txt");
                        colonMovieFinder.setFilename(resource);
                        return colonMovieFinder;
            }

}
Construct the application context
1.       Pass the class having @Configuration to AnnotationConfigApplicationContext constructor
2.       More than one configuration can be written and all of them can be passed to constructor as it support java 5 vargar


AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

Test class
public class ApplicationContextAnnotationTest {

            public static void main(String[] args) {
        //Initialize IoC Container
                        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

                        //Retrieve the bean from Container
                        MovieLister myBean = context.getBean(MovieLister.class);

                        for(Movie m : myBean.moviesDirectedBy("Rajnikant")){
                                    System.out.println(m); 
                        }
                        context.destroy();
                        context.close();
            }
           
}

Problems what if you two bean of same type?
1.       Pass the name to @bean attribute
2.       Get the bean from context using been class and name.

Register bean by name
            public @Bean(name="moviFinder")
            MovieFinder movieFinder() {
                        ColonMovieFinder colonMovieFinder = new ColonMovieFinder();
                        Resource resource = new ClassPathResource("examples/movies.txt");
                        colonMovieFinder.setFilename(resource);
                        return colonMovieFinder;
            }

Retrieve bean by name
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
           
MovieFinder myFind = context.getBean("moviFinder",MovieFinder.class);


Problem : this approach doesn’t cut the configuration it just move the configuration from xml file to java file, right way to cut the configuration is autowiring that I will post in next blog