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!