CHAPTER 2

image

Spring Fundamentals

This chapter is necessary for building a Spring background, which will be very helpful in the upcoming chapters. This chapter will help you get familiar with the Spring container, context, beans, and most Spring core modules and how they work together to allow developers to focus on solving problems instead of building up support.

The Basics

Any application system is made of components that work together to solve a problem. In object-oriented design they are called classes. Figure 2-1 depicts the sequence of operations necessary to create a Person instance. Because this chapter is about Spring Core, a web application is not needed, so requests to manipulate Person instances will be directed to implementations of the PersonManager interface. Implementations of this interface will provide access to the database using an implementation of PersonRepository interface. The operation is pretty simple and the setup to write and execute the code should be too. This is where Spring comes in—providing a way to build an application using plain old Java objects (POJOs)1 and applying enterprise services (transaction execution, remote execution) noninvasively.

9781484208090_Fig02-01.jpg

Figure 2-1. UML sequence of operations necessary to create a Person instance

The components making up an application interact and depend on one another. Defining how these objects are composed is quite difficult using plain Java. Even with the help of all the design patterns defined by experts in the software industry, the work is still cumbersome, as the pattern components still have to be implemented before being used. The Spring inversion of control (IoC) container was designed to help developers compose objects into fully working applications, ready to use.2

The Spring container is responsible for the creation of components, resolving their dependencies and providing them to other components. It does this by reading the configuration of an application from *.xml files or annotated configuration classes, and internally constructs a graph of dependencies between the objects. It then proceeds to traverse the graph, and creates and injects dependencies according to the configuration. The result of this initialization is an ApplicationContext, which provides access to application components, resource loading, internationalization support, and other features that won’t be mentioned in this guide because it is out of scope.3 Figure 2-2 depicts the process of creating an ApplicationContext using the Spring IoC container.

9781484208090_Fig02-02.jpg

Figure 2-2. How Spring works

The Spring Core Container

The Spring core container is made of the following modules:

  • spring-beans
  • spring-core
  • spring-context and spring-context-support (provides support classes that help integration of third-party libraries for caching, mailing, scheduling, and template engines)
  • spring-expression

The spring-core and spring-beans modules provide the fundamental part of the framework: the IoC and dependency injection features, which the container needs to solve and inject dependencies as specified in the configuration. The spring-context module extends the previous two modules, adding support for internationalization, resource loading, event propagation, and transparent creation of contexts. The core component of this module is the ApplicationContext interface. The spring-expression module provides a powerful Expression Language for querying and manipulating an object graph at runtime, and for operations like setting and getting property values, property assignment, and others.

Considering the diagram in Figure 2-1, the classes needed to support implementing the operation to save a Person instance look like this:

public class PlainPersonManagerImpl implements PersonManager {
    PersonRepository repo;

    //injecting a dependency using the constructor
    public PlainPersonManagerImpl(PersonRepository repo) {
        this.repo = repo;
    }
...
}
public class PlainPersonRepository implements PersonRepository {
    private DataSource dataSource;
    @Override
    public int save(Person person) {
        ..
    }
    //injecting a dependency using a setter method
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

Image !  The PlainPersonRepository class is a simple POJO persistence handler. Its sole responsibility is to ensure Person instances are saved and retrieved from the database. Its behavior is built on a javax.sql.DataSource implementation. This is different from the Spring Data JPA repositories used in the Personal Records Manager project, which will be presented later. The approach specific to this chapter is Spring Core–based, which is more “old-style,” before Spring Data JPA existed; this is to best introduce the Spring core modules and possibilities.

To implement that functionality in plain Java language, you have to write something like this:

PersonRepository repo = new PlainPersonRepository();

DataSource dataSource = new com.oracle.jdbc.pool.OracleDataSource();
dataSource.setUrl("jdbc:oracle:thin:@localhost:1521:orcl");
//set other dataSource properties
       ...
repo.setDataSource(dataSource);
PersonManager personManager = new PlainPersonManagerImpl(repo);
Person person = new Person("John", "Smith","1980-04-13");
// Use the manager
personManager.save(person);

As you can easily see, except the last line, everything else is setup code—the preparation before the execution of the method. It is a lot of code. What would happen if you decided to change method signatures or to use a different DataSource implementation? A big part of this code would have to be changed too.

In the next section, let’s see how Spring does the same thing.

Spring Configuration

There are three ways to define the configuration of an application using Spring:

  • Special XML configuration files that allow usage of elements described in the associated namespaces
  • Java-based configuration classes (classes annotated with @Configuration can be used by Spring IoC as a source for bean definitions)
  • Mixed configuration: XML and annotations

All three types of configurations are covered in the following sections. The code sources attached to this chapter will help you test your understanding of each.

XML

The following code is the XML content of a file named app-simple-config.xml, which is the configuration file for a simple Spring application:

<beans>
        <bean id="personManager" class="com.book.plain.PlainPersonManagerImpl">
              <constructor-arg ref="personRepository" />
        </bean>
        <bean id="personRepository" class="com.book.plain.PlainPersonRepository">
              <property name="dataSource" ref="dataSource" />
        </bean>
        <bean id="dataSource" class="com.oracle.jdbc.pool.OracleDataSource">
              <property name="URL" value="jdbc:oracle:thin:@localhost:1521:orcl" />
              ...
        </bean>
</beans>

And here is how the code to save a Person instance looks with Spring:

// Create the application from the configuration
ApplicationContext context =
              new ClassPathXmlApplicationContext("app-simple-config.xml");
// Look up the application manager interface
PersonManager manager = (PersonManager) context.getBean("personManager");
// Use the manager
manager.save(new Person("John", "Smith","1980-04-13"));

As you can see, the code is a lot smaller, because all the preparation of the environment was moved into the XML configuration file. And the configuration file can be manipulated more easily. If an external property file is used as entry for some of the values in it, in some simple cases, the application doesn’t even have to be recompiled to change behavior. The DataSource configuration can be separated from the general configuration file, which can later allow you to easily switch between DataSource implementations—depending on the context in which a code should run.

<util:properties id="dbProp" location="classpath:datasource/db.properties"/>

<bean id="dataSource" class=
           "org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="#{dbProp.driverClassName}"/>
    <property name="url" value="#{dbProp.url}"/>
    <property name="username" value="#{dbProp.username}"/>
    <property name="password" value="#{dbProp.password}"/>
</bean>

In the previous example, the property values that look like #{value} are loaded from the db.properties file, which contains the following:

driverClassName=org.h2.Driver
url=jdbc:h2: ~/prod
username=prod
password=prod

The values for the properties are loaded into a java.util.Properties instance with an id of dbProp using a functionality offered by the util namespace in the first line of the configuration, and then their values are accessed using the SpEL (Spring Expression Language) syntax and injected into the dataSource bean. (There is another way to do this using a component named PropertyPlaceholderConfigurer, which is covered in the “How Bean Factory Post Processors Work” section.) Spring knows how to do this because configuration files are constructed using XML namespaces.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd">
         ...
</beans>

The underlined values in the previous example show how a prefix is assigned to a namespace and how a namespace is associated with an XSD schema that contains the XML elements that can be used in the configuration file. Usually, each namespace contains definitions for all XML tags for a specific spring module, or a group of tags with related responsibilities.

As everything in Spring is a bean, most commonly used configuration styles use the bean’s root element, and the namespace for it is declared using the xmlns attribute. When additional namespaces are used, the elements defined by them need to be used inside the current element (beans). They need to have a prefix associated so that the Spring IoC knows in which namespace to look for those element definitions; notations such as xmlns:[prefix]="[namespace URL]" are used.

Image !  The running code in this example can be found in 02-chapter-solution project. This is a module of the book-code project, which was designed to gradually test your knowledge acquired while reading this book. The book-code contains one or more modules for each chapter. Some module names are postfixed with -practice and contain a series of TODO tasks that the developer should be able to complete after reading a chapter.

The modules prefixed with -solution contain the completed tasks and are meant to be used for comparison with the developer’s own solution. Sometimes a solution module might contain extra code that is meant simply to show the developer other situations that he might encounter in Spring projects.

For example, by splitting up the configuration file to isolate the DataSource configuration, you could have the following configuration for a production environment:

ApplicationContext context =
                new ClassPathXmlApplicationContext("application-config.xml","db-config.xml");
                        

And this configuration could be for a test environment:

ApplicationContext context =
                new ClassPathXmlApplicationContext("application-config.xml","test-db-config.xml");
                      

The two environments are completely decoupled, and the tests are very easy to write. Figure 2-3 displays a typical structure for a Spring Maven project with a split configuration for production and a test environment.

9781484208090_Fig02-03.jpg

Figure 2-3. Typical Maven structure for a project

Image !  In this example, the configuration files were created under a directory named spring to emphasize that these are Spring configuration files, because in a more complex project there could be XML configuration files for other purposes (for example, logging or caching stored outside of the spring directory). The code in this book intentionally skips the spring directory from the path to reduce the size of the quotes and to make the list of configuration files more readable.

In the configuration files, and when instantiating contexts, resources are usually prefixed with a word that tells the Spring container where they are located. These prefixes can be used for any type of resources needed in an application. Consider a standard Maven setup for a project like the one in Figure 2-3; Table 2-1 shows the paths where a Spring container would look for resource files depending on the prefix.

Table 2-1. Prefixes and Corresponding Paths

Prefix

Location

Comment

no prefix

In root directory where the class creating the context is executed.

In the main or test directory. The type of the resource being loaded depends on the ApplicationContext instance being used. (A detailed example is presented after this table.)

classpath:

The resource should be obtained from the classpath.

In the resources directory; the resource is of type ClassPathResource.

file:

In the absolute location following the prefix.

The resource is loaded as a URL from the filesystem and is of type UrlResource.

http:

In the web location following the prefix.

The resource is loaded as a URL and is of type UrlResource.

The following is an example of resource loading without using a prefix:

Resource template = ctx.getResource("application-config.xml");

Depending on the context class used, the resource loaded can have one of the following types:

  • If ctx is a ClassPathXmlApplicationContext instance, the resource type is ClassPathResource
  • If ctx is a FileSystemXmlApplicationContext instance, the resource type is FileSystemResource
  • If ctx is a WebApplicationContext instance, the resource type is ServletContextResource

Annotations

Spring also supports configuration via annotations. The previous XML configuration can be replaced by a class annotated with @Configuration, and looks like this:

@Configuration
@PropertySource(value = "classpath:datasource/db.properties")
public class AppConfig {

    @Autowired
    Environment env;

    @Bean(name="personManager")
    public PersonManager getPersonManager(){
        return new PlainPersonManagerImpl(getPersonRepository());
    }

    @Bean(name="personRepository")
    public PersonRepository getPersonRepository(){
        PersonRepository repo = new PlainPersonRepository();
        repo.setDataSource(getDataSource());
        return repo;
    }

    @Bean(name="dataSource")
    public DataSource getDataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("driverClassName"));
        dataSource.setUrl(env.getProperty("url"));
        dataSource.setUsername(env.getProperty("username"));
        dataSource.setPassword(env.getProperty("password"));
        return dataSource;
    }
}

All the code to save a Person instance looks like this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppConfig.class})
public class SecondAnnotationPersonSaveTest {

    @Autowired
    PersonManager personManager;

    @Test
    public void savePerson() {
        personManager.save(new Person("John", "Smith", "1980-04-13"));
    }
}

When annotations are used, XML configuration files are no longer needed, nor namespaces. Specific annotations are used to mark configuration classes (@Configuration) and to mark methods as bean definitions (@Bean); this is not covered because it is outside the scope of this book. What you need to remember is that the @Bean annotation makes sure that every time the annotated method is called the same bean is returned. Without it, the method will return a newly created instance each time.

Image CC  In the previous code example, each @Bean annotation has the attribute name populated with a value to name the bean created by the method. This attribute is neither mandatory nor necessary. When it is not specified, the Spring IoC determines a name for the bean based on the method name by removing the get and lowercasing the first letter of the remaining string.

Mixed Approach

XML and annotations can be mixed. You could have the bean classes annotated with @Component (or any annotation extending @Repository for DAO repository classes, @Service for service classes, or @Controller for MVC handler classes) and one or more XML files, which define just the DataSource configuration and specifies where the beans are located. In the following code sample, the DataSource configuration is separated in another file (as shown in the “How Bean Factory Post Processors Work” section) to decouple configurations for production and test environments.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd"

       <context:component-scan base-package="com.book.beans"/>
       ...
</beans>

In XML configuration files, bean definitions describe the way a dependency should be provided to them: using either constructors or setters. This is called autowiring. When using annotations, the way a dependency should be provided is described using the @Autowire annotation on a constructor or setter.4 But you need to tell the Spring IoC container to look for that type of annotation, and the declaration <context:component-scan ...> does exactly that.

When using annotations, <bean> declarations are no longer needed because each bean type is annotated with @Component, or an extension of it, and the <context:component-scan..> declaration tell the Spring IoC container to look for those types of annotations in the specific file. The process of identifying annotated bean types is called autodiscovery.

Thus what the following configuration element does is enable bean autowiring and autodiscovery anywhere in the classpath in packages (and subpackages) named as the value of the attribute base-package.

<context:component-scan base-package="com.book.beans"/>

The <context: ..> declarations are Spring’s way of compacting the declaration of infrastructure beans named *PostProcessor, which take care of interpreting annotations into beans definitions.

  • <context:annotation-config/> registers the following:
    • –   AutowiredAnnotationBeanPostProcessor (supports @Autowired, @Value, @Inject)
    • –   CommonAnnotationBeanPostProcessor (supports @Resource, @PostConstruct,@PreDestroy)
    • –   PersistenceAnnotationBeanPostProcessor (supports @PersistenceUnit, @PersistenceContext)
    • –   RequiredAnnotationBeanPostProcessor (supports @Required)
  • <context:component-scan base-package="com.book.beans"/> implicitly enables the functionality of <context:annotation-config> and adds support for more annotations (@Repository, @Service, @Controller, which are specializations of @Component, @Configuration, etc.)

If you want to extend your knowledge about this, you can always read the Spring Reference Documentation.5 More detailed information is outside the scope of this book.

The Beans

The beans are the objects handled by the Spring IoC container. The following section will cover all you need to know about how beans are created, how the beans are categorized, how they are accessed, and how they are destroyed when they are no longer needed.

Lifecycle and Instantiation

The beans are created in order of dependency. If a bean of type B, needs a bean of type A for its creation, the Spring container will know to first create bean A and then inject it into bean B. If an application has multiple configuration files, the Spring container first reads all of them, internally creates a dependency tree of bean definitions, and then starts traversing the tree, starting with its lowest level where the simplest bean definitions are. In the cases mentioned in previous sections, the order for bean creation (instantiation) is dataSource, personRepository, and personManager. The steps are described in Figure 2-4.

9781484208090_Fig02-04.jpg

Figure 2-4. Order of bean instantiation

A bean cannot be created if its required dependencies do not exist; an exception is thrown in case of missing dependencies. But how does the Spring container know which dependencies are required? There are a few ways. One is the type of injection. Spring supports two types of injection: via constructor and via setter. The constructor injection is when the constructor of a bean is defined as having an argument of type another bean. In the previous example, the PersonManagerImpl constructor definition requires a PersonRepository instance as an argument, and thus the PersonManagerImpl requires a bean of type PersonRepository to be created before its creation.

<!– Constructor injection –>
<bean id="personManager" class="com.book.PersonManagerImpl">
        <constructor-arg ref="personRepository" />
    </bean>

    <!– Setter injection–>
    <bean id="personRepository" class="com.book.JdbcPersonRepository">
        <property name="dataSource" ref="dataSource" />
</bean>

Any object that has a constructor with arguments cannot be constructed without passing in arguments. This restriction does not apply for the setter injection, but it can be enforced in two ways:

  • By annotating the setter method with @Required. If the property is not set, a BeanInitializationException is thrown.
  • By annotating the setter method with @Autowire the Spring IoC container tries to inject a bean with the specific type. If such a bean is not found, a BeanCreationException is thrown.

One of the advantages of using the setter injection is that you can create hierarchical beans, and setters will be inherited. In a setter injection, bean creation and dependency injection are two separate steps; for constructor injection there is only one step. So basically, setter injection makes your configuration more flexible.

For a bean to “come to life” and become available to be used for a purpose, it has to go through the steps shown in Figure 2-5.

9781484208090_Fig02-05.jpg

Figure 2-5. The steps for a bean creation

How Bean Factory Post Processors Work

A bean definition can be modified before instantiating the bean, and this is done by beans called bean factory post processors. They are defined as classes implementing the BeanFactoryPostProcessor interface and are recognized by an application context and instantiated before any other beans in the container. The most used and known in the Spring world is the PropertyPlaceholderConfigurer.

<bean id="dataSource" class=
             "o.s.jdbc.datasource.DriverManagerDataSource">

      <property name="driverClassName" value="${driverClassName}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
</bean>

<context:property-placeholder location="classpath:datasource/db.properties"/>

The last line in this example is a simplified version of defining a PropertyPlaceholderConfigurer using the Spring context namespace; it is equivalent to the following:

<bean class=
  "o.s.beans.factory.config.PropertyPlaceholderConfigurer">
       <property name="location" value="classpath:datasource/db.properties"/>
</bean>

This bean reads those properties from the db.properties file and then populates the dataSource source bean with their values. Of course, the easier way to do this is to use SpEL expressions and the util namespace:

<util:properties id="dbProp" location="classpath:datasource/db.properties"/>

<bean id="dataSource" class=
      "o.s.jdbc.datasource.DriverManagerDataSource">
   <property name="driverClassName" value="#{dbProp.driverClassName}"/>
   <property name="url" value="#{dbProp.url}"/>
   <property name="username" value="#{dbProp.username}"/>
   <property name="password" value="#{dbProp.password}"/>
</bean>

Bean Initialization and Destruction

An ApplicationContext instantiates all singleton (bean scopes are covered in detail in the “Bean Scopes” section) beans by default and also destroys them at the end of their lives. After a bean has been created and its dependencies injected, it can be initialized automatically by telling the context to execute a specified method. Before a bean ends its life, a different method might be called to do some resource cleanup. The context can be told to automatically do that too. These methods must have a void no-argument signature. There is no restriction on the accessor used for them. In the official documentation, the lifecycle methods given as example are all public. But there are opinions that state they should be protected or private (obviously, it does not apply to InitializingBean’s afterPropertiesSet and DisposableBean’s destroy) to prevent direct calls of these methods from the application code, as these methods should be called only once and only by the Spring IoC container.

There are multiple options for bean initialization:

  • Using @PostConstruct from JSR 250
  • Using @Bean’s initMethod attribute
  • Implementing InitializingBean and providing implementation for the afterPropertiesSet method (not recommended because it couples the application code with Spring infrastructure code)
  • Using the init-method attribute on a <bean/> XML definition

When a bean ends its life, some cleanup operations might be necessary; to implement this kind of behavior, there are also multiple options:

  • Using @PreDestroy from JSR 250
  • Using @Bean’s destroyMethod attribute
  • Implementing DisposableBean and providing implementation for the destroy method (not recommended, as it couples the application code with Spring infrastructure code)
  • Using the destroy-method attribute on a <bean/> XML definition

In the code sample there is a bean in the com.book.spring.components package that was implemented in such a way to clarify the Spring bean lifecycle. The bean is called CompleteLivingBean and has @PostConstruct and @PreDestroy annotated methods, implements InitializingBean and DisposableBean, and has methods in which names are used as values for attributes init-method and destroy-method. This bean was implemented using a combined lifecycle strategy to clearly show when each initializer/destruction method is called by the Spring IoC and to clearly display the bean creation steps in Figure 2-5.

This is the configuration:

<context:component-scan base-package="com.book.beans"/>

<bean id="livingBean" class="com.book.beans.CompleteLivingBean"
       init-method="initMethod"
       destroy-method="destroyMethod">
       <property name="internal" value="testValue"/>
</bean>

This is the definition of the bean class:

public class CompleteLivingBean implements InitializingBean, DisposableBean {
    public String internal;

    public CompleteLivingBean() {
        logger.info("1. Constructor.");

    }


    public void setInternal(String internal) {
        logger.info("2. Setter.");
        this.internal = internal;

}

    @PostConstruct
    public void postConstruct(){
        logger.info("3. @PostConstruct.");
    }

    @Override
     public void afterPropertiesSet() throws Exception {
        logger.info("4. afterPropertiesSet.");
    }

    public void initMethod(){
        logger.info("5. init-method.");

    }


    @PreDestroy
    public void preDestroy(){
        logger.info("6. PreDestroy.");

    }

    @Override
    public void destroy() throws Exception {
        logger.info("7. destroy.");

}


    public void destroyMethod() throws Exception {
        logger.info("8. destroy-method.");

    }

}

Also, there is no restriction on method names used as values for init-method and destroy-method attributes; initMethod and destroyMethod were used in this example to make their purpose really obvious.

Image !  In the certification exam, you might be asked which method is executed first—the one annotated with @PostConstruct or the one mentioned by the init-method; so the CompleteLivingBean helps clear up when methods are executed and why.

When executing the test for the com.book.beans.BeanLifecycleTest bean, you will see the following output:

INFO c.b.b.CompleteLivingBean - 1. Constructor.
INFO c.b.b.CompleteLivingBean - 2. Setter.
INFO c.b.b.CompleteLivingBean - 3. @PostConstruct.
INFO c.b.b.CompleteLivingBean - 4. afterPropertiesSet.

INFO c.b.b.CompleteLivingBean - 5. init-method.
...
INFO c.b.b.CompleteLivingBean - 6. @PreDestroy.
INFO c.b.b.CompleteLivingBean - 7. destroy.
INFO c.b.b.CompleteLivingBean - 8. destroy-method.

As represented in Figure 2-5, when a bean is created, the following succession of actions happens:

  1. The constructor is called first to create the bean.
  2. The dependencies are injected (setters are called).
  3. The pre-initialization BeanPostProcessors are consulted to see if they want to call anything from this bean. The @PostConstruct annotation is registered by the CommonAnnotationBeanPostProcessor, so this bean will call this annotated method. This method is executed right after the bean has been constructed and before the class is put into service,6 before the actual initialization of the bean (before afterPropertiesSet and init-method).
  4. The InitializingBean’s afterPropertiesSet is executed right after the dependencies were injected.
  5. The init-method attribute value method is executed last, as this is the actual initialization method of the bean.

When a bean is destroyed:

  1. The @PreDestroy method is executed, as this has to be executed before a destroy method, if one exists. The PreDestroy annotation is used on methods as a callback notification to signal that the instance is in the process of being removed by the container.7
  2. The DisposableBean’s destroy method is executed next, as the Spring standard order defines it so.
  3. The destroy-method attribute value method is executed last, as this is the actual destroy method of the bean, and the Spring standard order defines it so.

This is the simplified and more natural explanation of the bean lifecycle; in most cases, this is all you will need. If you want to view the full picture with full plumbing details and other things the context does, you can read the official JEE and Spring documentation.8

Image !  The main reason for init-method and destroy-method creation was to give the developer a little control over beans definitions from third-party libraries, which have classes that cannot be modified or extended. This way, the developer can decide what gets executed after creation and what executes before destruction by using XML configuration.

How Bean Post Processors Work

A BeanPostProcessor allows the developer to process a bean instance created by the IoC container after its instantiation, and then again after the initialization lifecycle event has occurred on it. BeanPostProcessors are defined as classes implementing the BeanPostProcessor interface, and are recognized by an application context and instantiated before any other beans in the container, because after their instantiation, they are used to manipulate other beans instantiated by the IoC container. The @PostConstruct and @PreDestroy annotations are processed by a bean called CommonAnnotationBeanPostProcessor. This is not a default infrastructure bean for the Spring IoC container, so to use it you have to specify it in the configuration of the application. You would expect to need something like this in the mvc-config.xml file:

<bean class="o.s.c.a.CommonAnnotationBeanPostProcessor"/>

And this could work, but there will be some issues because configuring the bean like that overrides the Spring defaults, which might lead to unexpected behavior. Fortunately, this bean configuration is one of those included in the following line, a Spring shortcut based on the context namespace:

<context:component-scan base-package="com.book.beans"/>

Or in this one:

<context:annotation-config/>

The BeanPostProcessor beans wrap other beans into AOP proxies that add extra behavior (more details on AOP in the “Spring AOP” section). The Spring Framework has different types of BeanPostProcessors that can be used for caching, transactions, security, and so forth. The CommonAnnotationBeanPostProcessor scans for methods annotated with @PostConstruct and @PreDestroy, and calls those methods at the appropriate time.

The code samples use logback to display logs. By increasing the granularity of the log for the Spring Framework to DEBUG, you can see what is happening “behind the scenes,” and what CommonAnnotationBeanPostProcessor is actually doing. In the following configuration snippet, you are shown how to modify the granularity of the log by editing the logger element for the Spring Framework in the logback.xml file:

<logger name="org.springframework" level="DEBUG" additivity="false">
    <appender-ref ref="STDOUT" />
</logger>

After modifying the log file when running the BeanLifecycleTest, you can see the behavior of the CommonAnnotationBeanPostProcessor9:

INFO CompleteLivingBean - 1. Constructor.
DEBUG CABPP - Found init method on class
     CompleteLivingBean: private void CompleteLivingBean.postConstruct()
DEBUG CABPP Found destroy method on class
     CompleteLivingBean: protected void CompleteLivingBean.preDestroy()
DEBUG CABPP Registered init method on class CompleteLivingBean:
     InitDestroyAnnotationBeanPostProcessor$LifecycleElement@64e17f36
DEBUG CABPP Registered destroy method on class CompleteLivingBean:
     DestroyAnnotationBeanPostProcessor$LifecycleElement@a27dd7d7
INFO c.b.b.CompleteLivingBean - 2. Setter.
DEBUG CABPP - Invoking init method on bean ’livingBean’:
     private void CompleteLivingBean.postConstruct()
INFO c.b.b.CompleteLivingBean - 3. @PostConstruct.
INFO c.b.b.CompleteLivingBean - 4. afterPropertiesSet.
...
DEBUG CABPP - Invoking destroy method on bean ’livingBean’:
protected void CompleteLivingBean.preDestroy()
INFO c.b.b.CompleteLivingBean - 1. @PreDestroy.

The previous section mentioned that there are annotation attributes equivalents for the init-method and destroy-method. If you were to define CompleteLivingBean using a class annotated with @Configuration, it would look like this:

@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
public CompleteLivingBean getCompleteLivingBean() {
    return new CompleteLivingBean();
}

And would be equivalent to this XML definition:

<bean id="livingBean" class="com.book.beans.CompleteLivingBean"
             init-method="initMethod" destroy-method="destroyMethod"/>

Bean Scopes

When the Spring IoC instantiates beans, it creates a single instance for each bean—unless a property is set on the bean definition specifying otherwise. The property in question is called scope and the default scope for a bean is singleton. The scopes are defined in Table 2-2.

Table 2-2. Bean Scopes

Scope

Description

singleton

The Spring IoC creates a single instance of this bean and any request for beans with an id or ids matching this bean definition results in this instance being returned.

prototype

Every time a request is made for this specific bean, the Spring IoC creates a new instance.

request

The Spring IoC creates a bean instance for each HTTP request. Only valid in the context of a web-aware Spring ApplicationContext.

session

The Spring IoC creates a bean instance for each HTTP session. Only valid in the context of a web-aware Spring ApplicationContext.

global-session

The Spring IoC creates a bean instance for each global HTTP session. Only valid in the context of a web-aware Spring ApplicationContext.

So when a bean is created without a scope attribute, the scope of the bean is singleton:

<bean id="personRepository" class="com.book.JdbcPersonRepository">
      <property name="dataSource" ref="dataSource"/>
</bean>

Otherwise, the scope of the bean is the one specified by the value of the scope attribute:

<bean id="personRepository" class="com.book.JdbcPersonRepository"
            scope="prototype">
       <property name="dataSource" ref="dataSource"/>
</bean>

There is an annotation equivalent to this that can be used on @Component (and other stereotype annotations) annotated beans:

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {

    private Logger logger = LoggerFactory.getLogger(PrototypeBean.class);
    private static int instanceCounter = 0;
    public PrototypeBean() {
        logger.info("-> Constructing instance no: " + (++instanceCounter));
    }
}

Image !  @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) is equivalent to @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) and @Scope("prototype") because constant SCOPE_PROTOTYPE is of type string and has the "prototype" value. Using Spring constants eliminates the risk of misspelling the scope value.

The @Scope annotation can also be used on a bean definition annotated with @Bean to specify the scope of the resulting bean.

@Bean(name="personManager")
@Scope("prototype")
//or @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public PrototypeBean getPrototypeBean(){
    return new PrototypeBean();
}

If you were to execute the following test, the test would pass:

@Test
   public void testPrototype() {
       // Create the application from the configuration
       ClassPathXmlApplicationContext context =
         new ClassPathXmlApplicationContext("classpath:test-app-config.xml");
       PrototypeBean pb1 = (PrototypeBean)context.getBean("prototypeBean");
       assertNotNull(pb1);
       //the bean is requested by type
       PrototypeBean pb2 = context.getBean(PrototypeBean.class);
       assertNotNull(pb2);
       assertNotEquals(pb1,pb2);
}

And this is what would be seen in the log file:

DEBUG - Creating instance of bean 'prototypeBean'
INFO -> Constructing instance no: 1
DEBUG - Finished creating instance of bean 'prototypeBean'
DEBUG - Creating instance of bean 'prototypeBean'
INFO -> Constructing instance no: 2
DEBUG - Finished creating instance of bean 'prototypeBean'

A special case of bean scope is the scope of an inner bean. An inner bean is defined within the scope of another bean. The reason for doing this is because the bean does not need to be shared with other beans, but is needed only for the creation of the enclosing bean. The scope attribute has no meaning for an inner bean and is ignored; so are the attributes id and name, as the bean is anonymous. When using Java Configuration, the inner bean is just a local variable in a method. The following code snipped declares the DataSource bean as an inner bean:

<util:properties id="dbProp" location="classpath:datasource/db.properties"/>

<bean id="personRepository" class="com.book.JdbcPersonRepository">
      <property name="dataSource">
          <bean id="dataSource" class=
          "org.springframework.jdbc.datasource.DriverManagerDataSource">
              <property name="driverClassName" value="#{dbProp.driverClassName}"/>
              <property name="url" value="#{dbProp.url}"/>
              <property name="username" value="#{dbProp.username}"/>
              <property name="password" value="#{dbProp.password}"/>
      </bean>
    </property>
</bean>

Accessing Beans

Beans can be identified in three ways: by type, by name, and by id. The following subsections explain these in detail; examples are provided for each case. How to access beans configured with annotates is covered too.

Bean Identification by Type

A bean can be identified by its type if there is only one definition of a bean with that type in the Spring configuration file.

The BeanPostPrecessor classes registered by <context:annotation-config/> that scan for annotations are singleton infrastructure beans instantiated by the Spring IoC container, when that configuration line is present in a Spring configuration file. At any time during the life of an application only one instance of each of those beans will exist. Basically, this configuration file:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

       <context:annotation-config/>


</beans>

Is equivalent to this:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--the org.springframework. package was shortened to o.s.
 for this code to fit the page better -->
<bean class="o.s.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean class="o.s.context.annotation.CommonAnnotationBeanPostProcessor"/>
<bean class="o.s.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean class="o.s.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>

</beans>

Considering the following bean definition:

<bean class="com.book.sandbox.SimpleBean" />

If there is no other bean definition with the same class attribute value, the bean can be accessed like this:

SimpleBean sb = context.getBean(SimpleBean.class);

Or can even be injected as a dependency via autowiring:

@Autowired
SimpleBean simpleBean;

Image !  In the book-code/02-chapter project, there is a class called BeanIdentificationTest that tests various scenarios of bean identification.

Bean Identification by Name

The <bean/> element has an attribute called name. The value assigned to this attribute in a bean definition can be used to access this bean. A duplicate bean name will invalidate a configuration file. The name is flexible and can be used to define more than one name when the values are separated by a comma (",") or a semicolon (";"). The bean is defined as follows:

<bean name="sbb0" class="com.book.sandbox.SimpleBean"/>

Can be accessed as follows:

// the old way
SimpleBean sb0 = (SimpleBean)context.getBean("sb0");
  // or the Spring 3.0 way
SimpleBean sb0 = context.getBean("sb0", SimpleBean.class);

And can also be injected as a dependency via autowiring using the @Qualifier annotation:

@Autowired
@Qualifier(value = "sb0")
SimpleBean simpleBean;

The @Bean annotation has a name attribute too, so an equivalent annotation configuration can be created:

@Bean(name="simpleBean")
public SimpleBean getSimpleBean(){
  return new SimpleBean();
}

Bean Identification by id

The <bean/> element has an attribute called id. The value assigned to this attribute in a bean definition can be used to access the bean. This attribute uniquely identifies a bean, so a duplicate bean id will invalidate a configuration file. This attribute can appear alongside the name attribute, and both can be used to access the bean. The id and the name attributes serve the same purpose: they are both used to define bean identifications. The difference between them is that the value of the id attribute must conform to XML standard id, which means no weird characters like a comma (",") or semicolon (";") can be contained in it.

Basically, the following bean definition is valid:

<bean name="sb0" id="id0" class="com.book.sandbox.SimpleBean"/>

And the following test will pass, as both calls will return the same bean:

@Test
public void testBeans() {
    ...
    SimpleBean sb01 = context.getBean("sb0", SimpleBean.class);
    SimpleBean sb02 = context.getBean("id0", SimpleBean.class);
    assertTrue(sb01 == sb02);
}

Accessing Annotated Beans

The beans defined using @Component and extensions of it can be autowired by name or by type without any extra configuration.

Image CC  When using annotation configuration—beans annotated with @Component or extensions of it—the Spring IoC container also creates a logical name for these beans by lowercasing the first letter of the class name.

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean { ... }
...
\ requesting bean by name
PrototypeBean pb1 = (PrototypeBean)context.getBean("prototypeBean");
assertNotNull(pb1);
\Requesting bean by type
PrototypeBean pb2 = context.getBean(PrototypeBean.class);
assertNotNull(pb2);
assertNotEquals(pb, pb2);

Spring AOP

AOP is an acronym for aspect-oriented programming and represents a programming paradigm that aims to simplify code by grouping repetitive operations in units called aspects. AOP helps managing common functionality that spans across the application, like logging, security, and transactionality. AOP complements OOP (object-oriented programming) by providing a more advanced way of decoupling the code and modularizing an application.

The AOP framework complements the Spring IoC container. The container can be used without it in small applications that do not require the use of security or transactions, because these are the key crosscutting concerns for enterprise applications.

In Spring, an aspect is class annotated with @Aspect. It contains methods called advices that are annotated with aspect-specific annotations that intercept the execution of other beans’ methods and performs specific operations before and/or after their execution, and can prevent the execution of an intercepted method if necessary.

The AOP framework makes this possible by scanning all aspects when the application context is started, and creates AOP proxy objects that wrap around existing beans to implement aspect contracts. When the target beans are requested for usage or injection, the proxy object is injected or returned instead. From a developer’s point of view, it looks like the intended object is used, but the Spring IoC container works with the proxy object that is wrapped around it.

Let’s see how AOP can make things easier when you want to save a Person instance to the database using the PersonManagerImpl mentioned at the beginning of the chapter.

The following is what the code looks like in Spring without AOP. Figure 2-6 shows the UML diagram.

<!--  configuration  will  contain  this  element  -->
<bean  id="txManager"  class=
       "org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property  name="dataSource"  ref="dataSource"/>
</bean>

 //  PersonManagerImpl.java
...
@Autowired
@Qualifier("txManager")
PlatformTransactionManager  transactionManager;
@Autowired
@Qualifier("personRepository")
PersonRepository repo;

public int save(Person person) {
       TransactionDefinition def = new DefaultTransactionDefinition();
       TransactionStatus status = transactionManager.getTransaction(def);
       int result = repo.save(person);

       transactionManager.commit(status);
       return result;
}

9781484208090_Fig02-06.jpg

Figure 2-6. Diagram in non-AOP mode

And here is how it looks using AOP (the UML diagram is presented in Figure 2-7):

<!-- configuration will contain this element needed to switch on
the transactional behaviour -->
<tx:annotation-driven transaction-manager="txManager"/>
// PersonManagerImpl.java
@Component("personManager")
@Transactional
public class PersonManagerImpl implements PersonManager {
    @Autowired
    @Qualifier("personRepository")
    PersonRepository repo;

    public int save(Person person) {
        return repo.save(person);
}
}

9781484208090_Fig02-07.jpg

Figure 2-7. Diagram in AOP mode

The <tx:annotation-driven/> configuration element is defined in the Spring tx namespace, which has to be added to the configuration file:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">
       ...
</beans>

And in order to run the methods of a bean in a transactional environment, you also have to specify the TransactionManager instance used to handle the transactions. In a test environment, the annotation @TransactionConfiguration is used:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:app-aop-cfg.xml",
        "classpath:spring/test-db-config.xml"})
@TransactionConfiguration(transactionManager = "txManager")
public class PersonSaveTest {
...
}

To verify that the test method is running in a transaction environment, you can switch the Spring Framework log to DEBUG, as explained in the “Lifecycle and Instantiation” section, and run the test. In the console, the following logs will prove this:

DEBUG o.s.j.d.DataSourceTransactionManager - Acquired Connection
     conn1: url=jdbc:h2:mem:dataSource user=SA for JDBC transaction
DEBUG o.s.j.d.DataSourceTransactionManager - Switching JDBC Connection
     conn1: url=jdbc:h2:mem:dataSource user=SA to manual commit
INFO c.b.a.PersonManagerImpl - -> Calling repo.save(person)
DEBUG o.s.j.c.JdbcTemplate - Executing prepared SQL update
DEBUG o.s.j.c.JdbcTemplate - Executing prepared SQL statement
     insert into person (firstname, lastname, date_of_birth) values (?,?,?)
DEBUG o.s.j.c.JdbcTemplate - SQL update affected 1 rows
INFO c.b.a.PersonManagerImpl - -> repo.save execution completed.
DEBUG o.s.j.d.DataSourceTransactionManager - Initiating transaction commit
DEBUG o.s.j.d.DataSourceTransactionManager - Committing JDBC transaction on Connection
     conn1: url=jdbc:h2:mem:dataSource user=SA
DEBUG o.s.j.d.DataSourceTransactionManager - Releasing JDBC Connection
     conn1: url=jdbc:h2:mem:dataSource user=SA after transaction

Image CC  If the bean of type TransactionManager is named transactionManager when in a transactional environment, the Spring IoC container will detect it automatically and there is no need to specify it as an argument for the @TransactionConfiguration annotation. Even more, @TransactionConfiguration can be replaced with @Transactional, and the test methods will still be executed in a transactional environment.

The transaction-manager attribute from the <tx:annotation-driven/> can be omitted too.

Also, @Qualifier("transactionManager") is not needed when the transactionManager is autowired and the bean of type TransactionManager has the default name.

In the code samples presented here, a bean of type TransactionManager with a different name was used to show the developer the configurations needed to work in cases other than the default one, because in bigger applications, multiple beans of type TransactionManager might be needed.

Testing Spring Applications

When it comes to writing code, there are two types of testing that matter: unit testing and integration testing.

  • Unit testing is used to test small units of code, thus its naming. Unit testing is easy to do—not much setup is necessary, and since JUnit10 has introduced @Test annotation writing, unit tests have become a breeze.
  • Integration testing is used to test bigger chunks of code made up of objects interacting together in a given context, and the focus is set on business logic and object integration with each other. The context is usually made up of mocks or stubs that replace the objects, which are not the focus of the tests. You can imagine that creating a testing context is not a simple job.

The Spring Framework includes a testing module called spring-test that makes integration testing really practical to implement. The tests that have been used throughout this chapter use the spring-test module.

  • The SpringJUnit4ClassRunner, as the names says, is a Spring class used to tell JUnit that the tests in this class are executed in a Spring test context.
  • The @ContextConfiguration receives one or more configuration files as parameters that are used to initialize the test context.
  • The @TransactionConfiguration is the annotation that injects the transactionManager instance used to run tests in a transactional environment. As mentioned earlier, this can be skipped, and @Transactional can be used when the TransactionManager bean has the default name.

Image CC  When using @ContextConfiguration to annotate a test class, the configuration file path can be skipped, and then Spring IoC container will look for a file named [TestClassName]-context.xml in the same location where the test class is defined. When the project has a Maven structure, the configuration is placed in the resources directory, and the directories matching the package name for the test class are created so the file will have the same relative path as the test class.

So if you have test class com.book.simple.SimpleTest annotated with @ContextConfiguration, then resources will have com/books/simple/SimpleTest-context.xml to provide the test context configuration, which is automatically discovered and used by the Spring IoC container.

Summary

After reading this chapter, you should have a basic knowledge of how Spring does its magic and understand the following:

  • Two flavors of configuration can be mixed: XML-based (decoupled from classes code) and Java annotation–based (bean definitions are mixed in the class code)
  • The lifecycle of a bean
  • How to access a bean
  • What AOP is and how and where Spring can apply it
  • How to test Spring applications

Quick Quiz

Question 1: What is a bean?

  1. a plain old Java object

  2. an instance of a class

  3. an object that is instantiated, assembled, and managed by a Spring IoC container

Question 2: What is the default scope of a bean?

  1. default

  2. singleton

  3. protected

  4. Dprototype

Question 3: What types of dependency injection are supported by Spring IoC container?

  1. setter injection

  2. constructor injection

  3. interface-based injection

  4. field-based injection

Question 4: What is true about @PostConstruct and @PreDestroy ?

  1. they are JSR-250 annotations

  2. they are supported by AutowiredAnnotationBeanPostProcessor

  3. they are registered by the <context:component-scan/> element

Detailed answers are in the Appendix.

Practical Exercise

The practice module for this chapter is in the book-code project; it is named 02-chapter-practice. The solution is in the 02-chapter-solution module. You are given the code for a few related beans. Your task is to complete the existing configuration files, to create test contexts, and to make sure that the tests pass.

The book-code project is a gradle multimodule project. It can be built from the command line by running gradle build under the book-code directory. This will build all modules of the project. The build will fail when run for the first time because of the unresolved tasks in the -practice projects. If you do it this way, you will have something similar to the following output in your console:

$ gradle build
..
:02-chapter-practice:compileJava UP-TO-DATE
:02-chapter-practice:processResources UP-TO-DATE
:02-chapter-practice:classes UP-TO-DATE
:02-chapter-practice:jar UP-TO-DATE
:02-chapter-practice:assemble UP-TO-DATE
:02-chapter-practice:compileTestJava UP-TO-DATE
:02-chapter-practice:processTestResources UP-TO-DATE
:02-chapter-practice:testClasses UP-TO-DATE
:02-chapter-practice:test

com.book.plain.PlainPersonSaveTest > savePerson FAILED
java.lang.AssertionError at PlainPersonSaveTest.java:31

..
BUILD FAILED
Total time: 4.096 secs

If you decided to use the Intellij IDEA editor on the Gradle tab, you already have available all the predefined Gradle tasks and you can just double-click the one you are interested in. In the following image, the selected task is the build task for the project book-code; but if you scroll down in that view, you will see the modules in the project and you can choose to build a specific module. So double-click under the :02-chapter-practice on the build task and execute it. The build will fail, but this is expected. This task will succeed because it does not execute the tests. In Figure 2-8 you can see how your IDE should look.

9781484208090_Fig02-08.jpg

Figure 2-8. Intellij IDEA Gradle run

Image !  To compile projects without failing (due to tests in practice projects that are not fixed yet), you can use the allCompile task, which was created this purpose.

On the left in the Project view, you can see the book-code project and the component modules. Each module has the typical Maven structure mentioned earlier in the chapter (see Figure 2-3). Expand the 02-chapter-practice and look in the com.book.base package. In it you will notice the implementation of the Person class. The instances of this class are used in the test examples and are handled by instances of classes that implement PersonManager.

The PersonManager interface defines the int save(Person person) method, which should return the number of records that were affected. In this case, it can be only 1 (one Person instance was saved) or 0 (no Person instance was saved).

The PersonRepository interface will be implemented by the repository classes used in this example. Some repositories will actually save Person instances into a test database, but most of them will just print a log message to tell the developer that the method was executed correctly. The code for this chapter was created to show how a Spring application is configured, so more complex functionality is not covered. Classes and methods are commented properly, so using them is very straightforward.

Every time a project or module is built with gradle, a directory named build is created containing the detailed results of the build for that project or module. This can be seen in Figure 2-9.

9781484208090_Fig02-09.jpg

Figure 2-9. Intellij IDEA Project view

What is relevant when working with these sources is the reports estsindex.html file. When opened in a browser, it displays all the failing tests in that module. This page can be refreshed during development to track, step by step, the number of tests that have to be fixed. When accessed after the first gradle build, it should display what is depicted in Figure 2-10.

9781484208090_Fig02-10.jpg

Figure 2-10. Gradle-generated index.html

There are eight tests failing, and they do so because the implementation for them is incomplete. Completing them has been left as practice for you, the developer reading this book. Click the TODO label in the bottom-left corner. A view will open that should look like what is shown in Figure 2-11. Click and expand everything. Every TODO task has a number attached. Start resolving the tasks in ascending order.

9781484208090_Fig02-11.jpg

Figure 2-11. Intellij IDEA TODO tab

The root package is called com.book. Under this package all packages will group classes with a common purpose. For example, the plain package contains classes that implement the functionality for saving a Person instance by using plain Java—no Spring beans or configuration files, as was shown in the beginning of the “The Spring Core Container” section. As Maven convention requires, the test classes are placed in the same package as the classes being tested, but under the test directory. The first exercise is to complete the plain Java implementation to save a Person instance and make the com.book.plain.PlainPersonSaveTest. After you have written the code, run the test.

Just right-click anywhere in the file and choose Run and the class name in the menu, similar to what you see in Figure 2-12.

9781484208090_Fig02-12.jpg

Figure 2-12. Intellij IDEA— running a Gradle test

If the test does not pass, go back and re-read the beginning of this chapter to refresh your memory on how dependency injection is handled in plain Java. After you are done and you have a successful build for 02-chapter-practice, you can compare your solution to the one in 02-chapter-solution.

Also, you should take a look at the sources, test sources, and resources under packages aop, noaop, and sandbox.

The book.code.spring.noaop package contains classes that implement a transactional bean used to save a Person instance, but opening and committing a transaction are done manually.

The book.code.spring.aop package contains classes that implement a transactional bean used to save a Person instance using Spring AOP.

Both implementations are tested in a test context that uses a H2 in-memory database to perform the actual save of a Person instance. The configuration of the test database is in the test-db-config.xml file, and you will notice that the Spring jdbc namespace is used. As JPA is not used, you need some *.sql initialization files, which can be found under the test/resources/datasource directory.

<jdbc:embedded-database id="dataSource" type="H2">
    <jdbc:script location="classpath:datasource/db-schema.sql"/>
    <jdbc:script location="classpath:datasource/db-test-data.sql"/>
</jdbc:embedded-database>

The com.book.spring.sandbox contains classes and tests designed to help you understand how bean identification works.

When you have passed all the tests and you feel confident that you have a solid grasp of the Spring fundamentals, you can continue to the next chapter.

_________________________

1A software term introduced by Martin Fowler, Rebecca Parsons, and Josh MacKenzie in September 2000 to refer to ordinary Java objects not bound by any restriction.

2The process through which an object is provided its dependencies, whether it is using a constructor or properties which are set using setter methods, is called dependency injection. inversion of control is the concept through which an external component has control over what is provided as a dependency to an object.

3For more information, see the public reference documentation at http://docs.spring.io/spring/docs/current/spring-framework-reference.

4The @Autowiring annotation can also be used on the field directly, called field injection; but this approach is discouraged because it makes testing difficult. As the field is usually private, to test the containing bean, a full Spring context must be set up or reflection must be used to access the field.

5The Spring Reference Documentation can be accessed at http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/.

6A snippet from the JEE official Java doc at http://docs.oracle.com/javaee/7/api/javax/annotation/PostConstruct.html.

7A snippet from the JEE official Java doc at http://docs.oracle.com/javaee/7/api/javax/annotation/PreDestroy.html.

8http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/BeanFactory.html.

9CABPP is the acronym for CommonAnnotationBeanPostProcessor. It is used to fit a log quote nicely on a page.

10The most commonly used Java testing framework (see http://junit.org).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.133.233