AOP即面向切面编程,是一种设计思想。它需要在不改变原有目标对象的情况下,根据动态编织的具体方法对目标对象进行功能扩展。这里的具体方式,一种是编译时动态,另一种是运行时动态。我们可以将设计思维理解为OOP(面向对象编程)思维的补充和完善。OOP强调静态过程,而AOP是动态过程,在动态编译或运行时为设计好的对象服务。收益,如日志记录、事务增强、权限控制等。AOP可以在系统启动时为目标类型创建子类或兄弟类型对象。我们称这样的对象为动态代理对象。创建代理对象有两种方法:1.使用JDK官方API为目标对象创建其兄弟类型,但目标对象类型需要实现相应的接口2.使用CGLIB库创建其子类类型对象为目标对象类型,但目标对象类型不能用final来修饰AOP相关术语切面对象(Aspect):封装扩展业务逻辑的对象,在spring中可以使用@Aspect来描述。切入点(Pointcut):定义切入扩展业务逻辑的方法集合,即哪些方法在运行时切入扩展业务,会通过表达式来定义。一方面,可以定义多个入口点。JoinPoint:入口点方法集合,封装了一个正在执行的目标方法的信息对象。其实连接点就是在切入点方法中用来封装这个方法的信息的对象。可以使用该对象获取具体的目标方法信息,甚至调用目标方法(执行扩展业务逻辑)通知(Advice):切面(Aspect)内部封装了具体的方法对象用于扩展业务逻辑,可以有多个通知AOP一方面依赖于org.springframework.bootspring-boot-starter-aop2.5.2packagecom.cy.pj.sys.service.aspect;导入lombok.extern.slf4j.Slf4j;导入org.aspectj.lang.ProceedingJoinPoint;导入org.aspectj.lang.annotation.Around;导入org.aspectj.lang.annotation.Aspect;进口组织。aspectj.lang.annotation.Pointcut;importorg.springframework.stereotype.Component;/***@Aspect注解描述的类型是切面对象类型,可以在该切面中定义多个入口点和通知方法*/@Aspect@Component@Slf4jpublicclassSysLogAspect{/***@Pointcut注解用于定义切入点bean(“spring容器中bean的名称”)。这个bean表达式是由pointcutexpression*定义的语法它描述了一个bean中所有方法的集合或者多个bean的集合是入口点;缺点是不能精确到具体某个方法*/@Pointcut("bean(sysUserServiceImpl)")//这个bean中所有方法的集合是入口点//@Pointcut("bean(*ServiceImpl)")表示所有名称以ServiceImpl结尾的beanpublicvoiddoLog(){//这个方法用来携带入口}/***@paramjoinPoint连接点对象,它封装了执行的入口点方法信息,*可以通过连接点对象调用目标方法*@return目标方法的执行结果*@throwsThrowable*@Around()注解描述的方法会在之前和之前进行业务扩展入口点执行后,在当前业务中,该方法为日志环绕通知方法*/@Around("doLog()")//当执行到该入口点方法时,执行如下通知方法publicObjectdoAround(ProceedingJoinPointjoinPoint/*这个连接点只能用在周围通知中*/)throwsThrowable{log.info("start:{}"+System.currentTimeMillis());try{Objectresult=joinPoint.proceed();//执行目标方法(切入点A中的方法就是正在执行的方法)log.info("end:{}"+System.currentTimeMillis());返回结果;}catch(Throwablee){e.printStackTrace();log.error("Exception:{}"+System.currentTimeMillis());扔e;}}}Spring切面的工作原理当我们切面内部的入口点对应的目标业务方法执行时,底层会通过代理对象Notification方法使用反射访问切面,然后通过通知方法达到目的我们可以看到通过断点来增强标准业务的功能,如图:如果两个方法使用同一个入口点,执行顺序是随机的,取决于spring容器先扫描哪个。JDK动态代理需要在Spring的AOP通知类型@Around中添加yml配置文件中的spring:aop:proxy-target-class:false(所有通知中优先级最高的通知,可以灵活扩展前后业务@Before(在目标方法之前调用)@AfterReturning(目标方法正常结束时执行)@AfterThrowing(目标方法异常结束时执行)@After(目标方法正常结束时和目标方法结束时执行)方法异常结束)包com.cy。pj.sys.service.aspect;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.*;importorg.springframework.stereotype.Component;//ProceedingJoinPointjoinPoint这个连接点只能用在surroundnotificationMiddle//如果想在其他方法中使用joinpoint,可以使用JoinPointjp@Aspect@ComponentpublicclassSysTimeAspect{@Pointcut("bean(sysRoleServiceImpl)")publicvoiddoTime(){}@Before("doTime()")publicvoiddoBefore(){System.out.println("@Before");}@After("doTime()")publicvoiddoAfter(){System.out.println("@After");}@AfterReturning("doTime()")publicvoiddoAfterReturning(){System.out.println("@AfterReturning");}@AfterThrowing("doTime()")publicvoiddoAfterThrowing(){System.out.println("@AfterThrowing");}//最重要,最高优先级@Around("doTime()")publicObjectdoAround(ProceedingJoinPointjoinPoint)throwsThrowable{try{System.out.println("@Around.before");对象proceed=joinPoint.proceed();System.out.println("@Around.AfterReturning");返回进行;}catch(Exceptione){e.printStackTrace();System.out.println("Around.AfterThrowing");扔e;}最后{System.out.println("@Around.After");}}}SpringExpressions中的AOP入口点Spring项目中切入点表达式的定义可以分为两种:粗粒度切入点表达式定义(不精确到具体方法)细粒度切入点表达式定义(精确到具体方法)coarse-grainedPointcutexpressiondefinition:bean("beanname")表达式:用于定义粗粒度的切入点,不准确到具体方法bean(sysUserServiceImpl)//这个bean中所有方法的集合是入口bean(*ServiceImpl)//代表所有名称以ServiceImpl结尾的beanwithin("packagename..type")表达式:用于粗粒度entrypoints定义不能精确到具体的方法。within("com.cy.pj.sys.impl.SysUserServiceImpl")//表示本包下该类的所有方法within("com.cy.pj.sys.impl.*")//表示本包下的方法inallclasseswithin("com.cy.pj.sys.impl..*")//表示本包和子包下所有类的方法细粒度入口点表达式定义execution("返回值类全名.methodname(parameterlist)")expression:用于定义细粒度的入口点,可以精确到具体的方法。execution("intcom.cy.pj.sys.impl.SysUserServiceImpl.validById(Integer,Integer)//表示该方法的返回值是这个int类型包下的这个类中的方法,方法参数有twoIntegertypes.execution("*com.cy.pj.sys.impl.SysUserServiceImpl.*(..)//表示任意返回值,本包该类任意方法,参数列表也是任意的。execution("*com.cy.pj.sys.impl..*.*(..)//表示任意返回值,本包和子包下任意类的任意方法,参数列表也是任意的。执行("*com.cy.pj.sys.impl.*.*(..)//表示任意返回值,本包下任意类的任意方法,参数列表也是任意的@annotation("全名oftheannotatedclass"):用于细粒度切入点的定义,可以精确到具体的方法。@annotation("com.cy.pj.annotation.RequiredCache")//表示注解RequiredCache描述的方法为缓存入口方法@annotation("com.cy.pj.annotation.RequiredLog")//表示需要通过RequiredLog这个注解描述的方法是日志入口点方法Spring切面优先级设置。当项目中有多个切面并且对应同一个入口点时,如果切面中通知方法的执行有严格的顺序要求,那么我们就需要设置切面的优先级,可以用@来描述order(number)注解,数字越小,优先级越高。如果未指定优先级,则默认为最低优先级。同一个切入点下的多个切面构成切面链。Spring中的异步操作1.首先使用@EnableAsync注解来描述启动类。当我们再次运行启动类时,底层会帮我们配置一个线程池(这个底层配置线程池默认的核心线程数是8个)。2.使用@Async注解来描述方法。在spring中,这被认为是一种异步入口点方法。当这个入口点方法进行时,底层会通过通知方法获取线程池中的线程来调用入口点方法。但是这个注解不能直接描述有方法返回值的方法,因为我们不知道异步操作底层什么时候结束Spring中yml文件中线程池的配置任务。task:execution:pool:core-size:3max-size:5keep-alive:60000queue-capacity:10thread-name-prefix:db-service-task-core-size:一般设置核心线程数tothenumberofcpucores+1,andthesettingwithIOoperationsis2xcpucoresx8+11指磁盘数max-size:最大线程数(包括核心线程)keep-alive:空闲等待时间队列-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用于设置事务并发执行隔离级别(隔离越高level,数据越多正确性好,但并发性越差)4.1Isolation.READ_COMMITTED只读别人提交的数据4.2Isolation.REPEATABLE_READ读数据不给别人修改,重复读4.3Isolation.SERIALIZABLE做统计数据时不允许写入或删除5.timeouttimeout如果事务执行时间超过这个设置的值,将抛出异常。默认-1(不超时,建议在实际项目中设置超时时间)6.propagation设置事务传播特性(默认值为Propagation.REQUIRED),不同业务之间的方法时事务的执行策略对象相互调用。REQUIRED表示参与调用者的事务。6.1Propagation.REQUIRED表示无论谁调用,都参与调用事务。6.2Propagation.REQUIRES_NEW的意思是不管谁调用,都无所谓,只有类和方法在自己的事务中定义了事务的特性,那么该方法的优先级最高