1.AOP工作流程
2.AOP概述
AOP(Aspect Orient Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程(OOP)的一种补充和完善。实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少个模块,一个模块有哪些对象,对象有哪些属性),面向切面理解为一个动态过程(在对象运行时动态织入一些扩展功能或控制对象执行)。如图所示:
3.实现原理
AOP可以在系统启动时为目标类型创建子类或兄弟类型对象,这样的对象我们通常会称之为动态代理对象.如图所示:
其中,为目标类型(XxxServiceImpl)创建其代理对象方式有两种(先了解):
第一种方式:借助JDK官方API为目标对象类型创建其兄弟类型对象,但是目标对象类型需要实现相应接口.
第二种方式:借助CGLIB库为目标对象类型创建其子类类型对象,但是目标对象类型不能使用final修饰.
4.AOP使用流程
1.添加aop依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
说明:基于此依赖spring可以整合AspectJ框架快速完成AOP的基本实现。AspectJ 是一个面向切面的框架,他定义了 AOP 的一些语法,有一个专门的字节码生成器来生成遵守 java 规范的 class 文件。
2.创建注解类型,应用于切入点表达式的定义,关键代码如下:
package com.jt.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredLog {
String operation() default "";
}
3.创建切面对象,用于做日志业务增强,关键代码如下:
package com.jt.aop;
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注解描述的类我们称之为切面对象,此对象负责定义切入点和通知
* 1)切入点(在哪些方法执行时我们进行功能扩展-锦)
* 2)通知(所有的扩展逻辑都会写到通知方法中-花)
*/
@Aspect
@Component
public class LogAspect {
/**
* 通过@Pointcut注解定义定义切入点表达式,表达式的写法有多种,比较常用
* 有注解方式的表达式(@annotation(你自己定义的注解)).当使用自己写
* 的注解对方法进行描述时,这个方法就是切入点方法.在这个方法上要锦上添花
*/
@Pointcut("@annotation(com.jt.aop.RequiredLog)")
public void doLog(){}//这个方法没有意义,主要用于承载切入点
/**
* Around通知,在此通知内部可以定义我们扩展业务逻辑,还可以调用目标执行链
* @param joinPoint 连接点(通知方法与目标方法的连接点对象),ProceedingJoinPoint
* 类型的连接点只能应用在Around通知方法中.
* @return
* @throws Throwable
*/
@Around("doLog()")
public Object doAround(ProceedingJoinPoint joinPoint)
throws Throwable{
System.out.println("Before:"+System.currentTimeMillis());
Object result=joinPoint.proceed();//执行链(其它切面,目标对象方法)
System.out.println("After:"+System.currentTimeMillis());
return result;
}
}
4.通过注解RequiredLog注解描述日志查询或删除业务相关方法,此时这个方法为日志切入点方法,例如:
@RequiredLog(operation="公告查询")
@Override
public List<SysLog> findLogs(SysLog sysLog) {
List<SysLog> list=syslogDao.selectLogs(sysLog);
return list;
}
5.测试通知业务方法,并检测日志输出以及了解其运行原理,如图所示:
5.AOP进阶技术
Spring框架AOP模块定义通知类型,有如下几种:
@Around (优先级最高的通知,可以在目标方法执行之前,之后灵活进行业务拓展.)
@Before (目标方法执行之前调用)
@AfterReturning (目标方法正常结束时执行)
@AfterThrowing (目标方法异常结束时执行)
@After (目标方法结束时执行,正常结束和异常结束它都会执行)
5.1定义注解代码如下:
package com.cy.pj.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredTime{}
5.2定义时间切面对象对象演示通知执行,关键代码如下:
package com.cy.pj.sys.service.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class SysTimeAspect {
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredTime)")
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 result = joinPoint.proceed();
System.out.println("@Around.AfterReturning");
return result;
}catch(Exception e){
System.out.println("@Around.AfterThrowing");
e.printStackTrace();
throw e;
}finally {
System.out.println("@Around.after");
}
}
}
5.3切面执行顺序
切面的优先级需要借助@Order注解进行描述,数字越小优先级越高,默认优先级比较低。例如:
定义日志切面并指定优先级。
@Order(1)
@Aspect
@Component
public class SysLogAspect {
…
}
定义缓存切面并指定优先级:
@Order(2)
@Aspect
@Component
public class SysCacheAspect {
…
}
切入点增强
切入点的地中方法:
细粒度
@annotation(注解详细路径)
@execution(返回值类型 方法全路径 方法)
粗粒度
@bean("bean(userController)") @bean("bean(*Controller)")
@within(包的全路径.*) @within(包的全路径..*)
package cn.tedu.springweb.aop;
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
@Component
public class LogAspect {
@Pointcut("@annotation(cn.tedu.springweb.aop.RequiredLog)")
public void doLong(){};
//@Around("doLong()")
//@Around("@annotation(cn.tedu.springweb.aop.RequiredLog)")
//@Around("bean(userController)")
//@Around("bean(*Controller)")
//@Around("within(cn.tedu.springweb..*)")
//@Around("execution(String cn.tedu.springweb.controller.UserController.aaa())")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Before:"+System.currentTimeMillis());
Object result = proceedingJoinPoint.proceed();
System.out.println("After"+System.currentTimeMillis());
return result;
}
}