Using SPRINGAOP to implement logging functions in Springboot

Source: Internet
Author: User
Tags aop throw exception

Background:

I need to add a log record to each controller in a Springboot project to record some information about the request.

The code looks like this:

Logger.info (Request.getrequesturl ());

Sort of.

The code is not difficult, but due to the number of controllers, it is also hard work. So I thought of using spring AOP to solve this problem.

First, add SPRINGAOP dependencies to the POM:

< Dependency >        < groupId >org.springframework.boot</groupId>        <  Artifactid>spring-boot-starter-aop</artifactid> </ Dependency >

In the previous article, we said that if you want to annotate directly with @aspect, add the

<aop:aspectj-autoproxy/>

So do we need to add @enableaspectjautoproxy to the main class of the program to enable it? does not actually need

Well that is to say, as long as the introduction of SPRINGAOP-related jar dependencies, we can start the related Aspet programming.

Here is the code directly, and then explain:

The first is a diagram of the package structure:

There are two packages involved in receiving the requested controller, Com.stuPayment.controller and Com.stuPayment.uiController

Then look at the code of our section class Weblogaspect class:

 PackageCom.stuPayment.util;Importjava.util.Arrays;Importjavax.servlet.http.HttpServletRequest;ImportOrg.aspectj.lang.JoinPoint;ImportOrg.aspectj.lang.ProceedingJoinPoint;ImportOrg.aspectj.lang.annotation.After;Importorg.aspectj.lang.annotation.AfterReturning;ImportOrg.aspectj.lang.annotation.Around;ImportOrg.aspectj.lang.annotation.Aspect;ImportOrg.aspectj.lang.annotation.Before;ImportOrg.aspectj.lang.annotation.Pointcut;ImportOrg.slf4j.Logger;Importorg.slf4j.LoggerFactory;Importorg.springframework.stereotype.Component;Importorg.springframework.web.context.request.RequestAttributes;ImportOrg.springframework.web.context.request.RequestContextHolder;Importorg.springframework.web.context.request.ServletRequestAttributes, @Aspect @component Public classWeblogaspect {Private FinalLogger Logger = Loggerfactory.getlogger (weblogaspect.class); @Pointcut ("Execution (Public * com.stuPayment.controller). *.*(..))")//The pointcut describes this as the entry point for the controller package.     Public voidControllerlog () {}//signature, which can be understood as a name for this pointcut@Pointcut ("Execution (Public * com.stuPayment.uiController). *.*(..))")//pointcut Description, this is the entry point of the Uicontroller package     Public voidUicontrollerlog () {} @Before ("Controllerlog () | | Uicontrollerlog () ")//to do before the approach to the pointcut is run     Public voidLogbeforecontroller (Joinpoint joinpoint) {requestattributes requestattributes= Requestcontextholder.getrequestattributes ();//This requestcontextholder is something SPRINGMVC provides to get the request.HttpServletRequest request =((servletrequestattributes) requestattributes). Getrequest (); //record the requested contentLogger.info ("############### #URL:" +Request.getrequesturl (). toString ()); Logger.info ("############### #HTTP_METHOD:" +Request.getmethod ()); Logger.info ("############### #IP:" +request.getremoteaddr ()); Logger.info ("############### #THE ARGS of the CONTROLLER:" +arrays.tostring (Joinpoint.getargs ())); //The following getsignature (). Getdeclaringtypename () is the Get package + class name and then JoinPoint.getSignature.getName () Gets the method nameLogger.info ("############### #CLASS_METHOD:" + joinpoint.getsignature (). Getdeclaringtypename () + "." +joinpoint.getsignature (). GetName ()); //logger.info ("############### #TARGET:" + joinpoint.gettarget ());//returns the object of the target class that needs to be strengthened//logger.info ("############### #THIS:" + joinpoint.getthis ());//returns the object of the enhanced proxy class    }   }

For this section class, to expand the description of the @aspect slice class programming.

@Aspect and @component

First, this @aspect note tells Spring that this is a slice class, and then @compoment will be converted into a bean in the spring container or a proxy bean. In short, write the two annotations together.

Since it is a slice class, then it must contain PointCut and Advice two elements, and the following several annotations unfold to see how the Pointcut (PointCut) and enhanced Notification (Advice) are determined in @aspect.

@PointCut

This annotation contains two parts, pointcut expressions and pointcut signatures. Expressions are used to determine the location of pointcuts, and it is clear that some rules are used to determine which methods are to be enhanced, that is, which methods to intercept.

@PointCut (...) in the. Those inside the parentheses are the expressions. The execution here is one of the matching methods, as well as:

execution: Match connection point within: Inside a class  This : Specifies the type of the AOP proxy class target: Specifies the type of the target object args: Specifies the type of the parameter bean: Specifies a specific bean name, which can be used with wildcards (spring-brought) @target: type @args with the specified annotation: Specifies the parameter that runs simultaneous with the specified annotation @within: matches the class using the specified annotation @annotation: annotations applied by the specified method

Note that because it is a dynamic proxy implementation method, so not all methods can be intercepted, for the JDK agent only public methods to intercept, for cglib only public and protected methods to intercept.

Here we mainly introduce the matching method of execution, because most of the time we will use this to define Pointcut:

Execution (Modifiers-pattern ret-type-pattern declaring-type-pattern?name-pattern (param-pattern)             throws-pattern? ) Execution (method modifier (optional)  return type  class path method name  parameter  

In addition to the return type, the method name has parameters, and the others are optional

Ret-type-pattern: can represent any return value, the class name of the full path, and so on.

Name-pattern: Specifies the method name, * represents so, set*, which represents all methods that begin with set.
Parameters Pattern: Specifies the method parameter (declared type), () matches no parameters; (..) Represents any number of parameters; (*) represents a parameter, but can be of any type; (*,string) represents the first parameter as any value and the second is a String type.

Here are a few examples:

1) Execution ( public * *(..)) --Means match all public method 2) Execution (* set*(..)) --denotes all methods starting with "set" 3) Execution (* com.xyz.service.accountservice.*(..)) --means to match all Accountservice interfaces 4) Execution (* com.xyz.service.*.*(..)) --Represents the matching service package under All method 5) Execution (* com.xyz.service). *.*(..)) --Represents the method under which the service package and its sub-packages are matched

Then the other matching method to use when the Baidu Bar ~

And then the second part of @pointcut, the signature signature, which is the code.

@Pointcut ("Execution (public * com.stuPayment.uiController). *.*(..))") // pointcut Description, this is the entry point    of the Uicontroller package  Public void Uicontrollerlog () {}

This public void Uicontrollerlog () {}, as defined by the method, is something that looks like a method definition, that is, a signature, which is not actually useful, but is used to mark a pointcut that can be understood as a token of this pointcut.

@Before

This is the label that determines where advice is executed in the Pointcut method, meaning that the advice we define is executed before the Pointcut method executes.

// to do    before the approach to the pointcut is run  Public void Logbeforecontroller (joinpoint joinpoint) {

@Before note the parentheses are written in a pointcut, where the pointcut expression can be described with a logical symbol &&,| |,!. A pointcut expression can also be built into parentheses, which is written directly:

@Before ("Execution (public * com.stuPayment.uiController). *.*(..))")

The effect is the same as written @before ("Uicontrollerlog ()").

Then see the note below the method, is to describe advice, we see there is a parameter joinpoint, this thing represents the weaving enhancement processing connection point. The joinpoint contains several useful parameters:

    • Object[] Getargs: Returns the parameters of the target method
    • Signature getsignature: Returns the signature of the target method
    • Object Gettarget: Returns the target object that was woven into the enhanced processing
    • Object Getthis: Returns the proxy object generated by the AOP framework for the target object

In addition to the annotation @around method, the other can add this joinpoint as a parameter. The parameters of @Around annotated method must be Proceedingjoinpoint, as described below.

@After

This annotation is the addition of our advice enhancements after the cut-in method has run out. You can add Joinpoint in the same way.

@Around

This annotation can be simply seen as a combination of @before and @after. This annotation is more specific than the other, and its method parameters must be Proceedingjoinpoint, and this object is a subclass of Joinpoint. We can think of this as a substitute for the method of the pointcut, this proceedingjoinpoint has a proceed () method, which is equivalent to that method of the pointcut execution, simply to let the target method execute, then this method will return an object, This object is the object returned by the method where the Pointcut is located.

In addition to this proceed method (a very important method), the others are the same as those of the annotations.

@AfterReturning

As the name implies, this annotation is used to weave the enhanced processing after the target method is completed normally. This annotation can specify two attributes (the parentheses after the previous three annotations write only one @pointcut expression, that is, only one property), and one is the same as the other annotations, which is the pointcut expression that describes which access point the advice is woven into You can then also have a returning property that indicates that you can have the parameters of the target method return value in the advice method.

@AfterReturning (returning = "Returnob", Pointcut = "Controllerlog () | | | Uicontrollerlog () ")    publicvoid  doafterreturning (joinpoint joinpoint, Object Returnob) {        System.out.println ("##################### The return of the method is:" + returnob);    }

After the browser makes a request, the effect:

(Here is a request to request a login interface, so Uicontroller returns a string as the view.) )

@AfterThrowing

Exceptions throw enhancements that are woven after an exception is thrown. A bit like the @afterreturning above, this annotation also has two properties, pointcut and throwing.

The usage is similar to the one returning just now:

@AfterThrowing (pointcut = "Controllerlog () | | | Uicontrollerlog () ", throwing =" ex ")publicvoid  doafterthrowing (Joinpoint joinpoint, Exception ex) {        = point.getsignature (). GetName ();        List<Object> args = arrays.aslist (Point.getargs ());        System.out.println ("Connection point method is:" + MethodName + ", parameter is:" + args + ", exception is:" + ex);          }    

Okay, now that the annotations are all over, here's a reference to the class used above: Requestcontextholder

For example, there is a need to get request and response in the service, we generally (I am) directly in the controller that request or response as a parameter to the service, which is very ugly. Later know, originally SPRINGMVC provides a very powerful class Reqeustcontextholder, through him you can get request and response what.

//below There is no difference between two methods in a project that does not use JSF Requestattributes requestattributes = requestcontextholder.currentrequestattributes (); //                                             requestcontextholder.getrequestattributes (); // get the corresponding value from the session String str = (string) requestattributes.getattribute ("name"== ((servletrequestattributes) requestattributes). GetResponse ();

All right, after you've finished programming this section, you've successfully cut the log into the individual controllers. Take a look.

Finally, the problem of interception sequence of various advice is recorded.

Situation one, there is only one aspect class:

No exception: @Around (before proceed) → @Before → method execution → @Around (part after proceed () → @After → @AfterReturning

There is an exception: @Around (Proceed (previous)) → @Before → throw exception ing→ @After → @AfterThrowing (presumably because the method did not run out of throw exception, did not correctly return all @around proceed () after the ministry Points and @afterreturning two annotations are not able to be woven into)

In case two, the same method has multiple @aspect class interception:

A single aspect is certainly the same as when there is only one aspect, but the order of advice in different aspect?? The answer is not necessarily, like a thread, no one who first, unless you assign them priority, similarly, here you can also assign a priority to the @aspect, so you can decide who first who is behind.

There are two ways of prioritizing:

    • Implement the Org.springframework.core.Ordered interface and implement its GetOrder () method
    • Add @order annotations to aspect, which are all called: Org.springframework.core.annotation.Order

Either way, the smaller the value of the order, the less the first execution:

@Order (5) @Component @aspectpublicclass  Aspect1 {    //  ... } @Order (6) @Component @aspectpublicclass  Aspect2 {    //  ...}

So the Aspect1 will always be executed before Aspect2.

Note the point:

    • If you define two identical advice (for example, two @Before) for the same pointcut in the same aspect class, then the execution order of the two advice is undefined, even if you add @Order to the two advice annotation , or not. Remember this.
    • For @around this advice, regardless of whether it has a return value, but must be inside the method, call Pjp.proceed (), otherwise, the interface in the Controller will not be executed, which also causes the @Before this advice will not be triggered.

References to blogs:

79143145 springboot implementing logs with AOP

72303040?utm_source=tuicool&utm_medium=referral-Execution expression

Methods of obtaining parameters in 51287200 advice

The Order of 52185827 advice

53559902 Introduction to Requestcontextholder class

Using SPRINGAOP to implement logging functions in Springboot

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.