spring framework 5.2 documentation – Inversion of Control IoC Container

IoC Topic

  • 1. Container Overview
  • 2. bean overview
  • 3. Dependency Injection (DI)
  • 4.Bean scope
  • 5. Customize a bean
  • 6 Extension points of containers

The most important thing about the Spring framework is the Inversion of Control (IoC) container

1. Container overview

The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling beans. Containers get instructions on which objects to instantiate, configure, and assemble by reading configuration metadata. Configuration metadata is represented as XML, Java annotations, or Java code. It lets you express the objects that make up your application and the rich interdependencies between those objects.

Spring provides multiple implementations of the ApplicationContext interface. In a standalone application, you typically create an instance of ClassPathXmlApplicationContext or FileSystemXmlApplicationContext. While XML is the traditional format for defining configuration metadata, you can declaratively enable support for these additional metadata formats by providing a small amount of XML configuration that instructs the container to use Java annotations or code as the metadata format.

In most use cases, no explicit user code is required to instantiate one or more Spring IoC container instances. For example, in a web application scenario, a simple eight (or so) lines of boilerplate Web descriptor XML in the application’s web.xml file is usually sufficient (see Convenient ApplicationContext Instantiation for Web Applications).

The following image shows a high-level view of how Spring works. Your application classes are combined with configuration metadata so that after the ApplicationContext is created and initialized, you have a fully configured and executable system or application.

2. Bean overview

The Spring IoC container manages one or more beans. These beans are created using configuration metadata that you provide to the container (for example, in the form of an XML definition).

Within the container itself, these bean definitions are represented as BeanDefinition objects, which contain (among other information) the following metadata:

  • Package-qualified class name: usually the actual implementation class of the bean being defined.

  • Bean behavior configuration element that describes how the bean behaves in the container (scope, lifecycle callbacks, etc.).

  • References to other beans that the bean needs to do its job. These references are also called collaborators or dependencies.

  • Other configuration settings that are set in the newly created object – for example, the size limit of the pool or the number of connections used in the bean that manages the connection pool.

This metadata is converted into a set of properties that make up each bean definition. The following table describes these properties:

Attributes Description
Class Instantiating Beans
Name Naming Beans
Scope Bean Scopes
Constructor arguments Dependency Injection
Properties Dependency Injection
Autowiring mode Autowiring Collaborators
Lazy initialization mode Lazy- initialized Beans
Initialization method Initialization Callbacks
Destruction method Destruction Callbacks

NOTE:

The ApplicationContext implementation also allows registration of existing objects created outside the container (by the user). This is done by accessing the ApplicationContext’s BeanFactory through the getBeanFactory() method, which returns the BeanFactory DefaultListableBeanFactory implementation. DefaultListableBeanFactory supports this kind of registration through the registerSingleton(…) and registerBeanDefinition(…) methods. However, a typical application uses only beans defined through regular bean definition metadata.

Bean metadata and manually provided singleton instances need to be registered early so that the container can correctly reason about them during autowiring and other introspection steps. While overwriting existing metadata and existing singleton instances is supported to some extent, registering new beans at runtime (simultaneous with live access to the factory) is not officially supported and may result in concurrent access exceptions, bean containers The status in is inconsistent.

3. Dependency Injection (DI)

Dependency injection (DI) is a process in which objects define their dependencies (that is, other objects with which they work) solely through constructor parameters, arguments to factory methods, or properties that are set after the object instance is constructed. Return from factory method. The container then injects these dependencies when creating the bean. This process is essentially the inverse of the bean itself (hence the name “inversion of control”), taking control of the instantiation or location of its dependencies on its own by using direct construction of the class or the service locator pattern.

With DI principles, code is clearer and decoupling is more effective when objects provide their dependencies. The object does not look up its dependencies and does not know the location or class of the dependencies. As a result, your classes become easier to test, especially when dependencies are on interfaces or abstract base classes, which allows the use of stubs or mock implementations in unit tests.

There are two main variations of DI: constructor-based dependency injection and setter-based dependency injection.

Constructor-based dependency injection

Constructor-based DI is accomplished by the container calling a constructor with multiple arguments, each representing a dependency. Calling a static factory method with specific arguments to construct a bean is nearly equivalent, and this discussion treats arguments to constructors and static factory methods in a similar manner. The following example shows a class that can only be dependency injected via constructor injection:

Constructor parameter analysis

Constructor argument parsing matching is done using the type of the argument. If there is no potential ambiguity in the constructor parameters of a bean definition, the order in which the constructor parameters are defined in the bean definition is the order in which they are supplied to the appropriate constructor when the bean is instantiated. Consider the following class:

public class ExampleBean {<!-- -->

    // Number of years to calculate the Ultimate Answer
    private final int years;

    // The Answer to Life, the Universe, and Everything
    private final String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {<!-- -->
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

Dependency resolution process

The container execution bean dependency resolution is as follows:

The ApplicationContext is created and initialized with configuration metadata that describes all beans. Configuration metadata can be specified via XML, Java code, or annotations.

For each bean, its dependencies are represented in the form of properties, constructor parameters, or parameters of a static factory method (if you use it instead of a normal constructor). These dependencies are provided to the bean when it is actually created.

Each property or constructor parameter is the actual definition of a value to be set, or a reference to another bean in the container.

Each property or constructor parameter that is a value is converted from its specified format to the actual type of the property or constructor parameter. By default, Spring can convert values provided in string format to all built-in types such as int, long, String, boolean, etc.

The Spring container validates the configuration of each bean when creating the container. However, the bean properties themselves are not set until the bean is actually created. Beans that are singleton scoped and set to pre-instantiated (default) are created when the container is created. Scope is defined in the bean scope. Otherwise, the bean is created only when requested. Creating a bean may result in the creation of a bean diagram, as the bean’s dependencies and their dependencies’ dependencies (and so on) are created and assigned. Please note that parsing

Circular dependencies

If you primarily use constructor injection, you may create unresolved circular dependency scenarios.

For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to inject into each other, the Spring IoC container detects this circular reference at runtime and throws a BeanCurrentlyInCreationException.

One possible solution is to edit the source code of some classes so that they are configured by setters instead of constructors. Alternatively, avoid constructor injection and only use setter injection. In other words, although not recommended, it is possible to configure circular dependencies through setter injection.

Unlike the typical case (where there is no circular dependency), a circular dependency between bean A and bean B forces one of the beans to inject the other before it is fully initialized (a classic chicken-and-egg scenario).

4.Bean scope

When you create a bean definition, you create a recipe to create an actual instance of the class defined by the bean definition. The idea that a bean definition is a recipe is important because it means that, like classes, you can create many object instances from a single recipe.

Not only can you control the various dependencies and configuration values that are inserted into objects created from a specific bean definition, you can also control the scope of the objects created from a specific bean definition. This approach is powerful and flexible because you can configure the scope of the object you create instead of having to bake the scope of the object at the Java class level. Beans can be defined for deployment in one of multiple scopes. The Spring Framework supports six scopes, four of which are only available when you use a web-aware ApplicationContext. You can also create custom scopes.

The following table describes the scope of support:

  • Singleton
    (Default) Scope a single bean definition to a single object instance per Spring IoC container.

  • prototype
    Scope a single bean definition to any number of object instances.

  • request
    Scope a single bean definition to the lifetime of a single HTTP request. That is, each HTTP request has its own bean instance, which is created from a single bean definition. Valid only in the context of a web-aware Spring ApplicationContext.

  • session
    Scope a single bean definition to the lifetime of the HTTP session. Valid only in the context of a web-aware Spring ApplicationContext.

  • application
    Scope a single bean definition to the life cycle of the ServletContext. Valid only in the context of a web-aware Spring ApplicationContext.

  • network socket
    Scope a single bean definition to the lifetime of WebSocket. Valid only in the context of a web-aware Spring ApplicationContext.

5. Customize a bean

  • Life cycle callbacks Callbacks

  • ApplicationContextAware and BeanNameAware

To interact with the container’s management of bean lifecycles, you can implement Spring’s InitializingBean and DisposableBean interfaces. The container calls afterPropertiesSet() for the former and destroy() for the latter, allowing the Bean to perform certain operations when initializing and destroying the Bean.

The JSR-250 @PostConstruct and @PreDestroy annotations are generally considered the best practice for receiving lifecycle callbacks in modern Spring applications. Using these annotations means that your beans are not coupled to Spring-specific interfaces. For more information, see Using @PostConstruct and @PreDestroy.

If you don’t want to use JSR-250 annotations, but still want to eliminate coupling, consider init-method and destroy-method bean definition metadata.

Internally, the Spring framework uses a BeanPostProcessor implementation to handle any callback interface it can find and call the appropriate method. If you need custom functionality or other lifecycle behavior that Spring doesn’t provide by default, you can implement a BeanPostProcessor yourself. For more information, see Container Extension Points.

In addition to initialization and destruction callbacks, Spring-managed objects can implement the Lifecycle interface so that these objects can participate in the container’s own lifecycle-driven startup and shutdown process.

Initialization callback

The org.springframework.beans.factory.InitializingBean interface allows a bean to perform initialization work after the container has set all necessary properties of the bean. The InitializingBean interface specifies a method:

 void afterPropertiesSet() throws Exception;

Destroy callback

Implementing the org.springframework.beans.factory.DisposableBean interface allows a bean to get a callback when its containing container is destroyed. The DisposableBean interface specifies a method:

 void destroy() throws Exception;

Default initialization and destruction methods

When you write initialization and destruction method callbacks that do not use the Spring-specific InitializingBean and DisposableBean callback interfaces, you typically write callbacks named init(), initialize(), < strong>dispose() and other methods. Ideally, the names of such lifecycle callback methods are standardized across the project so that all developers use the same method name and ensure consistency.

You can configure the Spring container to “look up” the named initialization and destruction callback method names on each bean. This means that, as an application developer, you can write application classes and use an initialization callback called init() without having to configure the init-method=”init” attribute for each bean definition. . The Spring IoC container calls this method when the bean is created (and according to the standard lifecycle callback contract described previously). This feature also enforces a consistent naming convention for initialization and destruction method callbacks.

Suppose your initialization callback method is named init() and your destruction callback method is named destroy(). Your class will look like the one in the following example:

public class DefaultBlogService implements BlogService {<!-- -->

    private BlogDao blogDao;

    public void setBlogDao(BlogDao blogDao) {<!-- -->
        this.blogDao = blogDao;
    }

    // this is (unsurprisingly) the initialization callback method
    public void init() {<!-- -->
        if (this.blogDao == null) {<!-- -->
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}

ApplicationContextAware and BeanNameAware

When the ApplicationContext creates an object instance that implements the org.springframework.context.ApplicationContextAware interface, that instance provides a reference to the ApplicationContext. The following listing shows the definition of the ApplicationContextAware interface:

public interface ApplicationContextAware {<!-- -->

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
    
}

Therefore, beans can programmatically manipulate the ApplicationContext in which they were created, either through the ApplicationContext interface or by casting a reference to a known subclass of that interface (such as ConfigurableApplicationContext, which exposes additional functionality). One use is to retrieve other beans programmatically. Sometimes this feature is useful. However, in general, you should avoid it because it couples the code to Spring and does not follow the inversion of control style, where collaborators are provided as properties to the bean. Other methods of the ApplicationContext provide access to file resources, publishing application events, and accessing the MessageSource. These additional capabilities are described in Additional capabilities for ApplicationContext.

Autowiring is another alternative method of obtaining an ApplicationContext reference. The traditional constructor and byType autowiring patterns (as described in Autowiring Collaborators) can provide dependencies of type ApplicationContext for constructor parameters or setter method parameters respectively. For greater flexibility, including the ability to autowire fields and multiple parameter methods, use the annotation-based autowiring feature. If you do this, the ApplicationContext will be autowired into fields, constructor parameters, or method parameters that require an ApplicationContext type if the related field, constructor, or method is annotated with @Autowired. For more information, see Using @Autowired.

When the ApplicationContext creates a class that implements the org.springframework.beans.factory.BeanNameAware interface, the class will get a reference to the name defined in its associated object definition. The following listing shows the definition of the BeanNameAware interface:

public interface BeanNameAware {<!-- -->

    void setBeanName(String name) throws BeansException;
}

6 Extension points of containers

Typically, application developers do not need to subclass the ApplicationContext implementation class. In contrast, the Spring IoC container can be extended by plugging in implementations of special integration interfaces. The next few sections describe these integration interfaces.

Use BeanPostProcessor to customize beans

The BeanPostProcessor interface defines callback methods that you can implement to provide your own (or override the container’s defaults) instantiation logic, dependency resolution logic, etc. If you want to implement some custom logic after the Spring container has finished instantiating, configuring, and initializing the bean, you can plug in one or more custom BeanPostProcessor implementations.

You can configure multiple BeanPostProcessor instances, and you can control the order in which these BeanPostProcessor instances run by setting the order property. This property can be set only if the BeanPostProcessor implements the Ordered interface. If you write your own BeanPostProcessor, you should also consider implementing the Ordered interface. See the javadoc for the BeanPostProcessor and Ordered interfaces for more details. See also the note on programmatic registration of BeanPostProcessor instances.

BeanPostProcessor instances operate on bean (or object) instances. That is, the Spring IoC container instantiates a bean instance and then the BeanPostProcessor instances do their work.

BeanPostProcessor instances are scoped per container. This is only relevant if you use container hierarchies. If you define a BeanPostProcessor in a container, it only post-processes beans in that container. In other words, beans defined in one container are not post-processed by a BeanPostProcessor defined in another container, even if the two containers belong to the same hierarchy.

To change the actual bean definition (that is, the blueprint in which the bean is defined), you need to use BeanFactoryPostProcessor, as described in Customizing configuration metadata using BeanFactoryPostProcessor.

The org.springframework.beans.factory.config.BeanPostProcessor interface consists of two callback methods. When such a class is registered as a postprocessor in a container, the postprocessor is called in the container initialization method (such as InitializingBean.afterPropertiesSet() or any declared init method) for each bean instance created by the container, and in After any bean initialization callback. A postprocessor can perform any operation on a bean instance, including ignoring callbacks entirely. The bean postprocessor usually checks the callback interface, or it may wrap the bean with a proxy. Some Spring AOP infrastructure classes are implemented as bean postprocessors to provide proxy wrapping logic.

The ApplicationContext automatically detects any beans defined in configuration metadata that implement the BeanPostProcessor interface. The ApplicationContext registers these beans as postprocessors so that they can be called later when the beans are created. Bean postprocessors can be deployed in containers like any other bean.

Note that when using the @Bean factory method to declare a BeanPostProcessor on a configuration class, the return type of the factory method should be the implementation class itself or at least the org.springframework.beans.factory.config.BeanPostProcessor interface, clearly indicating the post-processor nature of the bean. . Otherwise, the ApplicationContext cannot automatically detect it by type until it is fully created. This early type detection is crucial because the BeanPostProcessor needs to be instantiated early in order to be applied to the initialization of other beans in the context.

Programmatically registering a BeanPostProcessor instance
Although the recommended way to register BeanPostProcessor is through automatic detection through the ApplicationContext (as mentioned earlier), you can register them programmatically against a ConfigurableBeanFactory using the addBeanPostProcessor method. This is useful when you need to evaluate conditional logic before registering, or even copy bean postprocessors between contexts in a hierarchy. Note, however, that programmatically added BeanPostProcessor instances do not follow the Ordered interface. Here, the order of registration determines the order of execution. Also note that regardless of any explicit ordering, BeanPostProcessor instances registered programmatically are always processed before instances registered via autodetection.

BeanPostProcessor instances and AOP automatic proxies
Classes that implement the BeanPostProcessor interface are special and will be treated differently by the container. All BeanPostProcessor instances and the beans they directly reference are instantiated at startup as part of a special startup phase of the ApplicationContext. Next, all BeanPostProcessor instances are registered in an ordered manner and applied to all other beans in the container. Because AOP autoproxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessor instances nor the beans they directly reference are eligible for autoproxying, and therefore no aspects are woven into them.

For any such bean, you should see an informational log message: Bean someBean is not eligible for processing by any BeanPostProcessor interface (for example: not eligible for automatic proxying).

If you connect beans to a BeanPostProcessor by using autowiring or @Resource (possibly falling back to autowiring), Spring may access unexpected beans when searching for type-matching dependency candidates, thus making them ineligible for auto-proxies or other Type of conditional bean post-processing. For example, if you have a dependency annotated with @Resource where the field or setter name does not correspond directly to the bean’s declared name, and the name attribute is not used, Spring accesses other beans to match them by type.

The following example shows how to write, register, and use a BeanPostProcessor instance in the ApplicationContext.

  • Example: Hello World, BeanPostProcessor style

The first example illustrates basic usage. This example shows a custom BeanPostProcessor implementation that calls the toString() method as each bean is created by the container and prints the resulting string to the system console.

The following listing shows a custom BeanPostProcessor implementation class definition:

package scripting;

import org.springframework.beans.factory.config.BeanPostProcessor;

public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {<!-- -->

    // simply return the instantiated bean as-is
    public Object postProcessBeforeInitialization(Object bean, String beanName) {<!-- -->
        return bean; // we could potentially return any object reference here...
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) {<!-- -->
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
        return bean;
    }
}
  • Example: AutowiredAnnotationBeanPostProcessor

Using callback interfaces or annotations with a custom BeanPostProcessor implementation is a common way to extend the Spring IoC container. One example is Spring’s AutowiredAnnotationBeanPostProcessor? – a BeanPostProcessor implementation that ships with the Spring distribution and autowires annotated fields, setter methods, and arbitrary configuration methods.

Use FactoryBean to customize instantiation logic

You can implement the org.springframework.beans.factory.FactoryBean interface for objects that are themselves factories.

The FactoryBean interface is the pluggable point for the Spring IoC container’s instantiation logic. If you have complex initialization code that is better expressed in Java rather than (potentially) verbose XML, then you can create your own FactoryBean, write the complex initialization in the class, and then insert the custom FactoryBean into the container.

The FactoryBean interface provides three methods:

T getObject(): Returns an instance of the object created by this factory. The instance may be shared, depending on whether the factory returns a singleton or a prototype.

boolean isSingleton(): Returns true if this FactoryBean returns a singleton, false otherwise. The default implementation of this method returns true.

Class getObjectType(): Returns the object type returned by the getObject() method, or null if the type is not known in advance.

FactoryBean concepts and interfaces are used in many places within the Spring framework. Spring itself comes with over 50 implementations of the FactoryBean interface.

When you need to request the actual FactoryBean instance itself from the container rather than the bean it generates, prefix the bean’s id with an ampersand (&) when calling the ApplicationContext’s getBean() method. Therefore, for a given FactoryBean with id myBean, calling getBean(“myBean”) on the container will return the product of the FactoryBean, while calling getBean(” & myBean”) will return the FactoryBean instance itself.