zoukankan      html  css  js  c++  java
  • SpringBoot AOP概念及使用Demo

    AOP核心概念
    1、横切关注点

    对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
    2、切面(aspect)-》(通知+切点)

    类是对物体特征的抽象,切面就是对横切关注点的抽象。
    通知+切点
    意思就是所有要被应用到增强(advice)代码的地方。(包括方法的方位信息)
    3、连接点(joinpoint)-》(被拦截的方法)

    被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截的方法,实际上连接点还可以是字段或者构造器
    4、切入点(pointcut)-》(描述拦截那些方法的部分)

    对连接点进行拦截的定义
    5、通知(advice)-》(拦截后执行自己业务逻辑的那些部分)

    所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
    这玩意也叫 增强
    在逻辑层次上包括了我们抽取的公共逻辑和方位信息。因为Spring只能方法级别的应用AOP,也就是我们常见的before,after,after-returning,after-throwing,around五种,意思就是在方法调用前后,异常时候执行我这段公共逻辑呗。
    6、目标对象

    代理的目标对象
    7、织入(weave)

    将切面应用到目标对象并导致代理对象创建的过程。
    比如根据Advice中的方位信息在指定切点的方法前后,执行增强。这个过程Spring 替我们做好了。利用的是CGLIB动态代理技术。
    8、引入(introduction)

    在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
    图解

    上面那一堆看不懂对吗? 我也不太懂。
    来看张图

    好了,现在我们直接看代码,简单的使用如下:

    引入pom

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-core</artifactId>
                <version>2.9.8</version><!--$NO-MVN-MAN-VER$-->
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.2</version><!--$NO-MVN-MAN-VER$-->
            </dependency>
            
            <!-- AspectJ -->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>1.9.2</version><!--$NO-MVN-MAN-VER$-->
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.2</version><!--$NO-MVN-MAN-VER$-->
            </dependency>
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>2.2.2</version>
            </dependency>
    
        </dependencies>

    创建DTO对象

    package com.szl.demo.common.dto;
    
    import lombok.Data;
    
    @Data
    public class AccountDto {
        private String accountNo;
        private String accountName;
        private Double balance;
    }

    创建service和service实现类

    package com.szl.demo.service;
    
    import com.szl.demo.common.dto.AccountDto;
    
    public interface DemoService {
        
        public AccountDto getUserAccount();
        
    }
    package com.szl.demo.service.impl;
    
    import org.springframework.stereotype.Service;
    import com.szl.demo.common.dto.AccountDto;
    import com.szl.demo.service.DemoService;
    
    @Service("demoService")
    public class DemoServiceImpl implements DemoService {
        
        public AccountDto getUserAccount() {
            AccountDto dto = new AccountDto();
            dto.setAccountNo("22133232001");
            dto.setAccountName("XXX");
            dto.setBalance(3000D);
            return dto;
        }
    }

    创建controller

    package com.szl.demo.controller;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import com.szl.demo.common.dto.AccountDto;
    import com.szl.demo.service.DemoService;
    
    @Controller
    public class DemoController {
        @Autowired
        private DemoService demoService;
        
        /**
         * @param request
         * @param response
         * @param body          这个是请求body体
         * @param nickName      这个是post参数
         * @param birthday      这个是post参数
         * @return
         */ @ResponseBody
        @RequestMapping(value = "/testAopDemo", method = RequestMethod.POST,
                consumes = "application/json",
                produces = "application/json;charset=UTF-8")
        public AccountDto testAopDemo(HttpServletRequest request, HttpServletResponse response,
                @RequestBody String body,
                @RequestParam("nickName") String nickName,
                @RequestParam("birthday") String birthday) {
            log.info("controller层 请求body内容: " + body);
            log.info("controller层 请求参数nickName内容: " + nickName);
            log.info("controller层 请求参数birthday内容: " + birthday);
            return demoService.getUserAccount();
        } }

    以上工作全部做完后,现在我们创建AOP核心代码块

    package com.szl.demo.common.aop;
    
    import java.util.Arrays;
    import javax.servlet.http.HttpServletRequest;
    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.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * @author Jimmy Shan
     * @desc 创建AOP 切面记录controller的日志记录
     *       @Slf4j 必须结合lombok一起使用,否则无效
     */
    @Slf4j
    @Aspect
    @Component
    public class WebLogAcpect {
    // 用于记录每个controller执行的耗时时间,毫秒级
    private ThreadLocal<Long> timeLocal = new ThreadLocal<>();
    /** * 定义切入点,切入点为com.example.aop下的所有函数 */ @Pointcut("execution(public * com.szl.demo.controller.*.*(..))") public void webLog(){} /** * @param joinPoint * @throws Throwable * @desc 前置通知:在连接点之前执行的通知 */    @Before("webLog()")
        public void doBefore(JoinPoint joinPoint) throws Throwable {
            long startTime = System.currentTimeMillis();
            timeLocal.set(startTime);// 记录开始时间

            // 接收到请求,记录请求内容
            ServletRequestAttributes attributes = (ServletRequestAttributes)
                    RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            
            // 记录下请求内容
            log.info("URL : " + request.getRequestURL().toString());
            log.info("HTTP_METHOD : " + request.getMethod());
            log.info("IP : " + request.getRemoteAddr());
            log.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            log.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
            
            //获取请求参数信息 和 requestBody数据
            List<String> paramList = new ArrayList<>();
            Object[] objArr = joinPoint.getArgs();
            for (int i = 0; i < objArr.length; i++) {
                 if (objArr[i] instanceof String) {
                     paramList.add(String.valueOf(objArr[i]));
                 }
            }
            log.info("AOP获取参数内容为: " + String.join("###", paramList));
        }
    /** * @param ret * @throws Throwable * @desc 处理完成请求,返回的信息 */    @AfterReturning(returning = "ret", pointcut = "webLog()")
        public void doAfterReturning(Object ret) throws Throwable {
            log.info("返回的内容: " + ret);
            log.info("耗时: {} 毫秒", (System.currentTimeMillis() - timeLocal.get().longValue()));
            timeLocal.remove();
        } }

    OK,到此我们的AOP就可以正常的工作了,我们来看一下效果,控制台日志信息输出如下:

    00:33:37.167 [http-nio-8081-exec-1] INFO   - Initializing Spring DispatcherServlet 'dispatcherServlet'
    00:33:37.167 [http-nio-8081-exec-1] INFO   - Initializing Servlet 'dispatcherServlet'
    00:33:37.171 [http-nio-8081-exec-1] INFO   - Completed initialization in 2 ms
    00:33:37.218 [http-nio-8081-exec-1] INFO   - URL : http://127.0.0.1:8081/testAopDemo
    00:33:37.218 [http-nio-8081-exec-1] INFO   - HTTP_METHOD : POST
    00:33:37.218 [http-nio-8081-exec-1] INFO   - IP : 127.0.0.1
    00:33:37.218 [http-nio-8081-exec-1] INFO   - CLASS_METHOD : com.szl.demo.controller.DemoController.testAopDemo
    00:33:37.218 [http-nio-8081-exec-1] INFO   - ARGS : [org.apache.catalina.connector.RequestFacade@69bd93bb, org.apache.catalina.connector.ResponseFacade@5a33b12, {"info2":"haha", "name":"Jackson"}, 百里守约, 2019-06-05]
    00:33:37.218 [http-nio-8081-exec-1] INFO   - AOP获取参数内容为: {"info2":"haha", "name":"Jackson"}###百里守约###2019-06-05
    00:33:37.234 [http-nio-8081-exec-1] INFO   - controller层 请求body内容: {"info2":"haha", "name":"Jackson"}
    00:33:37.234 [http-nio-8081-exec-1] INFO   - controller层 请求参数nickName内容: 百里守约
    00:33:37.234 [http-nio-8081-exec-1] INFO   - controller层 请求参数birthday内容: 2019-06-05
    00:33:37.234 [http-nio-8081-exec-1] INFO   - 返回的内容: AccountDto(accountNo=22133232001, accountName=XXX, balance=3000.0)
    00:33:37.234 [http-nio-8081-exec-1] INFO   - 耗时: 16 毫秒

    蓝色字体部分就是 AOP 和 controller 获取参数的值信息。

     转自:https://www.cnblogs.com/lic309/p/4079194.html

             https://blog.csdn.net/lmb55/article/details/82470388

  • 相关阅读:
    C#XML创建与节点对象引用
    设计模式之四单件模式
    设计模式之三抽象工厂模式
    设计模式之二工厂方法模式
    设计模式之 一简单工厂模式
    多线程之生产者---消费者模式
    c#指针用法示例。
    序列化与反序列化实现深度复制。
    把datagrid转换成gridview
    3.sql基础
  • 原文地址:https://www.cnblogs.com/jimmyshan-study/p/10972998.html
Copyright © 2011-2022 走看看