IMPORTANT:
Some of the content here is a personal summary/abbreviation of contents on the Offical Spring Framework Documentation. Feel free to refer to the official site if you think some of the sections written here are not clear.
This is a Guide, since it is mostly conceptual and does not include much actual coding (except for examples illustrating those concepts). For more practical uses/examples, please refer to the Spring Boot Manual or the Spring Cloud Manual.
Spring Intro
The term “Spring” means different things in different contexts. It can be used to refer to the Spring Framework project itself, which is where it all started. Over time, other Spring projects have been built on top of the Spring Framework. Most often, when people say “Spring”, they mean the entire family of projects. This reference documentation focuses on the foundation: the Spring Framework itself.
When you learn about a framework, it’s important to know not only what it does but what principles it follows. Here are the guiding principles of the Spring Framework:
- Provide choice at every level. Spring lets you defer design decisions as late as possible. For example, you can switch persistence providers through configuration without changing your code.
- Accommodate diverse perspectives. Spring embraces flexibility and is not opinionated about how things should be done. It supports a wide range of application needs with different perspectives.
- Maintain strong backward compatibility. Spring’s evolution has been carefully managed to force few breaking changes between versions. Spring supports a carefully chosen range of JDK versions and third-party libraries to facilitate maintenance of applications and libraries that depend on Spring.
- Care about API design. The Spring team puts a lot of thought and time into making APIs that are intuitive and that hold up across many versions and many years.
- Set high standards for code quality. The Spring Framework puts a strong emphasis on meaningful, current, and accurate javadoc. It is one of very few projects that can claim clean code structure with no circular dependencies between packages.
IoC - Inversion of Control
IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes or a mechanism such as the Service Locator pattern.
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.
The main tasks performed by IoC container are:
- to instantiate the application class
- to configure the object
- to assemble the dependencies between the objects
And there are two types of IoC containers. They are:
- BeanFactory (
org.springframework.beans
) - ApplicationContext (
org.springframework.context
)
In short, the BeanFactory
provides the configuration framework and basic functionality, and the ApplicationContext
adds more enterprise-specific functionality. The ApplicationContext
is a complete superset of the BeanFactory
and is used exclusively in this chapter in descriptions of Spring’s IoC container. For more information on using the BeanFactory
instead of the ApplicationContext
, see The BeanFactory.
Java Beans vs Spring Beans vs POJOs
(additional reference: http://www.shaunabram.com/beans-vs-pojos/)
JavaBeans are classes that encapsulate many objects into a single object (the bean). It is a java class that should follow following conventions:
Must implement
Serializable.
It should have a public
no-arg
constructor.All properties in java bean must be
private
withpublic getters()
andpublic setter()
methods.1
2
3
4
5
6
7
8
9
10public class TestBean {
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
setter methods:
- It should be
public
in nature. - The return-type should be
void
. - The setter method should be prefixed with
set
. - It should take some argument i.e. it should not be no-arg method.
getter methods
- It should be
public
in nature. - The return-type should not be
void
i.e. according to our requirement we have to give return-type. - The getter method should be prefixed with
get
. - It should not take any argument.
Boolean properties getter method
- name can be prefixed with either “
get
” or “is
”. But recommended to use “is
”. - returns a
boolean
POJO is an acronym for Plain Old Java Object. The term was coined by Martin Fowler et. al., as a ‘fancy’ way to describe ordinary Java Objects that do not require a framework to use, nor need to be run in a application server environment.
A Spring bean is basically an object managed by Spring. More specifically, it is an object that is instantiated, configured and otherwise managed by a Spring Framework container. Spring beans are defined in a Spring configuration file (or, more recently, by using annotations), instantiated by the Spring container, and then injected into your application.
Spring can manage just about any object, even if it doesn’t have JavaBean type characteristics such as default constructors or mutator methods (getters and setters).
IoC Container Overview
The org.springframework.context.ApplicationContext
interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans. The container gets its instructions on what objects to instantiate, configure, and assemble by reading configuration metadata.
The configuration metadata is represented in XML, Java annotations, or Java code.
The following diagram 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.
A Spring IoC container manages one or more beans. These beans are created with the configuration metadata that you supply to the container (e.g. via an XML file).
Within the container itself, these bean definitions are represented as BeanDefinition
objects, which contain (among other information) the following metadata:
- A package-qualified class name: typically, the actual implementation class of the bean being defined.
- Bean behavioral configuration elements, which state how the bean should behave in the container (
scope
,lifecycle callbacks
, and so forth). - References to other beans that are needed for the bean to do its work. These references are also called collaborators or dependencies.
- Other configuration settings to set in the newly created object — for example, the size limit of the pool or the number of connections to use in a bean that manages a connection pool.
This metadata translates to a set of properties that make up each bean definition. The following table describes these properties:
Property | Explained in |
---|---|
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 |
Configuration Metadata
As the preceding diagram shows, the Spring IoC container consumes a form of configuration metadata. This configuration metadata represents how you, as an application developer, tell the Spring container to instantiate, configure, and assemble the objects in your application.
Configuration metadata is can be supplied using one of the three forms:
- XML file
- Java-based configuration
- Annotation-based configuration
(the following chapters only cover Java-based Configuration. If you prefer other means of configuration, please refer to the Offical Documentation for more information.)
Java Based Configuration
The central artifacts in Spring’s new Java-configuration support are @Configuration
-annotated classes and @Bean
-annotated methods.
The @Bean
annotation is used to indicate that a method instantiates, configures, and initializes a new object (type signature of the method) to be managed by the Spring IoC container. For those familiar with Spring’s <beans/>
XML configuration, the @Bean
annotation plays the same role as the <bean/>
element. You can use @Bean
-annotated methods with any Spring @Component
. However, they are most often used with @Configuration
beans.
Annotating a class with @Configuration
indicates that its primary purpose is as a source of bean definitions. Furthermore, @Configuration
classes let inter-bean dependencies be defined by calling other @Bean
methods in the same class. The simplest possible @Configuration
class reads as follows:
1 |
|
Note:
- Unlike full
@Configuration
, lite@Bean
methods (declared within classes that are not annotated with @Configuration) cannot declare inter-bean dependencies. Instead, they operate on their containing component’s internal state and, optionally, on arguments that they may declare. Such a@Bean
method should therefore not invoke other@Bean
methods. Each such method is literally only a factory method for a particular bean reference, without any special runtime semantics.
The @Bean
and @Configuration
annotations are discussed in depth in the following sections. First, however, we need to cover the various ways of creating a spring container using by Java-based configuration.
Instantiating the Spring Container Using AnnotationConfigApplicationContext
When @Configuration
classes are provided as input, the @Configuration
class itself is registered as a bean definition and all declared @Bean
methods within the class are also registered as bean definitions.
This means that instead of using XML files, you can use @Configuration
classes as input when instantiating an AnnotationConfigApplicationContext
. This allows for completely XML-free usage of the Spring container, as the following example shows:
1 | public static void main(String[] args) { |
AnnotationConfigApplicationContext
is not limited to working only with @Configuration
classes. Any @Component
or JSR-330 annotated class may be supplied as input to the constructor, as the following example shows:
1 | public static void main(String[] args) { |
The preceding example assumes that MyServiceImpl
, Dependency1
, and Dependency2
use Spring dependency injection annotations such as @Autowired
.
Building the Container Programmatically by Using register(Class<?>…)
One thing we see before for using AnnotationConfigApplicationContext
is that the configurations need to be specified when you instantiate the ApplicationContext
. However, sometimes you might need to programmatically add configurations later. This can be done by instantiate an AnnotationConfigApplicationContext
by using a no-arg constructor and then configure it by using the register()
method. This approach is particularly useful when programmatically building an AnnotationConfigApplicationContext
. The following example shows how to do so:
1 | public static void main(String[] args) { |
Enabling Component Scanning with scan(String…)
To enable component scanning, you can annotate your @Configuration
class as follows:
1 |
|
In the preceding example, the com.acme
package is scanned to look for any @Component
-annotated classes, and those classes are registered as Spring bean definitions within the container.
Additionally, you can use the AnnotationConfigApplicationContext
object with the scan(String…)
method to allow for the same component-scanning functionality, as the following example shows:
1 | public static void main(String[] args) { |
In the preceding example, assuming that AppConfig
is declared within the com.acme
package (or any package underneath). The above works since @Configuration
classes are meta-annotated with @Component
, so they are candidates for component-scanning as well. Then the AppConfig
along with its components is picked up during the call to scan()
. Upon refresh()
, all its @Bean
methods are processed and registered as bean definitions within the container.
Using the @Bean
Annotation
You can use the @Bean
annotation in a @Configuration
-annotated or in a @Component
-annotated class.
To declare a bean, you can annotate a method with the @Bean
annotation. You use this method to register a bean definition within an ApplicationContext
of the type specified as the method’s return value. By default, the bean name is the same as the method name. The following example shows a @Bean
method declaration:
1 |
|
The preceding configuration is exactly equivalent to the following Spring XML:
1 | <beans> |
Both would make a bean (method for getting an Object) named transferService
available in the ApplicationContext
, bound to an object instance of type TransferServiceImpl
, as the following text image shows:
1 | transferService -> com.acme.TransferServiceImpl |
You can also declare your @Bean
method with an interface (or base class) return type, as the following example shows:
1 |
|
However, this limits the visibility for advance type prediction to the specified interface type (TransferService
), such that the full type (TransferServiceImpl
) is known to the container only once.
Determining a Bean’s Runtime Type
The runtime type of a specific bean is non-trivial to determine. A specified class in the bean metadata definition is just an initial class reference, potentially combined with a declared factory method or being a FactoryBean class which may lead to a different runtime type of the bean, or not being set at all in case of an instance-level factory method (which is resolved via the specified factory-bean name instead).
The recommended way to find out about the actual runtime type of a particular bean is a BeanFactory.getType
call for the specified bean name. This takes all of the above cases into account and returns the type of object that a BeanFactory.getBean call is going to return for the same bean name.
Bean Dependencies
A @Bean
-annotated method can have an arbitrary number of parameters that describe the dependencies required to build that bean. For instance, if our TransferService
requires an AccountRepository
, we can materialize that dependency with a method parameter, as the following example shows:
1 |
|
The resolution mechanism is pretty much identical to constructor-based dependency injection
. See below sections for more details.
Dependency Injection
Dependency injection (DI) is a process whereby objects define their dependencies (that is, the other objects with which they work) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method.
The container then injects those dependencies when it creates the bean.
Code is cleaner with the DI principle, and decoupling is more effective when objects are provided with 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, particularly when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.
DI exists in two major variants:
- Constructor-based dependency injection
- Setter-based dependency injection
Constructor-based dependency injection
(additional reference: https://www.tutorialspoint.com/spring/constructor_based_dependency_injection.htm)
Constructor-based DI is accomplished when the container invokes a class constructor with a number of arguments, each representing a dependency on the other class.
Consider you have an application which has a text editor component and you want to provide a spell check. Your standard code would look something like this:
1 | public class TextEditor { |
What we’ve done here is, create a dependency between the TextEditor and the SpellChecker.** In an inversion of control scenario, we would instead do something like this:**
1 | public class TextEditor { |
Here, the TextEditor
should not worry about SpellChecker
implementation. The SpellChecker
will be implemented independently and will be provided to the TextEditor
at the time of TextEditor
instantiation. This entire procedure is controlled by the Spring Framework.
Constructor Arguments Resolution
There may be an ambiguity while passing arguments to the constructor,** in case there are more than one parameters**. To resolve this ambiguity, the order in which the constructor arguments are defined in a bean definition is the order in which those arguments are supplied to the appropriate constructor. Consider the following class:
1 | package x.y; |
The following configuration works fine:
1 |
|
Let us check one more case where we pass different types to the constructor, and we want to specify a specific value for them. Consider the following class:
1 | package x.y; |
The container can also use type matching with simple types, if you explicitly specify the type of the constructor argument using the type attribute. For example −
1 |
|
If you are using XML, then the best way to pass constructor arguments is to use the index attribute to specify explicitly the index of constructor arguments. Here, the index is 0 based. For example:
1 | <beans> |
A final note, in case you are passing a reference to an object as an argument, you need to use ref attribute of <constructor-arg>
tag in the XML, and if you are passing a value
directly then you should use value attribute as shown above.
Setter-based Dependency Injection
Setter-based DI is accomplished by the container calling setter
methods on your beans after invoking a no-argument constructor or a no-argument static factory method to instantiate your bean.
For example:
(referece: https://www.tutorialspoint.com/spring/setter_based_dependency_injection.htm)
the content of TextEditor.java
file:
1 | public class TextEditor { |
Here you need to check the naming convention of the setter methods. To set an object SpellChecker
we are using setSpellChecker()
method which is very similar to Java POJO classes. Then the content of another dependent class file SpellChecker.java
could be:
1 | public class SpellChecker { |
Finally, the configuration would look like:
1 |
|
Finally, just to finish the example, you would have your main class (called Example
here):
1 | import com.mySpringProject.app.testIoC.AppConfig; |
You will see that the container managed the construction of objects/dependencies for us, so that SpellChecker
is first created, and then passed into the TextEditor
object. Lastly, you would be able to call the method spellCheck()
.
Constructor-based or Setter-based DI
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the @Required
annotation on a setter method can be used to make the property be a required dependency; however, constructor injection with programmatic validation of arguments is preferable.
Using only constructor injection, lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state.
Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency.
Lazy-initialized Beans
By default, ApplicationContext
implementations eagerly create and configure all singleton beans as part of the initialization process. Generally, this pre-instantiation is desirable, because errors in the configuration or surrounding environment are discovered immediately, as opposed to hours or even days later. When this behavior is not desirable, you can prevent pre-instantiation of a singleton bean by marking the bean definition as being lazy-initialized.** A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup**.
To achieve this in a Java-annotation based configuration, you add the @Lazy
to the configuration class. This will make all @Bean
bean methods to become lazily initialized:
1 | import org.springframework.context.annotation.Bean; |
If you only want to lazily initialize a speicific bean, then add @Lazy(true)
or @Lazy()
above that bean:
1 | import org.springframework.context.annotation.Bean; |
Component Scanning
(additional reference: https://www.baeldung.com/spring-bean-annotations)
Spring can automatically scan a package for beans if component scanning is enabled.
@ComponentScan
configures which packages to scan for classes with annotation configuration. We can specify the base package names directly with one of the basePackages
or value
arguments (value
is an alias for basePackages
):
1 |
|
Also, we can point to classes directly in the base packages with the basePackageClasses
argument:
1 |
|
Alternatively, we can use** @ComponentScans
to specify multiple @ComponentScan
configurations**:
1 |
|
Using @Component
Annotation
@Component
is a class level annotation. During the component scan, Spring Framework automatically detects classes annotated with @Component
.
For example:
1 |
|
By default, the bean instances of this class have the same name as the class name with a lowercase initial. On top of that, we can specify a different name using the optional value argument of this annotation.
Since @Repository
,@Service
, @Configuration
, and @Controller
are all meta-annotations of @Component
, they share the same bean naming behavior. Also, Spring automatically picks them up during the component scanning process.
For example, you can have an @Component
with:
1 | import org.springframework.context.annotation.Bean; |
Then in your main @Configuration
, you have:
1 | import org.springframework.context.annotation.*; |
Finally, in your main class, you would be able to use WordDictionary
class even if it was not specified in the ApplicationContext
:
1 | import com.mySpringProject.app.testIoC.AppConfig; |
Using @Autowired to configure your beans and dependencies
(additional reference: https://www.baeldung.com/spring-autowire)
Starting with Spring 2.5, the framework introduced a new style of Dependency Injection driven by @Autowired
Annotations. This annotation allows Spring to automatically resolve and inject collaborating beans into your bean. It has the following adavantages:
Autowiring can significantly reduce the need to specify properties or constructor arguments. (Other mechanisms such as a bean template discussed elsewhere in this chapter are also valuable in this regard.)
Autowiring can update a configuration as your objects evolve. For example, if you need to add a dependency to a class, that dependency can be satisfied automatically without you needing to modify the configuration. Thus autowiring can be especially useful during development, without negating the option of switching to explicit wiring when the code base becomes more stable.
Once annotation injection is enabled, autowiring can be used on properties, setters, and constructors.
@Autowired
on properties
First, to enable annotation-driven injection, you need to be using AnnotationConfigApplicationContext
to load your spring configuration as below:
1 | public class Example { |
Then, you can have the following Component
s/classes:
1 | package com.mySpringProject.app.testIoC.AutowiredSamples; |
And importantly, this class:
1 | package com.mySpringProject.app.testIoC.AutowiredSamples; |
Now, all you need to do in your @Configuration
clas is to point it to the correctly component using the component scan:
1 | import org.springframework.context.annotation.Configuration; |
Then, in the main application class, we can see the message by:
1 | import com.mySpringProject.app.testIoC.AutowiredConfig; |
@Autowired
on constructors
The @Autowired annotation can also be used on constructors. In the below example, when the annotation is used on a constructor, an instance of FooFormatter is injected as an argument to the constructor when FooService is created:
1 |
|
Now, an instance of FooFormatter
is injected as an argument to the constructor when FooService
is created.
This technically has the same effect as using @Autowired
on a specific field, but it turns out that** using @Autowired
on a constructor is always the better approach**. This is because:
- you can only
@Autowired
one field at a time, but above a constructor, all arguments will be instantiated/autowired. - using
@Autowired
on a field is vulnerable for the problem of having aNullPointerException
. This is because if the autowired field is not correctly instantiated, the** main object that depends on it might still be created, but invoking methods of that private field will causeNullPointerException
. However, by **having the@Autowired
for a constructor, it forces the main object to be created WITH an instantiation of that dependent object.
Optional Dependencies for @Autowired
(additional reference: https://www.baeldung.com/spring-autowire)
Spring expects @Autowired
dependencies to be available when the dependent bean is being constructed. If the framework cannot resolve a bean for wiring, it will throw the below-quoted exception and prevent the Spring container from launching successfully:
1 | Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: |
To avoid this from happening, a bean can optional be specified as below:
1 | public class FooService { |
Bean Scopes
(additional reference: https://www.baeldung.com/spring-bean-scopes)
When you create a bean definition, you create a recipe for creating actual instances of the class defined by that bean definition. The idea that a bean definition is a recipe is important, because it means that, as with a class, you can create many object instances from a single recipe.
In addition to all those configurations that you can specify to a bean, you can also specify its scope:** the scope of a bean defines the life cycle and visibility of that bean in the contexts in which it is used**.
The latest version of Spring framework defines 6 types of scopes:
The last four scopes listed above - request
, session
, application
and websocket
- are only available in a web-aware application.
Singleton Scope
Defining a bean with singleton scope means the container creates a single instance of that bean, and all requests for that bean name will return the same object, which is cached. Any modifications to the object will be reflected in all references to the bean. This scope is the default value if no other scope is specified.
For example, if you have a Person object that stores a name:
1 | public class Person { |
Afterwards, we define the bean with singleton scope by using the @Scope
annotation:
1 |
|
Now, if you use the getBean(Person.class)
to create that object, you will only get one and the same instance of it no matter how many times you this method call. This also means that if you change the name
field of one of the Person
object you get, it will be changed for all Person
objects that you got.
Prototype Scope
A bean with prototype
scope will return a different instance every time it is requested from the container. It is defined by setting the value prototype
to the @Scope
annotation in the bean definition:
1 |
|
Now, if you make multiple calls of getBean(Person.class)
, you will get multiple Person
objects instantiated (as many as the number of times you called the method). This will not cause the problem that, if you change the name of one Person
object, all name
s of other Person
objects will be changed.
As mentioned, there are four additional scopes that are only available in a web-aware application context. These are less often used in practice:
Request Scope
The request scope creates a bean instance for a single HTTP request.
We can define the bean with request scope using the @Scope
annotation:
1 |
|
The proxyMode
attribute is necessary here because, at the moment of the instantiation of the web application context, there is no active request. Spring will create a proxy to be injected as a dependency, and instantiate the target bean when it is needed in a request.
We can also use a @RequestScope
composed annotation that acts as a shortcut for the above definition:
1 |
|
Next, we can define a controller
that has an injected reference to the requestScopedBean
. We need to access the same request twice in order to test the web specific scopes.
If we display the message each time the request is run, we can see that the value is reset to null, even though it is later changed in the method. This is because of a different bean instance being returned for each request.
1 |
|
Session Scope
The session scope creates for an HTTP Session.
We can define the bean with session scope in a similar manner:
1 |
|
There’s also a dedicated composed annotation we can use to simplify the bean definition:
1 |
|
Next, we define a controller with a reference to the sessionScopedBean
. Again, we need to run two requests in order to show that the value of the message field is the same for the session.
In this case, when the request is made for the first time, the value message is null. But once, it is changed, then that value is retained for subsequent requests as the same instance of the bean is returned for the entire session.
1 |
|
Application Scope
The application scope creates the bean instance for the lifecycle of a ServletContext.
This is similar to the singleton scope but there is a very important difference with regards to the scope of the bean.
When beans are application scoped the same instance of the bean is shared across multiple servlet-based applications running in the same ServletContext, while singleton-scoped beans are scoped to a single application context only.
Let’s create the bean with application scope:
1 |
|
Analogously as for the request and session scopes, we can use a shorter version:
1 |
|
Now, let’s create a controller that references this bean:
1 |
|
In this case, value message once set in the applicationScopedBean will be retained for all subsequent requests, sessions and even for a different servlet application that will access this bean, provided it is running in the same ServletContext.
Websocket Scope
The websocket scope creates it for a particular WebSocket session.
Finally, let’s create the bean with websocket scope:
1 | @Bean |
WebSocket-scoped beans when first accessed are stored in the WebSocket session attributes. The same instance of the bean is then returned whenever that bean is accessed during the entire WebSocket session.
We can also say that it exhibits singleton behavior but limited to a WebSocket session only.
Specifying Bean Scope
Spring includes the @Scope
annotation so that you can specify the scope of a bean.
You can specify that your beans defined with the @Bean
annotation should have a specific scope. You can use any of the standard scopes specified in the Bean Scopes section above.
The default scope is singleton, but you can override this with the @Scope annotation, as the following example shows:
1 |
|
Method Injection
In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container creates the singleton bean A only once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.
The solution is to use the @Lookup
, namely, Method Injection.
Using @Lookup for Method Injection
@Lookup
is a method annotated that tells Spring to return an instance of the method’s return type every time when we invoke it.
First, let’s create a prototype bean that we will later inject into a singleton bean:
1 | package com.mySpringProject.app.testIoC.MethodInjectionSamples; |
And if we create a singleton bean that uses @Lookup
:
1 | package com.mySpringProject.app.testIoC.MethodInjectionSamples; |
Using @Lookup
, we can get an new instance of SchoolNotification through our singleton bean** everytime when the method getSchoolNotification
is called** (assuming you have a ScopeConfig
class with @Configuration
and ComponentScan
setup correctly):
1 | package com.mySpringProject.app; |
Customizing the Nature of Bean
The Spring Framework provides a number of interfaces you can use to customize the nature of a bean. This section groups them as follows:
- Lifecycle Callbacks
ApplicationContextAware
andBeanNameAware
- Other
Aware
Interfaces
Lifecycle Callbacks
(additional reference: https://www.tutorialspoint.com/spring/spring_bean_life_cycle.htm; https://www.baeldung.com/running-setup-logic-on-startup-in-spring)
The life cycle of a Spring bean is easy to understand. When a bean is instantiated, it may be required to perform some initialization to get it into a usable state. Similarly, when the bean is no longer required and is removed from the container, some cleanup may be required.
The whole concept of lifecycle callbacks is to solve the problem of handling too much control to the container. For example, We can’t simply include our logic in the beans’ constructors or call methods after instantiation of any object; we are simply not in control during those processes:
1 |
|
Therefore, this is problematic because calling not yet initialized fields will of course result in NullPointerExceptions
.
Initialization Callbacks
The org.springframework.beans.factory.InitializingBean
interface lets a bean perform initialization work after the container has set all necessary properties on the bean. The InitializingBean
interface specifies a single method:
1 | void afterPropertiesSet() throws Exception; |
However, it is not recommend that you use the InitializingBean
interface, because it unnecessarily couples the code to Spring. Alternatively, we suggest using the @PostConstruct
annotation or specifying a POJO initialization method.
The @PostConstruct
annotation can be used for annotating** a method that should be run once immediately after the bean’s initialization**. Keep in mind that the annotated method will be executed by Spring even if there is nothing to inject.
Here’s @PostConstruct
in action:
1 |
|
In the example above you can see that the Environment instance was safely injected and then called in the @PostConstruct
annotated method without throwing a NullPointerException
.
Destruction Callbacks
Implementing the org.springframework.beans.factory.DisposableBean
interface lets a bean get a callback when the container that contains it is destroyed. The DisposableBean
interface specifies a single method:
1 | void destroy() throws Exception; |
It is recommended that you do not use the DisposableBean
callback interface, because it unnecessarily couples the code to Spring. Alternatively, we suggest using the @PreDestroy
annotation or specifying a generic method that is supported by bean definitions.
A method annotated with @PreDestroy
runs only once, just before Spring removes our bean from the application context.
Same as with @PostConstruct
, the methods annotated with @PreDestroy
can have any access level but can’t be static.
1 |
|
The purpose of this method should be to release resources or perform any other cleanup tasks before the bean gets destroyed, for example closing a database connection.
Note:
- Note that both
@PostConstruct
and@PreDestroy
annotations are part of Java EE. And since Java EE has been deprecated in Java 9 and removed in Java 11 we have to add an additional dependency to use these annotations if you are using versions of Java beyond 11.
1 | <dependency> |
Default initMethod
and destroyMethod
When you write initialization and destroy method callbacks that do not use the Spring-specific InitializingBean
and DisposableBean
callback interfaces, you should typically write methods with names such as init()
, initialize()
, dispose()
, and so on. Ideally, the names of such lifecycle callback methods are standardized across a project so that all developers use the same method names and ensure consistency.
More importantly, you can specify a default initMethod
property or a destroyMethod
that can be used to always execute a method after a bean’s initialization/before destruction.
Here’s what a bean looks like:
1 | public class InitMethodExampleBean { |
You can notice that there are no special interfaces implemented nor any special annotations used.
Then, we can define the bean using the @Bean(intiMethod="<initMethodName>")
annotation:
1 | "init") // the key step (initMethod= |
And this is will be equivalent to the XML config:
1 | <beans default-init-method="init"> |
The presence of the initMethod="init"
attribute on the top-level causes the Spring IoC container to recognize a method called init
on the bean class as the initialization method callback. When a bean is created and assembled, if the bean class has such a method, it is invoked at the appropriate time.
You can configure destroy method callbacks similarly by using the @Bean(destroyMethod="<destroyMethoName>")
attribute on the top-level.
Combining Lifecycle Mechanisms
Multiple lifecycle mechanisms configured for the same bean, with different initialization methods, are called as follows in order:
- Methods annotated with
@PostConstruct
afterPropertiesSet()
as defined by theInitializingBean
callback interface- A custom configured
init()
method
Destroy methods are called in the same order:
- Methods annotated with
@PreDestroy
destroy()
as defined by theDisposableBean
callback interface- A custom configured
destroy()
method
Startup and Shutdown Callbacks
The Lifecycle
interface defines the essential methods for any object that has its own lifecycle requirements (such as starting and stopping some background process):
1 | public interface Lifecycle { |
Any Spring-managed object may implement the Lifecycle
interface. Then, when the ApplicationContext
itself receives start and stop signals (for example, for a stop/restart scenario at runtime), it cascades those calls to all Lifecycle
implementations defined within that context. It does this by delegating to a LifecycleProcessor
, shown in the following listing:
1 | public interface LifecycleProcessor extends Lifecycle { |
Notice that the LifecycleProcessor
is itself an extension of the Lifecycle
interface. It also adds two other methods for reacting to the context being refreshed and closed.
Note:
- Note that the regular
org.springframework.context.Lifecycle
interface is a plain contract for explicit start and stop notifications and does not imply auto-startup at context refresh time. For fine-grained control over auto-startup of a specific bean (including startup phases), consider implementingorg.springframework.context.SmartLifecycle
instead.
Order of Startup and Shutdown with Dependencies
The order of startup and shutdown invocations can be important. If a “depends-on” relationship exists between any two objects, the dependent side starts after its dependency, and it stops before its dependency. However, sometimes you might not want this order of execution. In this case, you would need to use the SmartLifeCycle
Interface:
1 | public interface SmartLifecycle extends Lifecycle, Phased { |
and the Phased
interface looks like:
1 | public interface Phased { |
When starting, the objects with the lowest phase start first. When stopping, the reverse order is followed. Therefore, an object that implements SmartLifecycle
and whose getPhase()
method returns Integer.MIN_VALUE
would be among the first to start and the last to stop. At the other end of the spectrum, a phase value of Integer.MAX_VALUE
would indicate that the object should be started last and stopped first (likely because it depends on other processes to be running). When considering the phase value, it is also important to know that the default phase for any “normal” Lifecycle object that does not implement SmartLifecycle
is 0
.
Shuttingdown a Non-Web application
If you use Spring’s IoC container in a non-web application environment (for example, in a rich client desktop environment), you should register a shutdown hook with the JVM. Doing so ensures a graceful shutdown and calls the relevant destroy methods on your singleton beans so that all resources are released. You must still configure and implement these destroy callbacks correctly (e.g. with @Bean(destoryMethod="dispose")
, and implement the dispose()
methods).
To register a shutdown hook, call the registerShutdownHook()
method that is declared on the ConfigurableApplicationContext
interface, as the following example shows:
1 | import org.springframework.context.ConfigurableApplicationContext; |
ApplicationContextAware
and BeanNameAware
When an ApplicationContext
creates an object instance that implements the org.springframework.context.ApplicationContextAware
interface, the instance is provided with a reference to that ApplicationContext
. This means that object could manipulate the container. The following listing shows the definition of the ApplicationContextAware interface:
1 | public interface ApplicationContextAware { |
Thus, beans can programmatically manipulate the ApplicationContext
that created them, through the ApplicationContext
interface or by casting the reference to a known subclass of this interface (such as ConfigurableApplicationContext
, which exposes additional functionality). One use would be the programmatic retrieval of other beans. Sometimes this capability 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 to beans as properties.
When an ApplicationContext
creates a class that implements the org.springframework.beans.factory.BeanNameAware
interface, the class is provided with a reference to the bean with name defined in its associated object definition. The following listing shows the definition of the BeanNameAware interface:
1 | public interface BeanNameAware { |
The callback is invoked after population of normal bean properties but before an initialization callback such as InitializingBean
, afterPropertiesSet
, or a custom init-method
.
Bean Definition Inheritance
(additional reference: https://www.tutorialspoint.com/spring/spring_bean_definition_inheritance.htm)
A bean definition can contain a lot of configuration information, including constructor arguments, property values, and container-specific information, such as the initialization method, a static factory method name, and so on. A child bean definition inherits configuration data from a parent definition. The child definition can override some values or add others as needed. Using parent and child bean definitions can save a lot of typing. Effectively, this is a form of templating.
In Java, this should be simple enough to have the extends
keyword for that specific class, and then use @Bean
at the correct place. You could also inherit an @Configuration
class, which will also inherit all the @Bean
configurations with it.
Without using the XML file, it should be similar to the normal Java inheritance.
Customizing your Container
Typically, an application developer does not need to subclass ApplicationContext implementation classes. Instead, the Spring IoC container can be extended by plugging in implementations of special integration interfaces, or simply by using some anotations, in order to add some customized functionalities for the container (e.g. @Autowired
is one example)
Using @Primary
Because autowiring by type may lead to multiple candidates, it is often necessary to have more control over the selection process. One way to accomplish this is with Spring’s @Primary
annotation. @Primary
indicates that a particular bean should be given preference when multiple beans are candidates to be autowired to a single-valued dependency. If exactly one primary bean exists among the candidates, it becomes the autowired value.
Consider the following configuration that defines firstMovieCatalog
as the primary MovieCatalog
if an MovieCatalog
is to be autowired:
1 |
|
With the preceding configuration, the following MovieRecommender
is autowired with the firstMovieCatalog
:
1 | public class MovieRecommender { |
Using @Qualifier
@Primary
is an effective way to use autowiring by type with several instances when one primary candidate can be determined. When you need even more control over the selection process, for example letting each bean to be autowired in a different setting, you can use Spring’s @Qualifier
annotation. You can associate qualifier values with specific arguments, narrowing the set of type matches so that a specific bean is chosen for each argument. In the simplest case, this can be a plain descriptive value, as shown in the following example:
1 | public class MovieRecommender { |
You can also specify the @Qualifier
annotation on individual constructor arguments or method parameters, as shown in the following example:
1 | public class MovieRecommender { |
Type Matching for Generics
In addition to the @Qualifier
annotation, you can use Java generic types as an implicit form of qualification. For example, suppose you have the following configuration:
1 |
|
Assuming that the preceding beans implement a generic interface, (that is, Store<String>
and Store<Integer>
), you can @Autowire
the Store
interface and the generic is used as a qualifier, as the following example shows:
1 |
|
Generic qualifiers also apply when autowiring List
s, Map
instances and Array
s. The following example autowires a generic List:
1 | // Inject all Store beans as long as they have an <Integer> generic |
Using @Resource
(additional reference: https://www.baeldung.com/spring-annotations-resource-inject-autowire)
The @Resource
annotation is part of the JSR-250 annotation collection and is packaged with Jakarta EE. This is used for field or setter dependency injection. This annotation has the following execution order:
- Match by Name (if a name is specified as an argument to
@Resource
, so it resolves the bean with the name specified) - Match by Type (if a name is not specified as an argument to
@Resource
, so it resolves the name by type ) - Match by Qualifier (if a name is specified as an argument to
@Resource
, but there is no bean with that name nor resolvable by type, then need to be resolved by@Qualifier
)
These execution paths are applicable to both setter and field injection.
Field Injection with
@Resource
Match by name:
1
2
3
4
5
6
7
8
9
10
11
12import javax.annotation.Resource;
import java.io.File;
public class FieldResourceInjection {
"namedFile") // injection by finding the corresponding Bean (name=
private File defaultFile;
public void printMessage(){
System.out.println("Hello");
}
}Your configuration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.File;
public class ResourceConfig {
"namedFile") // match by name (name=
public File generateFile(){
return new NamedFile("test NamedFile");
}
"annotatedFile") (name=
public File generateAnotatedFile(){
return new AnnotatedFile("test NamedFile");
}
public FieldResourceInjection testInjectionByName(){
return new FieldResourceInjection();
}
}Match by Type:
This works the same as having
@Autowired
, so that you canont have two beans with the same type produced:1
2
3
4
5
6
7
8
9
10
11
12import javax.annotation.Resource;
import java.io.File;
public class FieldResourceInjection {
// injection by finding the corresponding type amongst the Beans
private File defaultFile;
public void printMessage(){
System.out.println("Hello");
}
}The Configuration file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.File;
public class ResourceConfig {
"namedFile") // won't work if there is another Bean with outputting a File object (name=
public File generateFile(){
return new NamedFile("test NamedFile");
}
public FieldResourceInjection testInjectionByName(){
return new FieldResourceInjection();
}
}Match by Qualifier:
This works the same as match by name, but instead of specifying the name in the
@Resource
, you specify in@Qualifier()
:1
2
3
4
5
6
7
8
9
10
11
12
13import javax.annotation.Resource;
import java.io.File;
public class FieldResourceInjection {
"namedFile") // injection by finding the corresponding Bean (
private File defaultFile;
public void printMessage(){
System.out.println("Hello");
}
}Your configuration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.File;
public class ResourceConfig {
"namedFile") // match by qualifier, but pretty much the same as Match By Name (name=
// properly, the following also works
// @Bean
// @Qualifer("namedFile")
public File generateFile(){
return new NamedFile("test NamedFile");
}
"annotatedFile") (name=
public File generateAnotatedFile(){
return new AnnotatedFile("test NamedFile");
}
public FieldResourceInjection testInjectionByName(){
return new FieldResourceInjection();
}
}
Setter Injection
The only difference is that the
@Resource
is placed above the setter method instead of the field. For example, matching by name would look like:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import javax.annotation.Resource;
import java.io.File;
public class FieldResourceInjection {
private File defaultFile;
"namedFile") // injection by finding the corresponding Bean (name=
public void setFile(File namedFile){
this.defaultFile = namedFile;
}
public void printMessage(){
System.out.println("Hello");
}
}Your configuration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.File;
public class ResourceConfig {
"namedFile") // match by name (name=
public File generateFile(){
return new NamedFile("test NamedFile");
}
"annotatedFile") (name=
public File generateAnotatedFile(){
return new AnnotatedFile("test NamedFile");
}
public FieldResourceInjection testInjectionByName(){
return new FieldResourceInjection();
}
}the other matching schemes follow the same pattern, so they are not shown here.
Using @Value
@Value
is typically used to inject externalized properties:
1 |
|
With the following configuration:
1 |
|
And the following application.properties
file:
1 | catalog.name=MovieCatalog |
In that case, the catalog
parameter and field will be equal to the "MovieCatalog"
value.
Also, built-in converter support provided by Spring allows simple type conversion (to Integer or int for example) to be automatically handled. Multiple comma-separated values can be automatically converted to String array without extra effort.
When @Value
contains a SpEL
expression the value will be dynamically computed at runtime as the following example shows:
1 | public class MovieRecommender { |
SpEL
also enables the use of more complex data structures:
1 |
|
Classpath Scanning and Managed Components
@Component
and Further Stereotype Annotations
(additional reference: https://www.baeldung.com/spring-bean-annotations)
For example, the @Repository
annotation is a marker for any class that fulfills the role or stereotype of a repository (also known as Data Access Object or DAO
). Among the uses of this marker is the automatic translation of exceptions.
Spring provides further stereotype annotations: @Component
, @Service
, and @Controller
. @Component
is a generic stereotype for any Spring-managed component. @Repository
, @Service
, and @Controller
are specializations of @Component
for more specific use cases (in the persistence, service, and presentation layers, respectively). Therefore, you can annotate your component classes with @Component
, but, by annotating them with @Repository
, @Service
, or @Controller
instead, your classes are more properly suited for processing by tools or associating with aspects.
Using @Repository
DAO
or Repository
classes usually represent the database access layer in an application, and should be annotated with @Repository
:
1 |
|
One advantage of using this annotation is that it has automatic persistence exception translation enabled. When using a persistence framework such as Hibernate, native exceptions thrown within classes annotated with @Repository
will be automatically translated into subclasses of Spring’s DataAccessExeption
.
To enable exception translation, we need to declare our own PersistenceExceptionTranslationPostProcessor
bean:
1 |
|
Note, that in most cases, Spring does the step above automatically.
Using @Service
The business logic of an application usually resides within the service layer – so we’ll use the @Service annotation to indicate that a class belongs to that layer:
1 |
|
Using @Controller
@Controller
is a class level annotation which tells the Spring Framework that this class serves as a controller in Spring MVC:
1 |
|
Automatic Detecting Stereotype Annotations
Spring can automatically detect stereotyped classes and register corresponding BeanDefinition
instances with the ApplicationContext
. For example, the following two classes are eligible for such autodetection:
1 |
|
and
1 |
|
To autodetect these classes and register the corresponding beans, you need to add @ComponentScan
to your @Configuration
class, where the basePackages attribute is a common parent package for the two classes. (Alternatively, you can specify a comma- or semicolon- or space-separated list that includes the parent package of each class.)
1 |
|
Customizing ComponentScanning using Filters
By default, classes annotated with @Component
,@Repository
, @Service
, @Controller
, @Configuration
, or a custom annotation that itself is annotated with @Component
are the only detected candidate components. However, you can modify and extend this behavior by applying custom filters. Add them as includeFilters
or excludeFilters
attributes of the @ComponentScan
annotation.
Filter Type | Example Expression | Description |
---|---|---|
annotation (default) | SomeAnnotation | An annotation to be present or meta-present at the type level in target components. |
assignable | SomeClass | A class (or interface) that the target components are assignable to (extend or implement). |
aspectj | .*Service+ | An AspectJ type expression to be matched by the target components. |
regex | org.example.Default.* | A regex expression to be matched by the target components’ class names. |
custom | MyTypeFilter | A custom implementation of the org.springframework.core.type.TypeFilter interface. |
For example, you can have the configuration to** ignoring all @Repository
annotations** and using “stub” repositories instead:
1 |
|
AOP - Aspect Oriented Programming with Spring
(additional reference: https://www.tutorialspoint.com/springaop/springaop_overview.htm)
Aspect-oriented Programming (AOP) complements Object-oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns (such as transaction management) that cut across multiple types and objects. (Such concerns are often termed “crosscutting” concerns in AOP literature.)
One of the key components of Spring is the AOP framework. While the Spring IoC container does not depend on AOP (meaning you do not need to use AOP if you don’t want to), AOP complements Spring IoC to provide a very capable middleware solution.
The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Dependency Injection helps you decouple your application objects from each other, while AOP helps you decouple cross-cutting concerns from the objects that they affect. AOP is like triggers in programming languages such as Perl, .NET, Java, and others.
Spring AOP module lets interceptors intercept an application. For example, when a method is executed, you can add extra functionality before or after the method execution.
Core Concepts in AOP
Before we start working with AOP, let us become familiar with the AOP concepts and terminologies. These terms are not specific to Spring, rather they are related to AOP.
Terms | Description |
---|---|
Aspect | A module which has a set of APIs providing cross-cutting requirements. For example, a logging module would be called AOP aspect for logging. An application can have any number of aspects depending on the requirement. |
Join point | This represents a point in your application where you can plug-in AOP aspect. You can also say, it is the actual place in the application where an action will be taken using Spring AOP framework. |
Advice | This is the** actual action to be taken either before or after the method execution. This is the **actual piece of code that is invoked during program execution by Spring AOP framework. |
PointCut | This is a set of one or more joinpoints where an advice should be executed. You can specify PointCuts using expressions or patterns as we will see in our AOP examples. |
Introduction | An introduction allows you to add new methods or attributes to existing classes. |
Target object | The object being advised by one or more aspects. This object will always be a proxied object. Also referred to as the advised object. |
AOP proxy | An object created by the AOP framework in order to implement the aspect contracts (advise method executions and so on). In the Spring Framework, an AOP proxy is a JDK dynamic proxy or a CGLIB proxy. |
Weaving | Weaving is the process of linking aspects with other application types or objects to create an advised object. This can be done at compile time, load time, or at runtime. |
Spring aspects can work with five kinds of advice mentioned in the following table.
Advice Type | Description |
---|---|
before | Run advice before the method execution. |
after | Run advice after the method execution, regardless of its outcome. |
after-returning | Run advice after the method execution, only if the method completes successfully. |
after-throwing | Run advice after the method execution, only if the method exits by throwing an exception. |
around | Run advice before and after the advised method is invoked. |
Around advice is the most general kind of advice. Since Spring AOP, like AspectJ, provides a full range of advice types, it is recommended that you use the least powerful advice type that can implement the required behavior. For example, if you need only to update a cache with the return value of a method, you are better off implementing an after returning advice than an around advice, although an around advice can accomplish the same thing. Using the most specific advice type provides a simpler programming model with less potential for errors.
The concept of join points matched by pointcuts is the key to AOP, which distinguishes it from older technologies offering only interception. Pointcuts enable advice to be targeted independently of the object-oriented hierarchy. For example, you can apply an around advice providing declarative transaction management to a set of methods that span multiple objects (such as all business operations in the service layer).
Enabling AspectJ
To use annotations from AspectJ, you need to make sure that you have the aspectjweaver.jar
in your classpath. Since we are using Maven and will be mainly focused on Java-annotation based, it is as simple as adding a dependency in your POM
file:
1 | <dependency> |
Then, to enable @AspectJ
support with Java @Configuration
, add the @EnableAspectJAutoProxy
annotation, as the following example shows:
1 |
|
Note:
- By auto-proxying, we mean that, if Spring determines that a bean is advised by one or more aspects, it automatically generates a proxy for that bean to intercept method invocations and ensures that advice is executed as needed.
Declaring an Aspect
With @AspectJ
support enabled, any bean defined in your application context with a class that is an @AspectJ
aspect (has the @Aspect
annotation) is automatically detected by Spring and used to configure Spring AOP.
The following shows a NotVeryUsefulAspect
class definition, which is annotated with the org.aspectj.lang.annotation.Aspect annotation
to become an aspect:
1 | package org.xyz; |
Aspects (classes annotated with @Aspect
) can have methods and fields, the same as any other class. They can also contain pointcut, advice, and introduction (inter-type) declarations.
Note:
- You can register aspect classes as regular beans in your Spring XML configuration or autodetect them through classpath scanning — the same as any other Spring-managed bean. However, note that the
@Aspect
annotation is not sufficient for autodetection in the classpath. For that purpose, you need to add a separate@Component
annotation (or, alternatively, a custom stereotype annotation that qualifies, as per the rules of Spring’s component scanner).- In Spring AOP, aspects themselves cannot be the targets of advice from other aspects. The
@Aspect
annotation on a class marks it as an aspect and, hence, excludes it from auto-proxying.
Declaring a Pointcut
Pointcuts determine join points of interest and thus enable us to control when advice executes. Spring AOP only supports method execution join points for Spring beans, so you can think of a pointcut as matching the execution of methods on Spring beans.
A pointcut declaration has two parts:
- a method signature comprising a name and any parameters
- this will look like a standard java method, except that it must have a
void
return type
- this will look like a standard java method, except that it must have a
- a pointcut expression that determines exactly which method executions we are interested in.
The following example defines a pointcut named anyOldTransfer
that matches the execution of any method named transfer:
1 | "execution(* transfer(..))") // the pointcut expression ( |
The pointcut expression that forms the value of the @Pointcut
annotation is a regular AspectJ 5 pointcut expression. For a full discussion of AspectJ’s pointcut language, see the Offical AspectJ Programming Guide.
Supported Pointcut Designators
Spring AOP supports the following AspectJ pointcut designators (PCD) for use in pointcut expressions:
execution
: For matching method execution join points. This is the primary pointcut designator to use when working with Spring AOP.within
: Limits matching to join points within certain types (the execution of a method declared within a matching type when using Spring AOP).this
: Limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type.target
: Limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type.args
: Limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types.@target
: Limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type.@args
: Limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given types.@within
: Limits matching to join points** within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP)**.@annotation
: Limits matching to join points where the subject of the join point (the method being executed in Spring AOP) has the given annotation.bean
: Spring AOP also supports an additional PCD namedbean
. This PCD lets you limit the matching of join points to a particular named Spring bean or to a set of named Spring beans (when using wildcards).
More on using "execution"
Spring AOP users are likely to use the execution pointcut designator the most often. The format of an execution expression follows:
1 | execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) |
All parts except the returning type pattern (ret-type-pattern in the preceding snippet), the name pattern, and the parameters pattern are optional (the ones ending with “?
“ are optional). The returning type pattern determines what the return type of the method must be in order for a join point to be matched. *
is most frequently used as the returning type pattern. It matches any return type. A fully-qualified type name matches only when the method returns the given type. The name pattern matches the method name. You can use the *
wildcard as all or part of a name pattern. If you specify a declaring type pattern, include a trailing .
to join it to the name pattern component. The parameters pattern is slightly more complex: ()
matches a method that takes no parameters, whereas (..)
matches any number (zero or more) of parameters. The (*)
pattern matches a method that takes one parameter of any type. (*,String)
matches a method that takes two parameters. The first can be of any type, while the second must be a String. Consult the Language Semantics section of the AspectJ Programming Guide for more information.
Note:
- Declaring type pattern means: Package or class (ex:
com.app.service.*
- applies to all classes in this package;com.app.service.UserService
- applies only to UserService class;*
- all)
Combining PointCut Expressions
You can combine pointcut expressions by using &&
, ||
and !
. You can also refer to pointcut expressions by name(method name). The following example shows three pointcut expressions:
1 | "execution(public * *(..))") // anyPublicOperation matches if a method execution ( |
Examples of common PCDs
The following examples show some common pointcut expressions:
The execution of any public method (any return-type, any name, any number of arguments):
1 | execution(public * *(..)) |
The execution of any method with a name that begins with set
:
1 | execution(* set*(..)) |
The execution of any method defined by the AccountService
interface:
1 | execution(* com.xyz.service.AccountService.*(..)) |
The execution of any method defined in the service
package:
1 | execution(* com.xyz.service.*.*(..)) // notice the extra "." needed connecting the package name and method name |
The execution of any method defined in the service
package or one of its sub-packages:
1 | execution(* com.xyz.service..*.*(..)) |
Any join point (method execution only in Spring AOP, not all methods) within the service package:
1 | within(com.xyz.service.*) |
Any join point (method execution only in Spring AOP) within the service package or one of its sub-packages:
1 | within(com.xyz.service..*) |
Any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface:
1 | this(com.xyz.service.AccountService) |
Any join point (method execution only in Spring AOP) where the target object implements the AccountService
interface:
1 | target(com.xyz.service.AccountService) |
Any join point (method execution only in Spring AOP) that takes a single parameter and where the argument passed at runtime is Serializable
:
1 | args(java.io.Serializable) |
Any join point (method execution only in Spring AOP) where the target object has a @Transactional
annotation:
1 | (org.springframework.transaction.annotation.Transactional) |
Any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional
annotation:
1 | @within(org.springframework.transaction.annotation.Transactional) |
Any join point (method execution only in Spring AOP) where the executing method has an @Transactional
annotation:
1 | (org.springframework.transaction.annotation.Transactional) |
Any join point (method execution only in Spring AOP) which takes a single parameter, and where the runtime type of the argument passed has the @Classified
annotation:
1 | @args(com.xyz.security.Classified) |
Any join point (method execution only in Spring AOP) on a Spring bean named tradeService:
bean(tradeService)
Any join point (method execution only in Spring AOP) on Spring beans having names that match the wildcard expression *Service:
bean(*Service)
Structuring your Pointcuts
When working with enterprise applications, developers often want to refer to modules of the application and particular sets of operations from within several aspects. It is recommended to define a “SystemArchitecture
” aspect that captures common pointcut expressions for this purpose. Such an aspect typically resembles the following example:
1 | package com.xyz.someapp; |
Writing good Pointcuts
During compilation, AspectJ processes pointcuts in order to optimize matching performance. Examining code and determining if each join point matches (statically or dynamically) a given pointcut is a costly process. (A dynamic match means the match cannot be fully determined from static analysis and that a test is placed in the code to determine if there is an actual match when the code is running). On first encountering a pointcut declaration, AspectJ rewrites it into an optimal form for the matching process. What does this mean? Basically, pointcuts are rewritten in DNF (Disjunctive Normal Form) and the components of the pointcut are sorted such that those components that are cheaper to evaluate are checked first. This means you do not have to worry about understanding the performance of various pointcut designators and may supply them in any order in a pointcut declaration.
However, AspectJ can work only with what it is told. For optimal performance of matching, you should think about what they are trying to achieve and narrow the search space for matches as much as possible in the definition. The existing designators naturally fall into one of three groups: kinded, scoping, and contextual:
- Kinded designators select a particular kind of join point: execution, get, set, call, and handler.
- Scoping designators select a group of join points of interest (probably of many kinds): within and withincode
- Contextual designators match (and optionally bind) based on context: this, target, and @annotation
A well written pointcut should include at least the first two types (kinded and scoping). You can include the contextual designators to match based on join point context or bind that context for use in the advice. Supplying only a kinded designator or only a contextual designator works but could affect weaving performance (time and memory used), due to extra processing and analysis.
Declaring an Advice
Advice is** associated with a pointcut expression and runs before, after, or around method executions matched by the pointcut**. The pointcut expression may be either a simple reference to a named pointcut or a pointcut expression declared in place.
In plain words, pointcuts matches the place/method execution, and the advice is the actual piece of code that will execute at that pointcut position (could be before (the method execution), after (the method execution), or around (the method execution)).
Before
You can declare before
advice in an aspect by using the @Before
annotation. This will then execute (if matched) before the execution of the matched method:
1 | import org.aspectj.lang.annotation.Aspect; |
Now, you could utilize the pointcutting scheme you learnt, which can be applied in two ways in an Advice
:
- in-place pointcut expression
- refering to the pointcut expression
For example, the following two would be equivalent as the above:
1 | import org.aspectj.lang.annotation.Aspect; |
or the recommended if suitable approach:
1 | import org.aspectj.lang.annotation.Aspect; |
After Returning Advice
After returning advice runs when a matched method execution returns normally. You can declare it by using the @AfterReturning
annotation:
1 | import org.aspectj.lang.annotation.Aspect; |
Note that you can have multiple advice declarations (and other members as well), all inside the same aspect. We show only a single advice declaration in these examples to focus the effect of each one.
One useful feature of After returning advice is that you can use the return value of that method to do some additional work:
1 | import org.aspectj.lang.annotation.Aspect; |
The name used in the returning attribute must correspond to the name of a parameter in the advice method.** When a method execution returns, the return value is passed to the advice method as the corresponding argument value.** A returning clause also restricts matching to only those method executions that return a value of the specified type (in this case, Object, which matches any return value).
After Throwing Advice
After throwing advice runs when a matched method execution exits by throwing an exception. You can declare it by using the @AfterThrowing
annotation, as the following example shows:
1 | import org.aspectj.lang.annotation.Aspect; |
Often, you want the advice to run only when exceptions of a given type are thrown, and you also often need access to the thrown exception in the advice body. You can use the throwing
attribute to both restrict matching (if desired — use Throwable as the exception type otherwise) and bind the thrown exception to an advice parameter. The following example shows how to do so:
1 | import org.aspectj.lang.annotation.Aspect; |
The name used in the throwing attribute must correspond to the name of a parameter in the advice method. When a method execution exits by throwing an exception, the exception is passed to the advice method as the corresponding argument value. A throwing clause also restricts matching to only those method executions that throw an exception of the specified type (DataAccessException
, in this case).
After (Finally) Advice
After (finally) advice runs when a matched method execution exits. It is declared by using the @After
annotation. After advice must be prepared to handle both normal and exception return conditions. It is typically used for releasing resources and similar purposes. The following example shows how to use after finally advice:
1 | import org.aspectj.lang.annotation.Aspect; |
Around Advice
The last kind of advice is around advice. Around advice runs “around” a matched method’s execution. It has the opportunity to do work both before and after the method executes and to determine when, how, and even if the method actually gets to execute at all (see the next paragraph). Around advice is often used if you need to share state before and after a method execution in a thread-safe manner (starting and stopping a timer, for example). Always use the least powerful form of advice that meets your requirements (that is, do not use around advice if before advice would do).
Around advice is declared by using the @Around
annotation. The first parameter of the advice method must be of type ProceedingJoinPoint. Within the body of the advice, calling proceed()
on the ProceedingJoinPoint causes the underlying method to execute. The proceed method can also pass in an Object[]. The values in the array are used as the arguments to the method execution when it proceeds.
1 | import org.aspectj.lang.annotation.Aspect; |
Note that proceed()
may be invoked once, many times, or not at all within the body of the around advice. All of these are legal.
Additionally, there are other methods of the ProceedingJoinPoint
object that you can use to fetch information about the method that your are pointcutting:
getArgs()
: Returns the method arguments.getThis()
: Returns the proxy object.getTarget()
: Returns the target object.getSignature()
: Returns a description of the method that is being advised.toString()
: Prints a useful description of the method being advised.
Passing Parameters to Advice
We have already seen how to bind the returned value or exception value (using after returning and after throwing advice). To make argument values available to the advice body, you can use the binding form of args
.
If you use a parameter name in place of a type name in an args expression, the value of the corresponding argument is passed as the parameter value when the advice is invoked. An example should make this clearer. Suppose you want to advise the execution of DAO
operations that take an Account
object as the first parameter, and you need access to that Account
object in the advice body. You could write the following:
1 | "com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)") ( |
The args(account,..)
part of the pointcut expression serves two purposes.
- First, it restricts matching to only those method executions where the method takes at least one parameter, and the argument passed to that parameter is an instance of Account.
- Second, it makes the actual
Account
object available to the advice through the account parameter.
Another way of writing this is to declare a Pointcut
that “provides” the Account
object value when it matches a join point, and then refer to the named pointcut from the advice. This would look as follows:
1 | "com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)") // Pointcutting ( |
Using Advice Parameters for Generics
Spring AOP can handle generics used in class declarations and method parameters. Suppose you have a generic type like the following:
1 | public interface Sample<T> { |
You can restrict interception of method types to certain parameter types by typing the advice parameter to the parameter type for which you want to intercept the method:
1 | "execution(* ..Sample+.sampleGenericMethod(*)) && args(param)") ( |
Howver, this approach does not work for generic Collections. So you cannot define a pointcut as follows:
1 | "execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)") ( |
To make this work, we would have to inspect every element of the collection, which is not reasonable, as we also cannot decide how to treat null values in general. To achieve something similar to this, you have to type the parameter to Collection<?>
and manually check the type of the elements.
Advice Ordering
The problem comes when you have multiple advice executing at the same join point. Suppose you have two advices running at the same join point,, what would happen?
- The *highest precedence advice (
@Order(<the smallest int>)
) runs first “on the way in” *(so, given two pieces of before advice, the one with highest precedence runs first). - “On the way out” from a join point, the highest precedence advice (
@Order(<the smallest int>)
) runs last (so, given two pieces of after advice, the one with the highest precedence will run second).
Note that this means both (or more if you do have more) advices WILL execute, but the order of execution will be defined by the @Order
annotation that you specify.
For example:
1 |
|
Note:
- When two pieces of advice defined in different aspects both need to run at the same join point, unless you specify otherwise, the order of execution is undefined.
Using Introductions/@DeclareParents
Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare that advised objects implement a given interface, and to provide an implementation of that interface on behalf of those objects.
You can make an introduction by using the @DeclareParents
annotation. This annotation is used to declare that matching types have a new parent (hence the name).
For example, given an interface named UsageTracked
and an implementation of that interface named DefaultUsageTracked
, the following aspect declares that all implementors of service interfaces also implement the UsageTracked
interface.
1 | import org.aspectj.lang.annotation.Aspect; |
with the simple interface ProvidePrint
:
1 | package com.mySpringProject.app.testIoC.IntroductionsWithDeclareParents; |
Now, suppose there is just one class inside that package:
1 | package com.mySpringProject.app.testIoC.IntroductionsWithDeclareParents; |
and you have that defaultImp = DefaultPrint.class
:
1 | package com.mySpringProject.app.testIoC.IntroductionsWithDeclareParents; |
Then the result is that any bean
created inside/being matched by the package value="com.mySpringProject.app.testIoC.IntroductionsWithDeclareParents.*"
will all automatically implement the necessary method public void printIntroductionMessage()
, with the actual implementation using the one from defaultImpl = DefaultPrint.class
.
So that when you create that NormalObject
class using a bean, you can:
1 | NormalObject testObj = ctx.getBean(NormalObject.class); |
However, what if the interface method public void printIntroductionMessage();
has already been implemented by that NormalObject
class? It turns out that the container will run the method native to that NormalObject
class in the end. This means that the implementation in your DefaultImplementation
class will only run if the method has not been implemented.
Understanding the Mechanism of an AOP Proxy
Spring AOP is proxy-based. It is vitally important that you grasp the semantics of what that last statement actually means before you write your own aspects or use any of the Spring AOP-based aspects supplied with the Spring Framework.
Consider first the scenario where you have a plain-vanilla, un-proxied, nothing-special-about-it, straight object reference, as the following code snippet shows:
1 | public class SimplePojo implements Pojo { |
Now, when you use this class and call the method foo()
:
1 | public class Main { |
This means that JVM first reaches that object Pojo
, then looks for the foo()
method, and finally executes and returns the result back if there is any return
arguments.
However, things change slightly when the reference that client code has is a proxy. Consider the following diagram and code snippet:
1 | public class Main { |
The key thing to understand here is that the client code inside the main(..)
method of the Main class has a reference to the proxy, which contains an inner reference to the actual object you built. This means that method (pojo.foo()
) calls on that object reference are calls on the proxy. Therefore, you can see it as the proxy having the full power to control the executions of that object you built.
As a result, the proxy can delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object (the SimplePojo, reference in this case), any method calls that it may make on itself, such as this.bar()
or this.foo()
, are going to be invoked against the actual object/SimplePojo
reference, and not the proxy.
This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
Okay, so what is to be done about this? The best approach (the term, “best,” is used loosely here) is to refactor your code such that the self-invocation does not happen. This does entail some work on your part, but it is the best, least-invasive approach. The next approach is absolutely horrendous, and we hesitate to point it out, precisely because it is so horrendous. You can (painful as it is to us) totally tie the logic within your class to Spring AOP, as the following example shows:
1 | public class SimplePojo implements Pojo { |
This totally couples your code to Spring AOP, and it makes the class itself aware of the fact that it is being used in an AOP context, which flies in the face of AOP. It also requires some additional configuration when the proxy is being created, as the following example shows:
1 | public class Main { |
Finally, it must be noted that AspectJ does not have this self-invocation issue because it is not a proxy-based AOP framework. (However, since this guide do uses the AOP framework
, this is relevant.)
Manually Creating @AspectJ
Proxies
If you used @EnableAspectJAutoProxy
, then this will not be necessary (also, as I tested in my program, once this is declared (at any place), the auto proxy mechanism will be enabled everywhere in the same project). However, if you want to have full control of what is happening under the hood, you can choose to manually create/add proxies to your POJOs.
You can use the org.springframework.aop.aspectj.annotation.AspectJProxyFactory
class to create a proxy for a target object that is advised by one or more @AspectJ
aspects. The basic usage for this class is very simple, as the following example shows:
1 | // create a factory that can generate a proxy for the given target object |