我靠Spring AOP知识又涨了一点点薪!理论+实战(AOP统一异常处理)

1 篇文章 0 订阅

介绍:

AOP:面向编程,相对于OOP面向对象编程

Spring AOP 的存在就是用来解耦的 ,AOP可以让一组类共享相同的行为.在OOP中只能通过继承和实现接口,来使代码的耦合度增强,且类继承只能为单继承,阻碍更多行为添加进一组类上.AOP拟补了OOP的不足

AOP中的相关概念

Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。

Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。

Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。

Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。

Target(目标对象):织入 Advice 的目标对象.。

Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

 

举个例子:(感觉不是很好)

就举最近出现的肺炎疫情来讲吧. 比如说我们到了三月份就可以进行上班了,  公司所属大楼,大楼门外 就是一个切面. 因为我们也不知道哪个员工得肺炎,那么物业就会在出入口进行设卡,其检测设备就是体温枪,红外感温摄像头,对进大楼的每个员工进行检测. 而根据肺炎症状是体温超过37度,未带口罩被感染引擎.物业老王就有权限阻止其进入大厦,报警对其进行隔离.

 

Spring AOP 中 Joint point 指代的是所有方法的执行点, 而 pointcut 是一个描述信息, 它修饰的是 Joint point, 通过 pointcut, 我们就可以确定哪些 Joint point 可以被织入 Advice

 

Joint point 指代的是所有方法的执行点 上面例子中,就是所有要这栋大厦上班的员工,在Spring AOP中所有要被执行的方法执行点.

Pointcut(切点):我们知道所有要被执行的方法都被织入Advice中,我们一般都会根据需要加入织入Advice中,而Pointcut(切点)的作用就是提供一组规则来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice.那么上面例子中,根据肺炎症状:体温超过37度,未带口罩者 的员工物业老王就有权限阻止其进入大厦,报警对其进行隔离.

 

Advice(增强): 那就是对体温超过37度,未带口罩者的员工进行一些处理.比如说 进大厦之前先测体温,对应的是before前置增强;对未带口罩的员工体温正常后然后进行放行进入大厦,送一个口罩 ,送的口罩就是after后置增强了.

Aspect::Aspect 是 point cut 与 Advice 的组合 

 

我们来用代码实现一个统一日志栗子来实践理论知识:

Spring AOP 依赖 引入

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        
        <!-- google 工具包 -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>29.0-jre</version>
        </dependency>
        
        <!--Swagger-UI API文档生产工具-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>
        
        <!--Hutool Java工具包-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.2</version>
        </dependency>

另外那些都是使用的工具包.在工作中能给您很大的帮助.希望掌握.

 

做个controller层

AopDemoController.java

package com.example.demo.controller;

import com.example.demo.annotation.AopAnnotation;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AopDemoController {


    @ApiOperation("这个是swagger注释")
    @AopAnnotation(name = "方法注释")
    @PostMapping("/test")
    public Object getPath( Long s) {
        System.out.println(s);
        return s;
    }
}

自定义注解

package com.example.demo.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *注解本身是没有任何功能的,注解跟xml都是一种元数据,既解释数据的数据
 *@author: Garol
 *@create: 2020/5/26 11:38
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AopAnnotation {

     String name( );

}

 

AOP核心配置 (注释很详细了,这就不啰嗦了)

AOPespect

package com.example.demo.config;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.example.demo.annotation.AopAnnotation;
import com.google.common.collect.Maps;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.HttpRequestHandlerServlet;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.MalformedParameterizedTypeException;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * @author Administrator
 */

@Aspect
@Component
@Order(1)
public class AopAspect {


    /**
     * 注解声明切点 : 就是需要对某个文件的增强路径
     */
    @Pointcut("execution(public * com.example.demo.controller.*.*(..))")
    public void test() {

    }

    /**
     * 前置增强: 方法执行前执行增强
     */
    @Before("test()")
    public void beforeTest(JoinPoint joinPoint) {

        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        //获取注解信息
        AopAnnotation annotation = methodSignature.getMethod().getAnnotation(AopAnnotation.class);
        System.out.println("前置通知获取注解信息:" + annotation.name());
        System.out.println("beforeTest");
    }

    /**
     * 后置增强:
     */
    @AfterReturning("test()")
    public void afterTest() {
        System.out.println("beforeTest21");
    }

    /**
     * 环绕增强, 既可以前置增强,后置增强,异常增强. (建议使用)
     *
     * @param joinPoint
     * @return
     */
    @Around("test()")
    public Object aroundTest(ProceedingJoinPoint joinPoint) {
        //获取当前请求对象
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();

        //前置增强
        long start = System.currentTimeMillis();

        //一般我们可以记录些访问者信息比如ip 登录,操作用户,操作时间类的,我们可以创建个实体类来记录
        //这边直接使用map存储了
        Map map = Maps.newHashMap();
        String url = request.getRequestURL().toString();
        map.put("url", url);
        map.put("uri", request.getRequestURI());
        map.put("startTime", start);
        map.put("bashPath", StrUtil.removeSuffix(url, URLUtil.url(url).getPath()));
        map.put("ip", request.getRemoteAddr());

        System.out.println("before1");
        Object proceed = null;
        try {
            proceed = joinPoint.proceed();
            //下面是后置增强
            Signature signature = joinPoint.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            Method method = methodSignature.getMethod();

            //判断是否有swagger注解
            if (method.isAnnotationPresent(ApiOperation.class)) {
                ApiOperation annotation = method.getAnnotation(ApiOperation.class);
                String value = annotation.value();
                //存储swagger注解
                map.put("operation", value);
            }


            //获取注解信息
            AopAnnotation annotation = method.getAnnotation(AopAnnotation.class);
            //打印注解名称
            System.out.println(annotation.name());
            //打印方法的名称
            System.out.println("方法名:" + method.getName());

            System.out.println("after1");
            long endTime = System.currentTimeMillis();
            long spendTime = endTime - start;
            System.out.println("总执行时间:" + (endTime - start));
            //设置结束时间
            map.put("endTime", endTime);
            //执行时间
            map.put("spendTime", spendTime);
            //设置增强后的body
            map.put("result", proceed);
            System.out.println(map);
        } catch (Throwable throwable) {
            System.out.println("异常增强: ...");
            //TODO 可以做异常增强
            throwable.printStackTrace();
        }
        //将增强后的对象返回
        return proceed;
    }


}

执行程序结果:

before1
前置通知获取注解信息:方法注释
beforeTest
1
方法注释
方法名:getPath
after1
总执行时间:39
{result=1, bashPath=http://localhost:8080, ip=127.0.0.1, startTime=1590472592787, endTime=1590472592826, uri=/test, url=http://localhost:8080/test, spendTime=39}
beforeTest21

 

 

总结

在AOP中切面就是与业务逻辑独立,但又垂直存在于业务逻辑的代码结构中的通用功能组合;切面与业务逻辑相交的点就是切点;连接点就是把业务逻辑离散化后的关键节点;切点属于连接点,是连接点的子集;Advice(增强)就是切面在切点上要执行的功能增加的具体操作;在切点上可以把要完成增强操作的目标对象(Target)连接到切面里,这个连接的方式就叫织入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值