preface
Spring AOP is one of the most frequently used techniques in development, allowing us to log, process results, and do some input processing without contaminating the business. Transactions, such as those provided by Spring, are also implemented based on Spring AOP.
AOP
AOP is aspect oriented programming, which is mainly embodied in the concept of “aspect”. In layman’s terms, the idea is to treat a certain process, step or stage in a business process flow as if it were a Lego building block that can be plugged into other modules as needed. Of course, granularity in this concept can only reach the method level at the moment.
In this article, I will only cover the use of Spring AOP.
Use of Spring AOP
To prepare
Spring AOP needs to be introduced along with AspectJ, which requires the following dependencies
< the dependency > < groupId > org. Aspectj < / groupId > < artifactId > aspectjweaver < / artifactId > < version > 1.8.13 < / version > </dependency> <groupId>org.aspectj</groupId> <artifactId> aspectJrt </artifactId> <version>1.8.13</version> </dependency>Copy the code
Point of tangency Pointcut
When using AOP cuts, the most important thing is to tell you where to cut, followed by what operations to cut into. The Pointcut position, the Pointcut point, is defined by @pointcut, which takes two parameters:
Value: the point of tangency expression, support | | and (or) && (and) argNames: parameter expression, must and annotation methods into the same refs In the point of tangency expression, there is the following indicators:
execution
Matches the access restrictor, return type and other method information of the execution method, taking the matching method as the pointcut. The expression rule is as follows
Execution (The access modifier returns the full class name of the type. Method (input parameter type) – wildcards are supported
Eg:
Check when available
The above expressions cover most of the common scenarios, and some of the functionality can be implemented in other ways
within
Matches method executions of the specified type, and only those that implement the method are matched. Where type expression rules
Within (full class name) — Wildcard characters are supported
Eg:
Expression matching target
this
Methods that match the execution method of the current AOP proxy object type are, in essence, matched with methods overwritten/implemented in the type. Methods that are not overwritten or implemented in the parent class are not matched (call them current object scoped, non-super methods). The expression rule is as follows
This (full class name)
Eg:
target
This (full class name)
Eg:
args
Match method entries by parameter type (note that the match is not to match the type signed by the method, but to match the type passed in; for example, String can also be matched by Object). The expression rules are as follows
Args (full class name) – Support through.. Matching multiple parameters
Eg:
Note that since ARGS matching is a dynamic pointcut, it is expensive and recommended to use it sparingly when not necessary (pending confirmation)
@within, @target, @args, @annotation
@within, @target, and @args are shorthand for matching annotations within, target, and args, but they can only match annotations, not types
@annotation Matches methods that hold annotations of the specified type
Eg:
bean
This type is extended by Spring ASP; AspectJ does not have this type. A Bean method that matches the name specified in the Spring container
Bean (bean name) — Support for general * wildcard characters
Eg:
Reference point of tangency
Usually, this method is used to define pointcuts, such as:
@Before("execution(* (@org.springframework.stereotype.Controller *).*(..) )")
public void classAnnotationAop() {
System.out.println("class annotation");
}
Copy the code
Or you could write it as
@Pointcut("execution(* (@org.springframework.stereotype.Controller *).*(..) )")
public void classMethod() {}
@Before("classMethod()")
public void classAnnotationAop() {
System.out.println("class annotation");
}
Copy the code
Where @before directly references the pointcut expression defined on classMethod()
Notice to Advise
Notification type
There are several types of notification:
Before notification, After notification Before target method execution, AfterReturn notification After target method execution, AfterReturn notification Before @afterReturn, @Afterthrowing, An AfterThrowing exception handling notification is executed when a method successfully executes and returns a result, and an Around Around notification is executed when an exception is thrown during the execution of a method. The most special type of notification can achieve the same function as the above methods, but it is different from @before. The need to manually by ProceedingJoinPoint. Proceed to call into the method Execution sequence as shown in figure
Notice the reference
In any notification, an input parameter of type JoinPoint is supported. This input parameter can be used to obtain the target object, proxy object, method parameters, and other data of the currently notified method. This type provides the following methods
public interface JoinPoint { String toString(); / / connection information, such as execution (int xyz. Me4cxy. Aop. Advise. AdviseTarget. Test (int)) String toShortString (); // Short relevant information about the join point, such as execution(advisetarget.test (..)) ) String toLongString(); Detailed information on / / join points, such as the execution (public int xyz. Me4cxy. Aop. Advise. AdviseTarget. Test (int)) Object getThis (); // Get the AOP proxy Object getTarget(); Object[] getArgs(); // Get the current execution method entry parameter Signature getSignature(); SourceLocation getSourceLocation(); String getKind(); // Get the JoinPoint type, such as method-execution JoinPoint.StaticPart getStaticPart(); // Get join point static part property}Copy the code
Also, in an advice of type @around, the ProceedingJoinPoint type can be used, which provides the PROCEED method to execute the proxied target method.
public interface ProceedingJoinPoint { Object proceed() throws Throwable; // Execute the target method with the call parameter Object proceed(Object[] var1) throws Throwable; // Execute the target method and pass the user-defined parameters}Copy the code
Note that if you want to receive a JoinPoint parameter, it must be the first parameter of the method, otherwise an exception will occur
Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut
For notifications of type @afterReturn or @AfterThrowing, you can also specify the return value and the name of the exception passed in with the annotation parameters RETURNING and Throwing, respectively, as follows:
@AfterThrowing(value = "test()", throwing = "e")
public void afterThrowing(Exception e) {
System.out.println("afterThrowing");
System.out.println(e.getMessage());
}
@AfterReturning(value = "test()", returning = "result")
public void afterReturning(int result) {
System.out.println("afterReturning");
System.out.println(result);
}
Copy the code
The above methods are built in and will automatically match the incoming. If we need to pass in a method input or annotation that we need to match according to our matching rules, we can pass in the indicator described above. Except for the execution and bean indicators, which cannot pass parameters to the notification method, we can pass in matched parameters or objects as method inputs.
Eg:
Pass in the matching input parameter
@Before("execution(* xyz.me4cxy.aop.advise.AdviseTarget.*(*)) && args(param)")
public void before(JoinPoint joinPoint, int param) {
System.out.println("before");
System.out.println(param);
}
Copy the code
In the above example, the execution indicator matches the method in the AdviseTarget that has only one parameter, and then matches the input type of the executing method again with the args parameter type in the before method. If they are the same, it cuts in and passes the parameter as an input parameter to param, for example
@Component
public class AdviseTarget {
public Object test(Object i) {
if (i.equals(1))
System.out.println("Method of execution");
else
throw new RuntimeException("Abnormal");
returni; }}Copy the code
When the test method is executed, because the input parameter is of type Object, it will not be entered in the match because the param parameter in the before parameter is of type int. But we change the input parameter of test to int/Integer, and when we call and pass in 1, the result is as follows
Before 1 Execution method
Pass in the matching annotations
@Component
public class AnnotationTarget {
@RequestMapping({"/"."/test"})
public void test(String param) {
System.out.println("test"); }}Copy the code
@Pointcut("execution(* xyz.me4cxy.aop.anno.. *. * (*))")
public void exec() {}
@Pointcut("@annotation(requestMapping)")
public void requestMapping(RequestMapping requestMapping) {}
@Pointcut("args(str)")
public void stringParam(String str) {}
@Before("exec() && requestMapping(anno) && stringParam(str)")
public void before(RequestMapping anno, String str) {
System.out.println(ArrayUtil.arrayToString(anno.value()));
System.out.println(str);
}
@Autowired
private AnnotationTarget target;
@Test
public void test() {
target.test("123");
}
Copy the code
The above example makes use of “type-self-matching” (a non-technical name that is purely for personal convenience) to restrict the method types that can be matched by input type. At the same time can also complete the input parameter control. In the preceding example, the result is
/ /test 123 test
Registration of the Register
Here, I implement AOP registration using a Java configuration that is as simple as adding @aspect and @Component to the class (remember to use @enableAspectJAutoProxy to turn on the proxy).
When using Spring AOP, exceptions can occur when working with @AutoWired injection beans
Bean named ‘‘ is expected to be of type ‘‘ but was actually of type ‘com.sun.proxy.$Proxy**’
SpringAOP is implemented using JDK proxies and Cglib, and it is not difficult to find that the com.sun.proxy.$proxy ** class is generated by the JDK proxy.
In general, SpringAOP will use JDK proxies to generate proxy objects when proxying if the proxy target class implements the interface, which is why @autowired injection can cause bean type inconsistencies.
Fixing this problem is actually as simple as changing the proxyTargetClass parameter in the @EnableAspectJAutoProxy annotation to true.
The last
Thank you for reading here, after reading what do not understand, you can ask me in the comments section, if you think the article is helpful to you, remember to give me a thumbs up, every day we will share Java related technical articles or industry information, welcome to pay attention to and forward the article!