面向切面编程(AOP)
为什么会有面向切面编程?
面向切面是针对对面向对象编程的一种补充,
在开发时有时使用面向对象并不能很好完成一些额外的功能业务时,可以采用AOP来进行补充。
切面编程在代码中主要做了什么?
对目标对象的进行额外的增强或扩展,
原理就是对方法前后进行了织入了增强代码,
而增强代码本质也就是方法,就是对目标行为植入额外的逻辑代码,从而增强原有的功能。
了解AOP编程
在Spring中的AOP是基于JDK动态代理和CGLIB动态代理是实现的。
而Spring只是基于AspectJ的切点表达式语法(如@Pointcut
),再结合动态代理机制(JDK Proxy或CGLIB)实现的轻量级AOP。
其支持范围仅限于支持方法执行连接点且必须纳入Spring容器管理
咨询:AspectJ 作为独立的 AOP 语言扩展,支持全连接点类型和更底层的代码织入
实现SpringAOP案例:
对服务层的方法额外增强
Service层:目标连接点对象
@Service
public class UserService {
public String save(String name){
System.out.println("保存用户");
return "holle"+name;
}
}
配置类
Spring AOP 的设计目标是为 Spring 生态提供简单易用、低侵入性的 AOP 支持,而非替代 AspectJ。
通过@EnableAspectJAutoProxy
即用启用,无需额外编译器或复杂的编织配置
@Configuration
@ComponentScan(basePackages = "edu.keep")
@EnableAspectJAutoProxy(proxyTargetClass = true)
//启用AspectJ注解处理器
public class AppConfig {
}
切面类
@Aspect
@Component
public class UserServiceAspect {
/**
* 使用@Pointcut注解标识在一个任意的方法上表示未切入点
* 主要是来编写切入点表达式(AspectJ的切入点表达式)
* execution()是用来执行切入点表达式的一个函数
* 下面的表达式中:
* 第一个星号:表示连接点方法的访问修饰符
* 第二个星号:表示所有类
* 第三个星号:表示类下面的所有方法
* (星号可以改为具体的类名或者方法名)
* (..)表示任意类型和个数的参数
*
* 因此,被切入点找到的这些方法都称之为连接点
*/
@Pointcut("execution(* edu.keep.service.UserService.*(..))")
public void pointcut(){
}
/**
* 前置通知
* @param jp 连接点信息(目标方法的信息)
*/
@Before("pointcut()")
public void before(JoinPoint jp){
System.out.println("前置通知,参数:"+jp.getArgs()[0]);
}
/**
* 后置通知
* @param jp 连接点信息
* @param returnVal 连接点方法的返回值
*/
@AfterReturning(value = "pointcut()",returning = "returnVal")
public Object after(JoinPoint jp,String returnVal){
System.out.println("后置通知,返回值:"+returnVal);
return returnVal;
}
/**
* 环绕通知
* @param jp 连接点信息
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕通知前...");
//调用目标方法
Object proceed = jp.proceed();
System.out.println("环绕通知后...");
return proceed;
}
/**
* 异常通知
*/
@AfterThrowing(value = "pointcut()",throwing = "e")
public void afterThrowing(JoinPoint jp,Exception e){
System.out.println("异常通知,异常:"+e.getMessage());
}
/**
* 最终通知
* @param jp 连接点信息
*/
@After("pointcut()")
public void after(JoinPoint jp){
System.out.println("最终通知");
}
}
总结具体实现流程:
1.定义切入点:通过表达式指定增强的方法
2.编写通知或增强,本质就是定义增强逻辑(如日志、事务)方法,并在指定时机(方法执行前、后、异常时)触发。
3.动态代理:通过 JDK 动态代理(接口代理)或 CGLIB 动态代理(类代理)生成代理对象,包裹目标对象。
应用场景
日志操作:
可以在业务方法前后进行日志的记录,不需要每个业务方法中都编写重复的代码
权限管理:
可以在调用目标方法前确定是否有权限
事务管理 :
可以在调用业务方法前开启事务,方法执行完成后提交事务
AOP术语
切面(Aspect):
切面(本质上也是一个类在)是用于编写增强逻辑的一个类,这个类很类似于JDK动态代理中的回调处理器或者cglib中的方法拦截器,主要就是将需要增强目标对象的功能代码编写在这个类中,而这些功能增强的代码就是切面逻辑。
通知/增强(Advice):
增强(本质上也是方法)就是对目标行为植入额外的逻辑代码,从而增强原有的功能。增强分为五种类型:
1)前置通知(在目标方法调用之前执行)
2)后置通知(在目标方法正确返回之后执行)
3)环绕通知(在目标方法调用前后执行)
4)异常通知(当目标方法抛出异常时执行,并且不会执行后置通知)
5)最终通知(不管目标方法有无异常都会执行)
切入点(Pointcut):
切入点类似一个切入的坐标,目的就是要找到目标对象的哪些方法进行切入或增强。切入点可以使用表达式进行描述。
连接点(Joinpoint):
目标对象的方法(被切入的方法)就称之为连接点,一个切入点可以对应目标对象的的多个连接点。
代理(Proxy):
在运行时动态创建的对象,称之为代理对象,负责调用目标对象的方法,并执行增强功能。
目标(Target):
被代理的对象就是目标对象。
织入(Weaver):
将切面中的增强逻辑应用到目标具体的连接点上并产生代理的过程称之为织入。
因此通常描述为“将通知织入到具体的目标”。
织入的时机可以分为以下几种:
类加载时织入,需要特殊的类加载器(LTW)
编译时织入,需要特殊的编译器(CTW)
运行时织入,通常使用JDK或者CGLIB在程序运行创建代理对象,
spring就是基于运行时织入的。(注意:spring仅仅只是用到了AspectJ的切入点表达式和注解,但并没有使用AspectJ的类加载和编译时织入功能,而是使用JDK和CGLIB在运行时生成代理对象。)