37 Discuss the Lifecycle and Scopes of Spring Beans

Spring Bean 的生命周期可以分为以下几个阶段:

  1. 实例化:Spring 在启动时通过配置文件或注解定义的方式创建 Bean 实例。可以使用构造函数或工厂方法进行实例化。

  2. 设置属性:在实例化之后,Spring 会自动将配置文件中或通过注解设置的属性注入到 Bean 中。可以使用注解、XML 配置或者 Java 配置的方式来设置属性。

  3. 初始化:在属性注入之后,Spring 会调用特定的初始化方法,以执行一些自定义的初始化逻辑。可以使用注解或实现 InitializingBean 接口的方式来定义初始化方法。

  4. 使用:在初始化之后,Bean 实例可以被其他组件或代码使用。

  5. 销毁:在应用程序关闭或者请求范围结束时,Spring 会自动销毁 Bean 实例。可以使用注解、实现 DisposableBean 接口或配置 destroy-method 的方式来定义销毁方法。

而 Spring Bean 的作用域决定了一个 Bean 实例的生命周期范围,可以通过配置文件或注解来设置。以下是常见的几种作用域:

  1. 单例(Singleton):Spring 容器中只存在一个 Bean 实例,默认为单例。在整个应用程序中,只有一个 Bean 实例被共享。

  2. 原型(Prototype):每次请求或通过 getBean() 方法获取 Bean 实例时,都会创建一个新的实例。每个实例是相互独立的,互不影响。

  3. 会话(Session):在 Web 应用中,每个用户会话可以拥有一个特定的 Bean 实例。

  4. 请求(Request):在 Web 应用中,每个请求可以拥有一个特定的 Bean 实例。

  5. 其他作用域:还有一些其他作用域,如全局会话(Global Session)、应用(Application)和线程(Thread)等,可以根据具体需求进行配置和使用。

这就是 Spring Bean 的生命周期和作用域的概述。在实际使用中,生命周期和作用域的设置可以根据具体的业务需求来选择最合适的配置方式。

Typical Answer #

The lifecycle of a Spring Bean is relatively complex and can be divided into two processes: creation and destruction.

First, the creation of a Bean goes through a series of steps, including:

  • Instantiating the Bean object.
  • Setting Bean properties.
  • If we have declared dependencies through various Aware interfaces, the Bean will be injected with dependencies from the container’s infrastructure level. Specifically, BeanNameAware, BeanFactoryAware, and ApplicationContextAware will inject the Bean ID, Bean Factory, or ApplicationContext respectively.
  • Invoking the pre-initialization method postProcessBeforeInitialization of BeanPostProcessor.
  • If the Bean implements the InitializingBean interface, the afterPropertiesSet method will be called.
  • Invoking the Bean’s self-defined init method.
  • Invoking the post-initialization method postProcessAfterInitialization of BeanPostProcessor.
  • The creation process is complete.

You can refer to the following diagram to understand the specific process and order.

Second, the destruction of a Spring Bean involves invoking the destroy method of DisposableBean and the Bean’s self-defined destroy method.

Spring Bean has five scopes, of which the two basic ones are:

  • Singleton, which is the default scope of Spring, meaning that a unique Bean instance is created for each IOC container.
  • Prototype, where a new Bean instance is created for each getBean request.

From the perspective of Bean characteristics, Prototype is suitable for stateful Beans, while Singleton is more suitable for stateless situations. In addition, careful consideration should be given to using the Prototype scope, as frequent creation and destruction of Beans can result in noticeable overhead.

If it is a web container, three additional scopes are supported:

  • Request, which creates a separate Bean instance for each HTTP request.
  • Session, where the scope of the Bean instance is the Session.
  • GlobalSession, used for Portlet containers, as each Portlet has a separate Session. GlobalSession provides a global HTTP Session.

Analysis of Exam Points #

Today I have selected a high-frequency Spring interview question with an introductory nature. I believe that understanding and thinking about Bean lifecycle, which reflects Spring’s design and mechanism, is more meaningful than memorizing the details and steps in typical answers to questions.

As you can see, the lifecycle of a Bean is completely managed by the container. From property setting to various dependency relationships, the container is responsible for injection and handling of various other matters in each stage. Spring container defines a clear lifecycle communication interface for application developers.

If we look at it from the perspective of specific API design and usage techniques, do you remember the Marker Interface I mentioned in Lesson 13 of the column? The Aware interface is a typical application example. Beans can implement various sub-interfaces of Aware, providing a unified entry point for the container to inject dependency objects in the form of callbacks.

But let’s get back to learning and interviewing about Spring. Maybe even a whole book cannot fully cover its content. In the column, I will supplement it to a limited extent:

  • The basic mechanism of Spring.
  • The scope of Spring framework.
  • Some details of Spring AOP’s own design. The previous Lesson 24 focuses on the underlying implementation principles, which is not comprehensive enough after all. Whether it is dynamic proxy or bytecode manipulation, they are still only the foundation. What is more important is the support of Spring for aspect-oriented programming at the Spring layer.

Knowledge Expansion #

First, let’s take a look at the basic mechanisms of Spring. There are at least two fundamental aspects that you need to understand.

  • Inversion of Control (IoC), also known as Dependency Injection (DI), is widely used in the Spring framework to effectively solve the problem of tight coupling between modules.

    From the process of Bean creation, we can see that its dependencies are injected by the container, and the specific implementation methods include constructors with parameters, setter methods, or the @Autowired annotation.

  • AOP (Aspect-Oriented Programming), which we have already encountered before, is a programming mechanism used in Spring framework for implementing cross-cutting concerns. Spring framework relies on AOP technology for implementing functions such as transactions, security, and logging. I will provide further explanation below.

Secondly, what does Spring actually refer to?

The Spring I mentioned earlier is actually the narrow definition of Spring Framework. It includes core modules such as dependency injection and event mechanism, data access modules composed of functionalities such as transactions and O/R Mapping, as well as web frameworks like Spring MVC and other basic components.

Broadly speaking, Spring has become a huge ecosystem, for example:

  • Spring Boot, which integrates common practices and provides a more automated and intelligent dependency management. Spring Boot offers a foundation for rapid development in various typical application domains, making it an application-centric framework collection.
  • Spring Cloud, which can be considered as a higher-level framework built on top of Spring Boot. It provides common patterns for building distributed systems, including service discovery and registration, distributed configuration management, load balancing, distributed diagnostics, and various other subsystems, simplifying the construction of microservice systems.
  • Of course, there are also Spring Security, Spring Data, and other domain-specific Spring modules.

The above introduction is quite general, and with regards to such a wide range of content, if you set your goals too broad, you may get lost in the Spring ecosystem. I suggest deep diving into the modules you are currently using, such as Spring MVC. At the same time, it’s important to have an overall understanding of the application scope and internal design of major cutting-edge frameworks like Spring Cloud. At the very least, you should be familiar with the main components and their specific purposes, as building microservices has gradually become one of the hot topics in Java application development interviews.

Thirdly, let’s discuss the details of Spring AOP’s design and implementation.

First, let’s ask ourselves, why do we need aspect-oriented programming?

Aspect-oriented programming is actually applied in software engineering to achieve better modularity, not just to reduce repetitive code. Through mechanisms like AOP, we can extract cross-cutting concerns that span multiple modules, making the modules themselves more cohesive. As a result, business developers can focus more on the business logic. In terms of iterative ability, we can modify or add functionality through aspect-oriented methods, which is very useful both in problem diagnosis and product capability extension.

In the previous analysis, we have discussed the implementation principle of AOP Proxy. A brief review: it is based on technologies such as JDK dynamic proxy or cglib bytecode manipulation. At runtime, a dynamic subclass of the called type is generated, and proxy objects are instantiated. The actual method invocation is delegated to the corresponding proxy object. However, this does not explain what aspects are and how to define join points and aspect behaviors at the AOP design level.

Spring AOP introduces several other key concepts:

  • Aspect: It is a cross-cutting logic that operates across different layers of Java classes. In terms of implementation, it can be an ordinary class configured in an XML file or declared with the @Aspect annotation in class code. At runtime, the Spring framework will create something similar to an Advisor to represent it. Internally, it includes the join points (Pointcut) and aspect actions (Advice).
  • Join Point is a specific point at which an Aspect can be applied. In Spring, only methods can act as Join Points.
  • Advice defines the actions that can be taken within an aspect. If you look at the Spring source code, you will find that Advice and Join Point are not defined within Spring’s own namespace. This is because they originate from the AOP Alliance, and can be seen as a common specification for Java engineers to communicate at the AOP level.

Similar code exists in the core Java library. For example, the Flow API introduced in Java 9 is the smallest subset of the Reactive Stream specification. This ensures seamless communication between different products and promotes the adoption of good practices.

Please refer to the diagram below for the specific structure of Spring Advice.

Among them, BeforeAdvice and AfterAdvice, including their subinterfaces, are the simplest implementations. Interceptor is the so-called interceptor, used to intercept method (including constructor) invocation events and take corresponding actions. Therefore, Interceptor is an Advice that covers the entire method invocation process. Interceptor Advice is usually referred to as Around, which can be marked with “@Around” in the code or used with <aop:around> in the configuration.

If we look at the timing, we can refer to the diagram below to understand when specific actions occur.

  • Join Point is just an available opportunity.
  • Pointcut solves the “Where” problem in aspect-oriented programming, allowing the program to know which join points a certain aspect action can be applied to.
  • Advice clarifies the “What” in aspect-oriented programming, which means what to do; and by specifying Before, After, or Around, it defines the “When”, which means when to do.

When preparing for an interview, it is best to have practical experience with AOP. Otherwise, you can choose a typical AOP example to understand the specific implementation syntax details, as these technical details may be asked in the interview.

If you are interested in delving into the internals, it is best to combine it with the Bean lifecycle and understand how Spring parses AOP-related annotations or configuration items and when and where dynamic proxy and other mechanisms are used. To avoid getting overwhelmed by complex source code, I suggest starting with a relatively simple test case, such as CglibProxyTests.

In addition, the Spring framework itself has many features, and AOP is not the only aspect technology it supports. It can only perform runtime weaving using dynamic proxies, and cannot perform compile-time static weaving or class load-time weaving. For example, on the Java platform, we can use Java Agent technology to manipulate bytecode during the class loading process, such as modifying or replacing method implementations. How can we achieve similar functionality in the Spring ecosystem? You can use AspectJ, which has more comprehensive capabilities but is also more complex to use.

Today, I started with a common Spring interview question and discussed the basic mechanisms of Spring, explored the scope of the Spring ecosystem, and supplemented the analysis of some design details of AOP. I hope this is helpful to you.

Practice Exercise #

Do you have a clear understanding of the topic we discussed today? Today’s question for reflection is: Please introduce the implementation mechanism of Spring’s declarative transaction, and consider drawing a diagram to illustrate the specific process.

Please write your thoughts on this question in the comment section. I will select well-considered comments and reward you with a learning voucher. Feel free to engage in a discussion with me.

Are your friends also preparing for interviews? You can “invite friends to read” and share today’s question with them. Perhaps you can help them.