Spring--AOP面向切面编程

一、AOP

1、AOP定义:

        AOP是一个面向切面的编程,基于动态代理,动态将代码功能切入到所需要的地方,对原代码无入侵

生活理解:(选择性阅读)

  • 在健身房跑步机跑步,你戴着心率表,只管跑你的步
  • 跑前/跑时,我走过去,暂停跑步机,记录第一次生命特征信息,再打开跑步机,你继续接着跑
  • 红色部分:你一直在实现跑步的功能(相当于原代码功能一直在执行)
  • 紫色部分:
    • 我就是那个AOP,我的一系列动作,是我在做的;
    • 在你跑前/跑时的时候,去切断你的动作,先执行我的一系列动作,然后是让你恢复继续跑;我的存在和动作,都不会改变你脑子在想跑的功能
    • (所以我对你跑的代码,是没有入侵的,跟你没关系)
  • note:那么就会有同学想到,同步调用方法不也可以实现吗?
    • 我在执行:上述AOP方式
    • 你在执行:调用方法方式
      • 如果把上述中我的动作,以调用方法的方式去做。那么这个调用、执行这些动作,就不是我在执行,是你在调用,执行;
      • 原本你的只需要管你跑步就行,但是你又要去管记录生命特征信息的动作(此时就是对你跑的代码,存在入侵)
  • 结论:
    • AOP方式:我想给你,给他,给谁,什么时间,去执行,都是我来管,跟你没关系(对原代码无入侵)
    • 调用方法方式:你是否记录,什么时间记录,或者他要记录,那么你他都需要增加/减少代码(对原代码有入侵)

2、AOP常见作用:

  • 日志记录
  • 性能统计
  • 安全控制
  • 事务处理
  • 异常处理
  • 扩展

3、AOP相关概念

  • Aspect(切面): Aspect 切面类,包含着一些 切入点(Pointcut) 以及通知( Advice)
  • Joint point(连接点):表示Target(目标对象)中明确定义的点。
  • Pointcut(切点):通过通配、正则表达式等方式定义,它定义了相应的 Advice(通知) 将要发生的地方。
  • Advice(通知):具体要操作的内容
  • Target(目标对象):织入 Advice(通知) 的目标对象.。
  • Weaving(织入):将 Aspect 切面类 和其他对象连接起来, 并创建 Adviced object 的过程

4、AOP重要的六个注解,两个对象

  • Pointcut(切点)中包含一个重要注解

    • 切入点(Pointcut)
      • 通过通配、正则表达式等方式定义
  • Advice(通知)中包含五个重要注解

    • 前置通知(@Before)
      • 连接点(JoinPoint)之前执行的通知
    • 后置通知(@After Advice)
      • 连接点退出时,执行的通知(无视异常,异常时也会执行)
    • 返回后通知(After Return Advice)
      • 连接点正常完成后执行的通知(异常时不执行)
    • 异常通知(After Throwing Advice)
      • 在方法抛出异常退出时执行的通知
    • 环绕通知(Around Advice)
      • 可以代替前面四种
  • 两个对象

    • JoinPoint 
      • 封装了切面方法的信息
    • ProceedingJoinPoint
      • 继承JoinPoint的子类,有两个用于“执行连接点方法”的方法
    • 两者关系:

5、 切入点(Pointcut)表达式

语法:execution(访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表))

note:访问修饰符可省略

  • 标准表达式:

    • public void com.aop.service.impl.xxxxImpl.run();
  • 省略修饰符表达式:

    • void com.aop.service.impl.xxxxImpl.run();
  • 返回值“*”,表示任意返回值

    • * com.aop.service.impl.xxxxImpl.run();
  • 包名“*”,表示任意包

    • * *.*.*.*.xxxxImpl.run();
    • note:一个包对应一个*
  • 包名“..”,表示当前包及其子包

    • * *..xxxxImpl.run();
  • 类名和方法名 “*”

    • * *..*.*();
  • 参数列表的写法

    • 基本类型直接写 long
    • 引用类型写全包名
    • 用“*”:有参数时,任意类型
    • 用“..”:表示有或无参数,有时,可表示任意类型
    • 例:* *..*.*(..)
  • note:业务层语法需要在参数列表后面加异常

    • execution(访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表) 异常)

    • * void com.aop.service.impl.*.*(..)**;

6、代码实现

第一步:AOP拦截类

@Aspect
@Component
public class TestAspect {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    public TestAspect() {
    }

    @Pointcut("execution(* com.xxx.study.controller.*.*(..))")
    public void log() {
    }

    /**
     * 目标方法执行前
     */
    @Before("log()")
    public void doBefore() {
        System.out.println();
        System.out.println("-------doAfter-------");
        System.out.println("-------第一步-------");
    }



    /**
     * 目标方法执行响应结果后
     * 出现异常不出现
     * @param result
     */
    @AfterReturning(pointcut = "log()",returning = "result")
    public void doAfterReturn(Object result) {
        System.out.println("----AfterReturning----");
        System.out.println("-------第二步-------");
    }

    /**
     * 目标方法抛出异常时
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "log()",throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint,Exception e){
        System.out.println("-------异常-------");
    }

    /**
     * 目标方法执行之后
     * note:无视异常
     * @param joinPoint
     */
    @After("log()")
    public void doAfter(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String url = request.getRequestURL().toString();
        String ip = request.getRemoteAddr();
        String classMethod = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        RequestMethod requestMethod = new RequestMethod(url, ip, classMethod, args);
        System.out.println(requestMethod.toString());
        System.out.println("----doAfter----");
        System.out.println("-------第三步-------");
    }



    /**
     * 可以代替前四种
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("log()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("----around:第一步----");

        Object ret = proceedingJoinPoint.proceed();
        System.out.println("-------around:第三步-------");
        return ret;

    }

    private class RequestMethod {
        private String url;
        private String ip;
        private String classMethod;
        private Object[] args;

        public RequestMethod(String url, String ip, String classMethod, Object[] args) {
            this.url = url;
            this.ip = ip;
            this.classMethod = classMethod;
            this.args = args;
        }

        @Override
        public String toString() {
            return "RequestMethod{url='" + this.url + '\'' + ", ip='" + this.ip + '\'' + ", classMethod='" + this.classMethod + '\'' + ", args=" + Arrays.toString(this.args) + '}';
        }
    }
}

第二步:请求接口方法

@RestController
@RequestMapping({"/entry"})
public class TestEntry {

    @RequestMapping({"/hello"})
    public String helloTest() {
        System.out.println("----目标方法:进入----");
//       测试@AfterThrowing将下面代码注释去掉,执行测试一下
//        int s = 1/0;
        return "----目标方法:响应----";
    }
}

第三步:请求接口测试

  • @Around:在最开始和最后面,包裹所有
  • @Before:目标方法执行前,第一步

  • 目标方法执行

  • @AfterReturning:在目标方法响应后执行;出现异常不出现

  • @After:在目标方法响应结束,表示目标方法整个都结束后执行;无视异常

  • @AfterThrowing:在Before和After之间,执行目标方法时,抛出异常会执行

 note:

  •         注意看此处我将目标方法中的int s = 1/0打开,让其报错
  •         在如下图片中,目标方法进入后,抛出异常
  •         执行了AOP中的@AfterThrowing方法
  •         最后还执行了@After方法,注意此方法是无视异常的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值