Spring Dependency Injection and Inversion of Control Container Concepts: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
Line 203: Line 203:
</syntaxhighlight>
</syntaxhighlight>


<scan id='Constructor_Injection'></span>This is an example of '''constructor injection'''.
<span id='Constructor_Injection'></span>This is an example of '''constructor injection'''.
<syntaxhighlight lang='java'>
<syntaxhighlight lang='java'>
@Component
@Component

Revision as of 01:29, 7 November 2018

External

  • www.tutorialspoint.com/spring/index.htm

Internal

Overview

Spring is a dependency injection framework. Its main responsibilities are to provide a configuration model that specifies how components should be defined and what are the dependencies between defined components, detect conforming components in the libraries available on the classpath, instantiate the component graph while transitively honoring the dependency relationships - by instantiating the dependency before the dependent and injecting the appropriate dependency references into dependents - and finally expose the components to the application. Instantiation, dependency injection and component life cycle management take place within the Spring IoC container. Support for different application architectures, including messaging, transactions and persistence is built in top of the core container.

Dependency Injection

Rather than have individual components create and manage the lifecycle of their dependency components, a dependency-injected application relies on container to first create all components, then to inject them into other components that need them. Injection is typically done through constructor arguments or property setters.

Configuration Model

This section refers to component configuration. Configuration as in external data that is provided to the application in form of properties or environment variables, and which potentially modifies the behavior of the application, is addressed in the "Application Configuration Concepts" section.

Spring runtime expects to receive information about what components it should create and how those components should be wired together either in form of XML files or, since Spring 2.5, as annotation-based metadata embedded within the bytecode of the component classes themselves and referred to as Java-based configuration. This information that tells Spring how to build its components and what other components they depend on is called configuration metadata. Configuration metadata expressed as XML is equivalent with configuration metadata provided in form of Java annotations. Either can be used to the same effect.

XML-based configuration, Java-based configuration and annotation-based configuration can also be combined and used together. However, when they are used together, annotation injection is performed before XML injection, thus the XML configuration overrides the annotations for properties wired through both approaches.

Configuration metadata includes the definitions of what constitutes a component - the Java fully qualified classname of the component class, the component's dependencies on other components, the component lifecycle details and where to find component classes. Depending on the metadata format, this information is provided in different formats. For example, in case of XML-based configuration, the fully qualified class name is provided in the "class" attribute of the <bean> element. However, for Java-based configuration, a component class is annotated as a @Component or a similar annotation, so Spring runtime has already the class information when it detects the annotation in the class' bytecode.

XML-based Configuration Metadata

Playground XML-based Configuration Example

The XML-based configuration historically precedes Java-based configuration. First Spring releases came with only XML configuration support.

This is an example of a simple Spring XML configuration file:

beans.xml

XML-based Component Definition

When XML-based metadata is used, Spring components are defined using a <bean> element:

<beans ...>
    <bean name="red" class="playground.Red"/>
</beans>

In this example, the Red class is declared as a recipe to build a bean which has no other bean dependencies:

public class Red {
  public Red() {
    ...
  }
}

One essential element of component definition is the specification of the component's dependencies, using the ref attribute. Once the dependencies are specified in the XML configuration metadata, the Spring IoC container automatically injects dependency instances into its managed components. For XML-based metadata, dependencies of a component can be injected via constructor or via setter.

This is an example of constructor injection:

<bean name="blue" class="playground.Blue">
  <constructor-arg ref = "red"/>
</bean>

The corresponding component Java class is:

public class Blue {
  private Red red;
  ...
  public Blue(Red red) {
    this.red = red;
  }
  ...
}

This is an example of setter injection:

<bean name="green" class="playground.Green">
  <property ref = "red"/>
</bean>

The corresponding component Java class is:

public class Green {
  private Red red;
  ...
  public void setRed(Red red) {
    this.red = red;
  }
  ...
}

Note that dependencies are injected only into container-managed components. Components created with new are not managed by the container, so they will not have their dependencies injected. The transitive dependency injection is triggered when the component is obtained from the container with one of the getBean(...) methods, as in the following example. The application context must be one of the XML-enabled application context implementations, such as ClassPathXmlApplicationContext or FileSystemXmlApplicationContext.

ApplicationContext context = new ClassPathXmlApplicationContext("red-blue-grean-beans.xml");

// this component will get its "red" dependency injected by the container
Blue blue = (Blue)context.getBean("blue");

<context:annotation-config>

The <context:annotation-config> element activates detection of @Autowired and @Required, JSR-250 @PostConstruct, @PostDestroy, @PreDestroy and @Resource, @EJB, @PersistenceContext, etc.

<context:annotation-config/>

<context:component-scan>

The <context:component-scan> element configures the context to scans the classpath for annotated stereotype components that will be auto-registered Spring beans: @Component, @Repository, @Service, @Controller, @RestController, @ControllerAdvice, as well as Java-based configuration classes annotated with @Configuration:

<context:component-scan base-package="playground"/>

This tag requires <context:annotation-config> to be active. The equivalent annotation-based configuration annotation is @ComponentScan.

Java-based Configuration Metadata @Configuration/@Bean

Playground Java-based Configuration Example

Java-based configuration is semantically equivalent with XML-based configuration and it is an alternative to it, or it can be used together with it. The place of the XML bean definition file, whose root is the <beans> element, is taken by a Java class annotated with @Configuration. Individual XML <bean> definitions are replaced with @Bean-annotated methods of the configuration class. The application context's constructor accepts configuration class types as argument, instead of XML file names. The @Configuration annotation indicates that the class it annotates is a configuration class that will provide beans to the Spring application context. @Configuration triggers the configuration of the Spring infrastructure. A @Configuration-annotated class must expose one or more methods annotated with @Bean. A @Bean-annotated method is the equivalent of an <bean> element in an Spring XML configuration file. The objects returned by these methods are bean instances, and when the methods are invoked by the Spring container, the corresponding beans are added to the application context. By default, their respective bean IDs will be the same as the names of the methods that define them. The Spring container invokes these methods with the goal of obtaining bean definitions.

Java-based Component Definition

This section presents the equivalent Java-based component definition for the components defined with XML in the previous section. Concepts behind Java-based configuration are presented in the Java-based Configuration Metadata section.

The bean classes presented in the previous example do not change. The equivalent of the XML file is a @Configuration-annotated class:

@Configuration
public class ApplicationConfiguration {
  @Bean
  public Red red() {
    return new Red();
  }
  @Bean
  public Blue blue(Red red) {
    // constructor injection
    return new Blue(red);
  }
  @Bean
  public Green green(Red red) {
    Green green = new Green();
    // setting injection
    green.setRed(red);
    return green;
  }
}

The application context must be an AnnotationConfigApplicationContext:

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
Blue blue = (Blue)applicationContext.getBean("blue");

Annotation-based Configuration with @Autowired and Stereotype Annotations

Playground Annotation-based Configuration Example

Annotation-based configuration refers to the possibility of using annotations in the component class, on the relevant class, method and field declaration, to express the fact that the class is a Spring component, and also other Spring semantics, such as dependency on another component. Annotation-based configuration can be used to provide all metadata that would otherwise be provided with XML-based configuration or Java-based configuration. Components can be declared using stereotype annotations. A stereotype annotation denotes the role of the annotated bean in the overall architecture at a conceptual, rather than implementation, level:

Dependency between components can be declared with the @Autowired annotation. When a constructor used for autowiring also contains non-component arguments, default values for those can be specified with the @Value annotation. A detailed example of how to configure components is available below.

@ComponentScan is the annotation-based configuration equivalent of the XML <context:component-scan>. It allows specifying a base package, or multiple base packages to scan for annotations:

@ComponentScan("playground")
...

The <context:component-scan> element requires the <context:annotation-config> element; however, @ComponentScan annotation does not. This is because in almost all cases when using @ComponentScan, default annotation config processing (e.g. processing @Autowired) is assumed. Furthermore, when using AnnotationConfigApplicationContext, annotation config processors are always registered, meaning that any attempt to disable them at the @ComponentScan level would be ignored.

Annotation-based Component Definition

This section presents the equivalent annotation-based component definition for components defined with XML in the XML-based Component Definition section or with Java-based configuration in the Java-based Component Definition section. Concepts behind annotation-based configuration are presented in the Annotation-based Configuration section.

First off, there is no @Configuration-annotated class, nor an XML file. The components are detected dynamically in the classpath, being singled out by their stereotype annotations. The dependency relationships are not expressed in the @Bean-annotated methods, or <bean> XML elements. They are expressed with @Autowired annotations instead. Aside from getting extra annotations, the bean classes presented in the previous example do not change. The application context must be an AnnotationConfigApplicationContext configured with the name of the base package that contains the annotated classes; this is how the component are actually identified:

ApplicationContext applicationContext = new AnnotationConfigApplicationContext("io.playground.spring");
Blue blue = (Blue)applicationContext.getBean(Blue.class);

The component classes used in the previous examples will stay almost unchanged, with the exception of stereotype, @Autowired and @Value annotations:

@Component
public class Red {
  private String payload;
  public Red(@Value("bright") String payload) {
    this.payload = payload;
  }
  ...
}

This is an example of constructor injection.

@Component
public class Blue {
  private Red red;
  private String payload;
  @Autowired
  public Blue(Red red, @Value("some default value") String payload) {
    this.red = red;
    this.payload = payload;
  }
  ...
}
@Component
public class Green {
  private Red red;
  private String payload;
  public Green(@Value("fresh") String payload) {
    this.payload = payload;
  }
  @Autowired
  public void setRed(Red red) {
    this.red = red;
  }
  ...
}

XML-based, Java-based and Annotation-based Configuration Combinations

Metadata specified in an XML file can be used by an application that instantiates a Java-based-configuration application context if the XML metadata is exposed via a special @Configuration class:

@Configuration
@ImportResource({"classpath*:extra-beans.xml"})
public class SupplementalXmlConfiguration {}

This configuration class should be registered with the application context alongside the proper Java-based @Configuration classes:

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(
  ApplicationConfiguration.class, SupplementalXmlConfiguration.class);

Beans specified in the extra-beans.xml XML metadata file and those specified in the ApplicationConfiguration class can interoperate with each other and can be declared dependencies of each other.

Playground XML-based Configuration from a Java-based Configuration Application Context

Symmetrically, an XML-based application context detects @Configuration-annotated classes and allows beans declared by them to interoperate with beans declared in the XML metadata and declare dependencies on each other. The detection is configured by specifying <context:annotation-config> and <context:component-scan> tags in the XML file.

Playground Java-based Configuration from an XML-based-configuration Application Context

All three types of configuration can be mixed together:

ApplicationContext applicationContext = new AnnotationConfigApplicationContext("io.playground");
applicationContext.register(JavaBasedApplicationConfiguration.class);
applicationContext.register(SupplementalXmlConfiguration.class);

where JavaBasedApplicationConfiguration.class is a regular @Configuration-annotated configuration class, and SupplementalXmlConfiguration.class is also a configuration class that exposes XML metadata read from files, as described above.

Playground Annotation-based and Java-based Configuration from an XML-based-configuration Application Context

Spring Components

Also known as Spring beans.

Component Definition

See XML-based component definition, Java-based Component Definition and Annotation-based Component Definition above.

Component Lifecycle

Component Scope

Singleton

Component Detection

Auto-detection is ...

Component Scan

Spring IoC Container

The Spring IoC container is also referred to as the core container. The container is a pool of beans created in a memory space by the framework when the application starts up. Some of the beans, such as the singletons, are instantiated right away, when the container is created and started. Other beans are lazily loaded: unless the bean is requested by the application or some other beans as part of their dependency graph, the framework will not instantiate the bean. The container exposes an API that has methods to query these beans.

The simplest type of container is a bean factory, described by the BeanFactory interface. A bean factory provided dependency injection to its beans, initialization and destroy callback invocations.

An application context, described by the ApplicationContext interface, extends a bean factory, so anything a bean factory can do, the application context can do too. Additionally, an application context provides aspects, and application events. The Spring Framework comes with several types of application context implementations:

  • ClassPathXmlApplicationContext is a standalone XML application context that reads the the configuration metadata from XML files from the classpath.
  • FileSystemXmlApplicationContext is a standalone application context that reads the the configuration metadata from XML files loaded from the file system or from URLs, interpreting plain paths as relative file system location.
  • XmlWebApplicationContext.
  • AnnotationConfigApplicationContext is a standalone application context that accepts @Configuration-annotated classes, as well as stereotype-annotated classes and JSR-330 compliant classes using javax.inject annotations as input. In case of multiple @Configuration classes, @Bean methods defined in later classes will override those defined in earlier classes. This can be leveraged to deliberately override certain bean definitions via an extra @Configuration class. Component classes can be registered one by one using register(Class), or they can be detected via the component scan mechanism, triggered by scan(String) method. This application context is used by the Spring Boot applications.

A "classic" Spring Framework application explicitly creates its own application context, usually in its own main class:

public class Main {

    static ApplicationContext APPLICATION_CONTEXT = new ClassPathXmlApplicationContext("some-beans.xml");

    public static void main(String[] args) {
        ...
    }
}

One improvement brought forward by Spring Boot is that Spring Boot applications are not required to do that. The Spring Boot runtime transparently creates the application context.

How to I find an application context that already exists?