Spring Boot 面向切面编程

目录

  • 前言
  • 编程范式主要有以下几类
  • aop注解
  • 用法

前言

spring提供两个核心功能,一个是Ioc(控制反转),另一个是Aop(面向切面编程),Ioc有助于应用对象之间的解耦,AOP则可以实现横切关注点(如日志、安全、缓存、重复提交和事务管理)与他们所影响的对象之间的解耦。

编程范式主要有以下几类

  • AOP(Aspect Oriented Programming)面向切面编程
  • OOP(Object Oriented Programming)面向对象编程
  • POP(procedure oriented programming)面向过程编程
  • FP(Functional Programming)面向函数编程

aop注解

AOP主要包含了通知、切点和连接点灯术语,介绍如下:

  • 通知(Advice)

通知定义了切面是什么以及何时被调用,何时调用包含以下几种:

  1. Before 在方法被调用之前调用通知
  2. After 在方法完成之后调用通知,无论方法执行是否成功
  3. After-returning 在方法成功执行之后调用通知
  4. After-throwing 在方法抛出异常后调用通知
  5. Around 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
  • 切点(PointCut)

通知定义了切面是什么和何时被调用,切点定义了何处被调用,切点的定义会匹配通知所要织入的一个或多个连接点,我们通常使用明确的类的方法名称来指定这些切点,或是利用正则表达式定义匹配的类和方法名称来指定这些切点。

  • 连接点(JoinPoint)

连接点是在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时,抛出异常时,甚至是修改一个字段时,切面代码可以利用这些连接点插入到应用的正常流程中,并添加新的行为,如日志、安全、事务、缓存等。
(被拦截到的点,因为spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接带你还可以是字段或者构造器。)

AOP核心概念

  • @Aspect: 切面,由通知和切入点共同组成,这个注解标注在类上表示为一个切面。
  • @Joinpoint: 连接点,被AOP拦截的类或者方法,在前置通知中有介绍使用@Joinpoint获取类名、方法、请求参数。
  • Advice: 通知的几种类型
  • @Before: 前置通知,在某切入点@Pointcut之前的通知
  • @After: 后置通知,在某切入点@Pointcut之后的通知无论成功或者异常。
  • @AfterReturning: 返回后通知,方法执行return之后,可以对返回的数据做加工处理。
  • @Around: 环绕通知,在方法的调用前、后执行。
  • @AfterThrowing: 抛出异常通知,程序出错跑出异常会执行该通知方法。
  • @Pointcut: 切入点,从哪里开始。例如从某个包开始或者某个包下的某个类等。

用法

AOP在spring中有两种配置方式,一是xml配置的方式,二是自动注解的模式。

自动注解AOP
声明切面类,包含注解@Aspect以及何时执行通知(Advice)
package com.ganji.demo.service.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Service;

/**
 * Created by admin on 2015/9/2.
 */
@Aspect
@Service
public class XmlAopDemoUserLog {

// 配置切点 及要传的参数   
    @Pointcut("execution(* com.ganji.demo.service.user.UserService.GetDemoUser(..)) && args(id)")
    public void pointCut(int id)
    {

    }

// 配置连接点 方法开始执行时通知
    @Before("pointCut(id)")
    public void beforeLog(int id) {
        System.out.println("开始执行前置通知  日志记录:"+id);
    }
//    方法执行完后通知
    @After("pointCut(id)")
    public void afterLog(int id) {
        System.out.println("开始执行后置通知 日志记录:"+id);
    }
//    执行成功后通知
    @AfterReturning("pointCut(id)")
    public void afterReturningLog(int id) {
        System.out.println("方法成功执行后通知 日志记录:"+id);
    }
//    抛出异常后通知
    @AfterThrowing("pointCut(id)")
    public void afterThrowingLog(int id) {
        System.out.println("方法抛出异常后执行通知 日志记录"+id);
    }

//    环绕通知
    @Around("pointCut(id)")
    public Object aroundLog(ProceedingJoinPoint joinpoint,int id) {
        Object result = null;
        try {
            System.out.println("环绕通知开始 日志记录"+id);
            long start = System.currentTimeMillis();

            //有返回参数 则需返回值
            result =  joinpoint.proceed();

            long end = System.currentTimeMillis();
            System.out.println("总共执行时长" + (end - start) + " 毫秒");
            System.out.println("环绕通知结束 日志记录");
        } catch (Throwable t) {
            System.out.println("出现错误");
        }
        return result;
    }
}

以上即实现aop。

spring AOP之proceedingjoinpoint和joinpoint区别

import org.aspectj.lang.reflect.SourceLocation;  
public interface JoinPoint {  
   String toString();         //连接点所在位置的相关信息  
   String toShortString();     //连接点所在位置的简短相关信息  
   String toLongString();     //连接点所在位置的全部相关信息  
   Object getThis();         //返回AOP代理对象,也就是com.sun.proxy.$Proxy18
   Object getTarget();       //返回目标对象,一般我们都需要它或者(也就是定义方法的接口或类,为什么会是接口呢?这主要是在目标对象本身是动态代理的情况下,例如Mapper。所以返回的是定义方法的对象如aoptest.daoimpl.GoodDaoImpl或com.b.base.BaseMapper)
   Object[] getArgs();       //返回被通知方法参数列表  
   Signature getSignature();  //返回当前连接点签名  其getName()方法返回方法的FQN,如void aoptest.dao.GoodDao.delete()或com.b.base.BaseMapper.insert(T)(需要注意的是,很多时候我们定义了子类继承父类的时候,我们希望拿到基于子类的FQN,这直接可拿不到,要依赖于AopUtils.getTargetClass(point.getTarget())获取原始代理对象,下面会详细讲解)
   SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置  
   String getKind();        //连接点类型  
   StaticPart getStaticPart(); //返回连接点静态部分  
  }  
 
 public interface ProceedingJoinPoint extends JoinPoint {  
       public Object proceed() throws Throwable;  
       public Object proceed(Object[] args) throws Throwable;  
 } 
方法名 功能
Signature getSignature(); 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs(); 获取传入目标方法的参数对象
Object getTarget(); 获取被代理的对象
Object getThis(); 获取代理对象

proceedingjoinpoint和joinpoint的区别
JoinPoint的用法

ProceedingJoinPoint 执行procceed方法的作用

1、环绕通知ProceedingJoinPoint执行proceed方法的作用是让目标方法执行,这也是环绕通知和前置、后置通知方法的一个最大区别。
2、简单理解,环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的。

你可能感兴趣的:(aop,spring-mvc,springboot,java)