Actutor
Actutor
Spring AOP
In this chapter, I will explain AOP, which is the core of Spring as well as DI.
Overview of AOP
Preparation before development
AOP implementation (log output)
Summary
What is AOP?
AOP terminology
Internal workings of AOP
For example, suppose you write a start log and an end log in each class
method. In that case, the code would look like this:
[Sample] Output start log and end log for each method
In AOP, these common processes are put together in one place. Then, you
can select which class and which method to apply the common processing.
[Outline of AOP]
In this way, common processing is managed separately. That way, you can
focus on the essential code (the code you should write) when creating each
class. Also, since common processing is omitted, the readability of the code
is improved.
The following are typical common processes that can be managed by AOP.
Log output
Security
Transaction
Exception handling
Cache
Retry
...etc.
In object-oriented, it is difficult to assign a common process to each object.
It is AOP that made it possible.
[AOP terminology]
Terminology Description
[Before]
[After]
[Around]
[AfterThrowing]
Advice is executed when the method ends abnormally (exception).
7.1.3 Internal workings of AOP
I will explain how it works as an internal mechanism of AOP. If you
understand this, it will be much easier to understand when making.
[Mechanism of AOP]
First, it tries to call the method of the Bean registered in the DI container. In
the above figure, "LoginController" is registered as Bean.
However, it does not call the bean method directly. Proxy is automatically
generated and Bean method is called via Proxy. Then, execute the advice
(AOP processing) before and after calling the bean method.
*For those who do not have time, you can download the source code at the
end of these preparations from the link at the end of this book. In that case,
please download the folder "SpringStart" in the folder "01. Preparation
point" in Chapter 7. [Click here for links at the end of the book]
Also, there are various patterns such as Before and After in AOP, so I will
explain how to implement them.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
(Omitted)
Specify the AOP target with the Bean name registered in the
bean
DI container.
SpringStart
└ src/main
├ java
│└ com.example.demo Package
│ ├ SpringStartApplication.java
│ ├ WebConfig.java
│ └ login Login processing package
│ ├ aspect AOP package
│ │└ LogAspect.java
│ ├ controller Controller class package
│ └ domain Business logic package
└ resouces
├ static Folder for static files
├ templates Folder for html files
├ application.properties
├ messages.properties
├ messages_en.properties
├ data.sql
└ schema.sql
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
// Point1 : @Aspect
@Aspect
@Component
public class LogAspect {
Point1 : @Aspect
Annotate the AOP class with "@Aspect". At the same time, add
"@Component" annotation to define Bean to DI container. Please
remember to add these two annotations as a set.
Regular expressions can be used for package names and class names.
* (Asterisk)
Use an asterisk to represent any character string. One asterisk
represents one level of package. One asterisk, one argument in the
method.
.. (Two dots)
If you write two dots in a row, the package description indicates any
(0 or more) package. In the method argument, it represents an
arbitrary (0 or more) argument.
+ (Plus)
A Plus after the class name includes subclasses of the specified class.
[Execution]
After creating it, launch Spring Boot and access the login screen. Then, the
following log is output to the console.
Method start: String
com.example.demo.login.controller.LoginController.getLogin(Model)
Method end: String
com.example.demo.login.controller.LoginController.getLogin(Model)
Now every time the LoginController's getLogin method is called, a log will
be output. Of course, if this is the case, no log will be output even if the
method of another controller class is called.
Next, we will try to make it a little more general. Change the argument in
@Before and @After annotation as follows. The underlined part in red is the
changed part.
[7-3-2]com.example.demo.login.aspect.LogAspect.java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
Package name
Specify "*..*" to target all packages.
Class name
By specifying "∗Controller", the class with "Controller" at the end of
the class name will be the target.
Method name
∗ (asterisk) applies to all methods.
Argument
.. (two dots) to target all arguments.
[Execution]
After creating it, start Spring Boot and try to access the login screen and
user registration screen. Then, the start/end logs of all the methods of the
controller class are output to the console (the execution results are omitted).
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
// Point 1: @Around
@Around("execution(∗ ∗..∗.∗Controller.∗(..))")
public Object startLog(ProceedingJoinPoint jp) throws Throwable {
try {
// Point 2: Method execution
Object result = jp.proceed();
return result;
} catch (Exception e) {
System.out.println("Method abnormal termination:
" + jp.getSignature());
e.printStackTrace();
throw e;
}
}
}
Point 1: @Around
When using "Around" at JoinPoint (execution timing), use "@Around"
annotation.
So, using "Around", you can do arbitrary processing before and after the
method execution. Since the method is executed directly, specify the
return value of the execution result in the return statement.
First, let's see how to specify the target of AOP by Bean name.
[7-3-4]com.example.demo.login.aspect.LogAspect.java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
If you write "bean" in Pointcut, you can specify the target of AOP with
the Bean name registered in DI. Of course, you can also use regular
expressions.
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
// Point:@annotation
@Around("@annotation(org.springframework.web.bind.annotation.GetM
apping)")
public Object startLog(ProceedingJoinPoint jp) throws Throwable {
// Omitted
}
}
Point:@annotation
Next, we will create it by specifying all the methods of the annotated class.
[7-3-4]com.example.demo.login.aspect.LogAspect.java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
// Point: @within
@Around("@within(org.springframework.stereotype.Controller)")
public Object startLog(ProceedingJoinPoint jp) throws Throwable {
// Omitted
}
}
Point: @within
If you use "@within", all methods of the class with the specified
annotation will be targeted. Again, specify the class name including the
package name.
[AOP terminology]