preface

concept

OOP (Encapsulating Inheritance polymorphism) is a concept you’ll be introduced to when learning JAVA. These concepts decouple the common behavior of objects and make code simple and maintainable. But OOP is less powerful when it comes to bringing different objects into a common behavior. Typically is logging in the project, the object function in spreading out of each level of (before execution, execution, abnormal), if in each need to add a logging place, there will be a large number of redundant code, and the code itself and the business is not too big, and would make it harder to understand the business, Even more processing is needed to keep this code from interfering with the execution of the core business. This is where the concept of AOP is introduced, which defines the different layers of different objects as aspects. AOP also programs for aspects, separating core business behavior from common common behavior, which is its purpose.

The term

Advice

Features that need to be added, such as the logging mentioned above.

JoinPoint

Where you are allowed to notify during project execution.

Pointcut

Pointcuts are defined on the basis of join points. For example, if there are 10 join points in a class and you want to notify only five of them, you can define these five join points as pointcuts.

Aspect

The combination of pointcuts, which means “where,” advice, which means “what,” and “when,” and “what, where,” is a complete aspect definition.

Introduction

Add method attributes to an existing class. Is to use the aspect in the target class.

Weaving

The process of creating a new proxy class by introducing facets into the target object.

prompt

Article examples use JDK1.8+SpringBoot framework to explain. You should have some experience with SpringBoot.

The preparatory work

1.aop

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Copy the code

2.junit

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>
Copy the code

The body of the

The test code

@Service public class IAopServiceImpl implements IAopService { @Override public String test1(String param) { System. The out. Println (" I am test1 "); return "test1"; } @override public String test2() {system.out.println (" I am test2"); return "test2"; } @override public String test3() {system.out.println (" I am test3"); return "test3"; }}Copy the code
@RunWith(SpringRunner.class) @SpringBootTest public class AopApplicationTests { @Autowired IAopService iAopService; @test public void Test () {iaopservice.test1 (" parameter 1"); iAopService.test2(); iAopService.test3(); }}Copy the code

Lead to enhance

Weave enhancement processing before target method execution. Get the parameters and IP address of the request and print the log before executing the test code.

@aspect @Component @slf4j public class LogAspect {/** * all functions under the service package * @author pengyu * @param * @return void * @date 2020/12/23 11:18 */ @Pointcut("execution(public * com.pengyu.aop.service.. *. * (..) ") public void testLog(){} /** * * @author pengyu * @param joinPoint * @return void * @date 2020/12/23 11:18 */ @before ("testLog()") public void * @author pengyu * @param joinPoint * @return void * @date 2020/12/23 11:18 */ @before ("testLog()") public void doBefore(JoinPoint joinPoint){ RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); Map<String, Object> map = getNameAndValue(joinPoint); String IP =getIpAddr(request); The info (" this is the front, request parameters - > {}, IP - > {} ", the map. The toString (), IP); } private static Map<String, Object> getNameAndValue(JoinPoint joinPoint) { Map<String, Object> param = new HashMap<>(); Object[] paramValues = joinPoint.getArgs(); String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames(); for (int i = 0; i < paramNames.length; i++) { param.put(paramNames[i], paramValues[i]); } return param; } public static String getIpAddr(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip ! = null && ip.length() ! = 0 &&!" Unknown ".equalsignorecase (IP)) {// If (ip.indexof (",")! = -1) { ip = ip.split(",")[0]; } } if (ip ! = null | | "127.0.0.1" equals (IP)) {IP = null; } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("X-Real-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; }}Copy the code

After execution, the console prints:

The 2020-12-23 14:34:23. 14388-453 the INFO [main] com. Pengyu. Aop. Core. LogAspect: I'm test1 2020-12-23 14:34:23.460 INFO 14388 -- [main] com.pengyu.aop.core.LogAspect : This is front-facing, request parameters - > {}, IP - > 127.0.0.1 I am test2 14:34:23 2020-12-23. 14388-460 the INFO [main] com. Pengyu. Aop. Core. LogAspect: This is pre-enhanced, request parameters -->{}, IP -->127.0.0.1 I'm test3Copy the code

As you can see, a log is printed before each method is executed, which is called a front-loading enhancement.

The rear enhancement

Weave enhancement processing after target method execution. There are two annotations that need to be noted: @after and @afterRETURNING. @afterRETURNING will not be notified if the method fails due to exceptions or other reasons, and @after will be notified in any case. We use @afterreturning.

@aspect @Component @slf4j public class LogAspect {/** * all functions under the service package * @author pengyu * @param * @return void * @date 2020/12/23 11:18 */ @Pointcut("execution(public * com.pengyu.aop.service.. *. * (..) )") public void testLog(){} /** * [post notification] * @author pengyu * @param joinPoint * @param result Return value * @return void * @date 2020/12/23 15:00 */ @AfterReturning(value = "testLog()",returning = "result") public void doBefore(JoinPoint joinPoint,Object result){ RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); Map<String, Object> map = getNameAndValue(joinPoint); String IP =getIpAddr(request); Log. The info (" this is that the increase of the rear request parameters - > {}, IP - > {}, the return value - > {} ", the map. The toString (), IP, result. The toString ()); } private static Map<String, Object> getNameAndValue(JoinPoint joinPoint) { Map<String, Object> param = new HashMap<>(); Object[] paramValues = joinPoint.getArgs(); String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames(); for (int i = 0; i < paramNames.length; i++) { param.put(paramNames[i], paramValues[i]); } return param; } public static String getIpAddr(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip ! = null && ip.length() ! = 0 &&!" Unknown ".equalsignorecase (IP)) {// If (ip.indexof (",")! = -1) { ip = ip.split(",")[0]; } } if (ip ! = null | | "127.0.0.1" equals (IP)) {IP = null; } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("X-Real-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; }}Copy the code

Postenhancement takes the method return value, so we add a return value to the log output, which works like this:

I am test1 15:04:11 2020-12-23. 870 INFO 26348 - [the main] com. Pengyu. Aop. Core. LogAspect: Request parameter -->{param= parameter 1}, IP -->127.0.0.1, return value -->test1 I am test2 2020-12-23 15:04:11.872 INFO 26348 -- [main] com.pengyu.aop.core.LogAspect : Request parameters -->{}, IP -->127.0.0.1, return value -->test2 I'm test3 2020-12-23 15:04:11.872 INFO 26348 -- [main] Com. Pengyu. Aop. Core. LogAspect: this is that the increase of the rear request parameters - > {}, IP - > 127.0.0.1, the return value -- > test3Copy the code

The position of the post-enhancement is changed relative to the pre-enhancement, which is before the target method is executed, and the post-enhancement, which is after the target method is executed, and returns the value.

Surrounding the enhancement

Weave reinforcement processing before and after target method execution. As the name suggests, surround enhancement can be done both before and after the target method. For example, if we want to log the parameter before the method executes and print the return value after the method executes, we can say:

@aspect @Component @slf4j public class LogAspect {/** * all functions under the service package * @author pengyu * @param * @return void * @date 2020/12/23 11:18 */ @Pointcut("execution(public * com.pengyu.aop.service.. *. * (..) )") public void testLog(){} /** * [looparound notification] * @author pengyu * @param joinPoint * @return void * @date 2020/12/23 15:10 */ @Around("testLog()") public void doBefore(ProceedingJoinPoint joinPoint) throws Throwable { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); Map<String, Object> map = getNameAndValue(joinPoint); Log.info (" This is surround enhancement, before method execution, parameter -->{}",map.toString()); Object result=joinPoint.proceed(); Log.info (" this is surround enhancement, method executes, return value -->{}",result); } private static Map<String, Object> getNameAndValue(JoinPoint joinPoint) { Map<String, Object> param = new HashMap<>(); Object[] paramValues = joinPoint.getArgs(); String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames(); for (int i = 0; i < paramNames.length; i++) { param.put(paramNames[i], paramValues[i]); } return param; } public static String getIpAddr(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip ! = null && ip.length() ! = 0 &&!" Unknown ".equalsignorecase (IP)) {// If (ip.indexof (",")! = -1) { ip = ip.split(",")[0]; } } if (ip ! = null | | "127.0.0.1" equals (IP)) {IP = null; } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("X-Real-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; }}Copy the code

The results are as follows:

The 2020-12-23 15:17:52. 22380-731 the INFO [main] com. Pengyu. Aop. Core. LogAspect: , before this is surrounded, method performs parameters - > {parameters param = 1} I am test1 15:17:52 2020-12-23. 22380-739 INFO. [the main] com pengyu. Aop. Core. LogAspect: It is surrounded by enhanced, after the method performs, the return value -- > test1 15:17:52 2020-12-23. 22380-739 the INFO [main] com. Pengyu. Aop. Core. LogAspect: This is surrounded, way before execution, parameters - > {} I am test2 15:17:52. 2020-12-23 22380-739 the INFO [main] com. Pengyu. Aop. Core. LogAspect: It is surrounded by enhanced, after the method performs, the return value -- > test2 15:17:52 2020-12-23. 22380-740 the INFO [main] com. Pengyu. Aop. Core. LogAspect: This is surrounded, way before execution, parameters - > {} I am test3 15:17:52. 2020-12-23 22380-740 the INFO [main] com. Pengyu. Aop. Core. LogAspect: This is a wrap enhancement. After the method is executed, the return value is -->test3Copy the code

If you have noticed, the entry argument surrounding the enhancement changes from JoinPoint to ProceedingJoinPoint. If you click on it, you can see that The ProceedingJoinPoint extends the JoinPoint and exposes the proceed() method on the original interface. The method is to let the target method execute in order to support circular notification.

At the end

The three common types of enhancements mentioned above are pre-enhancement, post-enhancement, and wrap enhancement, as well as other enhancements such as exception enhancement, which will be discussed in the next article. If you find something wrong or I understand something wrong, you are welcome to correct it.