AOP

AOP是面向切面编程,是一种设计思想,它要在不改变原有目标对象的基础上,为目标对象基于动态织入的特定方式进行功能扩展。这里的特定方式一种是编译时动态,还有一种是运行时动态。我们可以将设计思想理解为OOP(面向对象编程)思想的补充和完善,OOP强调的一种静态过程,而AOP是一种动态过程,它要为设计好的对象在动态编译或运行时做服务增益,例如记录日志、事务增强、权限控制等。AOP可以在系统启动时为目标类型创建子类或兄弟类型对象,这样的对象我们称之为动态代理对象。


创建代理对象的方式有两种:

1.借助JDK官方API为目标对象创建其兄弟类型,但是目标对象类型需要实现相应的接口

2.借助CGLIB库为目标对象类型创建其子类类型对象,但是目标对象类型不能使用final修饰


AOP相关术语

切面对象(Aspect):封装了扩展业务逻辑的对象,在spring中可以使用@Aspect描述。

切入点(Poincut):定义了切入扩展业务逻辑的一些方法的集合,就是哪些方法要运行的时候切入扩展业务,会通过表达式进行相关定义。一个切面中可以定义多个切入点的定义

连接点(JoinPoint):切入点方法集合中封装了某个正在执行的目标方法信息对象。其实就是连接点是切入点方法当中的用来封装这个方法信息的对象。可以通过此对象获取具体的目标方法信息,甚至区调用目标方法(执行扩展业务逻辑)

通知(Advice):切面(Aspect)内部封装扩展业务逻辑的具体方法对象,一个切面中可以有多个通知


AOP依赖

        
            org.springframework.boot
            spring-boot-starter-aop
            2.5.2
        

package com.cy.pj.sys.service.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * @Aspect注解描述的类型为切面对象类型,此切面中可以定义多个切入点和通知方法
 */
@Aspect
@Component
@Slf4j
public class SysLogAspect {
    /**
     * @Pointcut注解用于定义切入点 bean(" spring容器中bean的名字 ")这个bean表达式为切入点表达式定义的一种语法
     * 它描述的是某个bean或多个bean中所有方法的集合为切入点;缺点是不能精确到某一个具体方法
     */
    @Pointcut("bean(sysUserServiceImpl)")//这个bean中所有方法的集合都是切入点
    //@Pointcut("bean(*ServiceImpl)")表示名字以ServiceImpl结尾的所有bean
    public void doLog() {//此方法用来承载切入点
    }

    /**
     * @param joinPoint 连接点对象,此对象封装了要执行的切入点方法信息,
     *                  可以通过连接点对象调用目标方法
     * @return 目标方法的执行结果
     * @throws Throwable
     * @Around()注解描述的方法,会在切入点执行之前和之后执行业务拓展, 在当前业务中,此方法为日志环绕通知方法
     */
    @Around("doLog()")//在这个切入点方法执行的时候,执行下面这个通知方法
    public Object doAround(ProceedingJoinPoint joinPoint/*这个连接点只能用在环绕通知中*/) throws Throwable {
       log.info("start:{}" + System.currentTimeMillis());
        try {
            Object result = joinPoint.proceed();//执行目标方法(切入点中的某个方法,就是正在执行的方法)
           log.info("end:{}" + System.currentTimeMillis());
            return result;
        } catch (Throwable e) {
            e.printStackTrace();
           log.error("Exception:{}" + System.currentTimeMillis());
            throw e;
        }
    }
}

Spring切面工作原理

当我们切面内部切入点对应的目标业务方法执行时,底层会通过代理对象用反射来访问切面中的通知方法,进而通过通知方法为目标业务做功能增强。我们可以通过断点看到,如图:
AOP_第1张图片

如果两个方法用了同一个切入点,执行顺序是随机的,要看spring容器优先扫描到哪个。


JDK动态代理

要在yml配置文件中加上

spring:
   aop:
    proxy-target-class: false


Spring中AOP通知类型

@Around(所有通知中优先级最高的通知,可以在执行方法之前和之后灵活进行业务拓展)
@Before(在目标方法之前调用)
@AfterReturning(目标方法正常结束时执行)
@AfterThrowing(目标方法异常结束时执行)
@After(目标方法正常结束和目标方法异常结束时都执行)

package com.cy.pj.sys.service.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//ProceedingJoinPoint joinPoint这个连接点只能用在环绕通知中
//如果其它方法想用连接点,可以用JoinPoint jp
@Aspect
@Component
public class SysTimeAspect {
    @Pointcut("bean(sysRoleServiceImpl)")
    public void doTime() {
    }

    @Before("doTime()")
    public void doBefore() {
        System.out.println("@Before");
    }

    @After("doTime()")
    public void doAfter() {
        System.out.println("@After");
    }

    @AfterReturning("doTime()")
    public void doAfterReturning() {
        System.out.println("@AfterReturning");
    }

    @AfterThrowing("doTime()")
    public void doAfterThrowing() {
        System.out.println("@AfterThrowing");
    }
//最重要,优先级最高
    @Around("doTime()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            System.out.println("@Around.before");
            Object proceed = joinPoint.proceed();
            System.out.println("@Around.AfterReturning");
            return proceed;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Around.AfterThrowing");
            throw e;
        } finally {
            System.out.println("@Around.After");
        }
    }
}

AOP_第2张图片


Spring中AOP切入点表达式

在Spring工程中对于切入点表达式的定义,可以分成两大类型:

粗粒度切入点表达式定义(不能精确到具体方法)
细粒度切入点表达式定义(可以精确到具体方法)

粗粒度切入点表达式定义:
bean("bean的名字")表达式:用于粗粒度切入点的定义,不能精确到具体方法。

bean(sysUserServiceImpl)//这个bean中所有方法的集合都是切入点
bean(*ServiceImpl)//表示名字以ServiceImpl结尾的所有bean

within("包名..类型")表达式:用于粗粒度切入点的定义,不能精确到具体方法。

within(" com.cy.pj.sys.impl.SysUserServiceImpl")//表示这个包下这个类里的所有方法
within(" com.cy.pj.sys.impl.*")//表示这个包下所有类里的方法
within(" com.cy.pj.sys.impl..*")//表示这个包以及子包下所有类里的方法

细粒度切入点表达式定义
execution("返回值 类全名.方法名(参数列表)")表达式:用于细粒度切入点的定义,可以精确到具体方法。

execution("int com.cy.pj.sys.impl.SysUserServiceImpl.validById(Integer, Integer)//表示方法的返回值是int类型 这个包下的这个类中的这个方法,方法参数有两个Integer类型。

execution("* com.cy.pj.sys.impl.SysUserServiceImpl.*(..)//表示任意返回值,这个包这个类中的任意方法,参数列表也是任意。

execution("* com.cy.pj.sys.impl..*.*(..)//表示任意返回值,这个包以及子包下任意类中的任意方法,参数列表也是任意。

execution("* com.cy.pj.sys.impl.*.*(..)//表示任意返回值,这个包下任意类中的任意方法,参数列表也是任意。

@annotation("注解的类全名"):用于细粒度切入点的定义,可以精确到具体方法。

@annotation("com.cy.pj.annotation.RequiredCache")//表示由RequiredCache这个注解描述的方法就是缓存切入点方法
@annotation("com.cy.pj.annotation.RequiredLog")//表示由RequiredLog这个注解描述的方法就是日志切入点方法

Spring中切面优先级设置

当项目中有多个切面时,且对应着相同的切入点,假如切面中的通知方法的执行需要有严格顺序要求,此时我们需要设置切面的优先级,这个优先级可以通过@Order(数字)注解进行描述,数字越小优先级越高。假如没有指定优先级,默认是最低的优先级。相同切入点下的多个切面会形成一个切面链。


Spring中异步操作

1.首先用@EnableAsync注解描述启动类,在我们再次运行启动类的时候,底层会帮我们配置一个线程池(这个底层配置的线程池,核心线程数默认是8)。

2.用@Async注解描述方法,在spring中会认为这个是异步切入点方法,在这个切入点方法进行时,底层会通过通知方法获取线程池中的线程,来调用切入点方法。但是这个注解不能直接去描述有方法返回值的方法,因为我们并不知道异步操作底层到底是何时结束

Spring中yml文件中线程池的配置

  task:
    execution:
      pool:
        core-size: 3               
        max-size: 5
        keep-alive: 60000
        queue-capacity: 10
      thread-name-prefix: db-service-task-

core-size:核心线程数 一般设置的值是cpu核数+1 有IO操作的设置2xcpu核数x8+1 1是指磁盘的个数
max-size:最大线程数(包含核心线程)
keep-alive:空闲等待时间
queue-capacity:队列容量,也就是等待排队的线程数
thread-name-prefix:设置线程名的前缀,用于好分辨线程是哪来的

core-size: 3 当核心线程数没有达到3时,每来一个新的任务就会创建一个新的线程,然后存储到线程池中。假如池中线程数已经达到核心线程数时,再接收新的任务时,要检查是否有空闲的核心线程,假如有,则使用空闲的核心线程执行新的任务。

queue-capacity: 假如核心线程数已经达到核心线程数的值,并且所有核心线程都在忙,再来新任务时,会将任务存储到任务队列中。

max-size: 当队列已满,核心线程数也都在忙,再来新的任务则会创建新的线程,但所有的线程数不能超过最大线程数设置的值,否则会抛出异常,拒绝执行。

keep-alive: 假如池中的线程数多余核心线程数的值,此时又没有新的任务,则一旦空闲线程空闲的时间超过keep-alive设置的时间值,则会被释放。

这些参数值都是传给ThreadPoolExecutor这个对象。


Spring中事务的处理

Spring中事务控制,推荐在业务层基于AOP方式进行实现,这样可以将事务逻辑与业务逻辑进行更好的解耦,同时重用事务逻辑代码。

@Transactional注解描述的方法为事务切入点方法,此方法在执行时就会通过通知方法为其进行事务的增强


@Transactional注解内部属性:

1.readyOnly 用来描述此事务是否为只读事务,若为只读,readOnly为true,默认值是false(表示不是只读事务),对于查询而言建议设置readOnly为true。

2.rollbackFor 用来指定,出现什么异常的时候回滚(默认RuntimeException)

3.noRollbackFor 用来指定出现什么异常时,不回滚

4.isolation用来设置事务并发执行隔离级别(隔离级别越高,数据正确性约好,但并发越差)
4.1 Isolation.READ_COMMITTED只读取别人提交的数据
4.2 Isolation.REPEATABLE_READ读数据的时候不给别人修改,重复读
4.3 Isolation.SERIALIZABLE在统计的时候不允许写入或删除数据

5.timeout超时时间 如果事务执行时间超过了这个设置的值,则抛出异常。默认-1(不超时,实际项目中建议设置超时时间)

6.propagation设置事务传播特性(默认值为Propagation.REQUIRED),不同业务对象之间的方法出现相互调用时,事务的执行策略。REQUIRED表示参与调用者的事务中去。
6.1Propagation.REQUIRED表示不管谁来调用,都参与到调用的事务中去
6.2Propagation.REQUIRES_NEW表示不管谁来调用,都不管,只在自己的事务中

如果类上和方法上都定义了事务的特性,那么方法的优先级最高

你可能感兴趣的:(AOP)