Speaking of spring, we know that its two core functions are AOP (aspect oriented) and IOC (control inversion). This article summarizes how SpringBoot integrates and uses AOP.
1, Example application scenario: log all web requests in perspective.
1. The web module of SpringBoot and AOP related dependencies are introduced into pom:
Of which:
cglib package is used for dynamic proxy, class based proxy;
aspectjrt and aspectjweaver are aspectj related packages to support aspect programming;
aspectjrt package is the runtime package of aspectj;
aspectjweaver is the weaving package of aspectj;
2. Implement a simple web request entry (pass in the name parameter and return "hello xxx"):
Note: after the introduction of AOP dependency package, generally speaking, no other configuration is required. People who have used the Spring annotation configuration method will ask whether it is necessary to add @ EnableAspectJAutoProxy to the main class of the program to enable it. In fact, it is not necessary.
Because in the default configuration attribute of AOP, the spring.aop.auto attribute is enabled by default, that is to say, @ EnableAspectJAutoProxy has been added by default as long as the AOP dependency is introduced.
3. Define the aspect class to realize the log aspect of the web layer
To turn a class into a faceted class, you need two steps,
① Use the @ Component annotation on the class to add the aspect class to the IOC container
② Use the @ Aspect annotation on the class to make it a faceted class
package com.example.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; /** * Created by lmb on 2018/9/5. */ @Aspect @Component public class WebLogAcpect { private Logger logger = LoggerFactory.getLogger(WebLogAcpect.class); /** * Define pointcuts. Pointcuts are all functions under com.example.aop */ @Pointcut("execution(public * com.example.aop..*.*(..))") public void webLog(){} /** * Pre notification: notification executed before the connection point * @param joinPoint * @throws Throwable */ @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { // Receive the request and record the request content ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // Record the request logger.info("URL : " + request.getRequestURL().toString()); logger.info("HTTP_METHOD : " + request.getMethod()); logger.info("IP : " + request.getRemoteAddr()); logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(returning = "ret",pointcut = "webLog()") public void doAfterReturning(Object ret) throws Throwable { // After processing the request, return the content logger.info("RESPONSE : " + ret); } }
The above facet classes cut through all functions under the com.example.aop package through the Pointcut defined by @ Pointcut, realize the pre notification of the Pointcut through @ Before, and record the objects returned by the request through @ AfterReturning.
visit http://localhost:8004/hello?name=lmb The console output is as follows:
See my Github for detailed code: Spring boot integrates AOP
2, AOP supported notifications
1. Pre notification @ Before: a notification executed Before a connection point. Unless an exception is thrown, this notification cannot prevent the execution process Before the connection point.
/** * Pre notification, called before method call * @param joinPoint/null */ @Before(value = POINT_CUT) public void before(JoinPoint joinPoint){ logger.info("Before advice "); //Get the parameter information of the target method Object[] obj = joinPoint.getArgs(); //AOP proxy class information joinPoint.getThis(); //Target object of proxy joinPoint.getTarget(); //Signature of the most used notifications Signature signature = joinPoint.getSignature(); //Which method is the proxy logger.info("Which method is the proxy"+signature.getName()); //The name of the AOP proxy class logger.info("AOP The name of the proxy class"+signature.getDeclaringTypeName()); //Class information of AOP proxy class signature.getDeclaringType(); //Get RequestAttributes RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); //Get HttpServletRequest information from RequestAttributes HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); //If you want to obtain Session information, you can write as follows: //HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION); //Get request parameters Enumeration<String> enumeration = request.getParameterNames(); Map<String,String> parameterMap = Maps.newHashMap(); while (enumeration.hasMoreElements()){ String parameter = enumeration.nextElement(); parameterMap.put(parameter,request.getParameter(parameter)); } String str = JSON.toJSONString(parameterMap); if(obj.length > 0) { logger.info("The requested parameter information is:"+str); } }
Note: JoinPoint and RequestContextHolder are used here.
1) . the signature information of the notification can be obtained through JoinPoint, such as target method name, target method parameter information, etc;
2) . obtain request information and Session information through RequestContextHolder;
2. Post notification @ AfterReturning: the notification executed after a connection point is usually executed when a matching method returns (the return value can be bound in the post notification).
/** * Post return notification * Note here: * If the first parameter in the parameter is JoinPoint, the second parameter is the information of the return value * If the first parameter in the parameter is not JoinPoint, the first parameter is the corresponding parameter in returning * returning: It is limited that the post return notification can be executed only when the return value of the target method is the corresponding parameter type of the notification method, otherwise it will not be executed, * For the notification method corresponding to returning, the parameter of Object type will match any target return value * @param joinPoint * @param keys */ @AfterReturning(value = POINT_CUT,returning = "keys") public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){ logger.info("Return value of the first post notification:"+keys); } @AfterReturning(value = POINT_CUT,returning = "keys",argNames = "keys") public void doAfterReturningAdvice2(String keys){ logger.info("Return value of the second post notification:"+keys); }
3. Post exception notification @ AfterThrowing: the notification executed when the method throws an exception and exits.
/** * Post exception notification * Define a name, which is used to match a parameter name of the notification implementation method. When the target method throws an exception and returns, the exception thrown by the target method will be passed to the notification method; * throwing:It is limited that the post exception notification can be executed only when the exception thrown by the target method and the corresponding parameter exception type of the notification method, otherwise it will not be executed, * For the notification method corresponding to throwing, the parameter of Throwable type will match any exception. * @param joinPoint * @param exception */ @AfterThrowing(value = POINT_CUT,throwing = "exception") public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){ //Target method name: logger.info(joinPoint.getSignature().getName()); if(exception instanceof NullPointerException){ logger.info("A null pointer exception occurred!!!!!"); } }
4. Post final notification @ After: the notification executed when a connection point exits (whether normal return or abnormal exit).
/** * Post final notification (the post notification method will be executed as long as the target method is executed) * @param joinPoint */ @After(value = POINT_CUT) public void doAfterAdvice(JoinPoint joinPoint){ logger.info("The post final notification was executed!!!!"); }
5. Surround notification @ Around: notifications surrounding a connection point, such as method calls. This is the most powerful type of notification. Surround notification can complete customized behavior before and after method call. It will also choose whether to continue to execute the connection point, or directly return its own return value or throw an exception to end execution.
Surround notification is the most powerful and troublesome. It is a surround of methods. The specific methods will be passed to the aspect through the agent. In the aspect, you can choose whether to execute the method or not, how many times to execute the method, etc. The surround notification uses a proxy Object of type ProceedingJoinPoint to manage the target Object, so the first parameter of this notification must be of type ProceedingJoinPoint. Calling the processed () method of the ProceedingJoinPoint in the notification body will cause the connection point method in the background to execute. The processed () method may also be called and passed in an Object [] Object, and the values in the array will be used as input parameters when the method is executed.
/** * Surround notification: * The surround notification is very powerful. It can determine whether the target method is executed, when it is executed, whether the method parameters need to be replaced during execution, and whether the return value needs to be replaced after execution. * The first parameter of the surround notification must be of type org.aspectj.lang.ProceedingJoinPoint */ @Around(value = POINT_CUT) public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){ logger.info("Target method name surrounding the notification:"+proceedingJoinPoint.getSignature().getName()); try { Object obj = proceedingJoinPoint.proceed(); return obj; } catch (Throwable throwable) { throwable.printStackTrace(); } return null; }
6. Sometimes when we define a section, we need to use a parameter of the target object in the section. How can we get the parameters of the target object from the section? Args can be used to bind. If a parameter name is used where the type name should be used in an args expression, the parameter value of the object will be passed in when the notification is executed.
@Before("execution(* findById*(..)) &&" + "args(id,..)") public void twiceAsOld1(Long id){ System.err.println ("section before Executed.... id==" + id); }
Note: any notification method can define the first parameter as org.aspectj.lang.JoinPoint type (for surround notification, you need to define the first parameter as ProceedingJoinPoint type, which is a subclass of JoinPoint). The JoinPoint interface provides a series of useful methods, such as getArgs() (return method parameters), getThis() (return proxy object), getTarget() (return target), getSignature() (return information about the method being notified) and toString() (print out useful information about the method being notified).
3, Pointcut expression
When defining a pointcut, you need a signature containing a name and any parameters, as well as a pointcut expression, such as execution(public * com.example.aop... (..))
Format of pointcut expression: execution([visibility] return type [declaration type]. Method name (parameter) [exception])
Among them, [] is optional, and others also support the use of wildcards:
1) *: match all characters
2) ..: generally used to match multiple packages and multiple parameters
3) +: represents a class and its subclasses
4) Operators are: & &, |, |,!
Pointcut expression keyword use case:
1) execution: used to match subexpressions.
//Match all methods in all classes in com.cjm.model package and its sub package, with any return type and method parameters
@Pointcut("execution(* com.cjm.model...(..))")
public void before(){}
2) within: used to match the Java class or package where the connection point is located.
//Matches all methods in the Person class
@Pointcut("within(com.cjm.model.Person)")
public void before(){}
//Match all methods in all classes in the com.cjm package and its child packages
@Pointcut("within(com.cjm..*)")
public void before(){}
3) this: used to pass a reference to the proxy object into the notification method.
@Before("before() && this(proxy)")
public void beforeAdvide(JoinPoint point, Object proxy){
//Processing logic
}
4) Target: used to pass a reference to the target object into the notification method.
@Before("before() && target(target)
public void beforeAdvide(JoinPoint point, Object proxy){
//Processing logic
}
5) args: used to pass parameters into the notification method.
@Before("before() && args(age,username)")
public void beforeAdvide(JoinPoint point, int age, String username){
//Processing logic
}
6) @ within: it is used to match the class that uses the annotation determined by the parameter at the class level, and all its methods will be matched.
@Pointcut("@within(com.cjm.annotation.AdviceAnnotation)")
- all classes marked by @ AdviceAnnotation will match
public void before(){}
7) @ target: similar to @ within, but the retention policy of annotation interface must be specified as RUNTIME.
@Pointcut("@target(com.cjm.annotation.AdviceAnnotation)")
public void before(){}
8) @ args: the Java class corresponding to the object passing in the connection point must be annotated by the Annotation annotation specified by @ args.
@Before("@args(com.cjm.annotation.AdviceAnnotation)")
public void beforeAdvide(JoinPoint point){
//Processing logic
}
9) @ Annotation: the method that matches the Annotation annotation of the connection point specified by its parameter. That is, all methods that are assigned Annotation annotations will match.
@Pointcut("@annotation(com.cjm.annotation.AdviceAnnotation)")
public void before(){}
10) Bean: defines the bean where the connection point is located by the name of the managed bean. This keyword is new in spring 2.5.
@Pointcut("bean(person)")
public void before(){}
reference material: https://www.cnblogs.com/lic309/p/4079194.html