目录
1、AOP的介绍及其作用
AOP:就是面向切面编程,和OOP(面向对象编程)类似,也是一种编程思想
作用:横向抽取机制
- 采用横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现
在事务处理、日志管理、权限控制、异常处理等方面
- 分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少
对业务代码的侵入,增强代码的可读性和可维护性
- 保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能,AOP就是代理
模式的典型应用
2、AOP常用框架
- Spring AOP
使用的是动态代理,所谓的动态代理就是AOP框架不会修改字节码,而是每次运行的时候
你在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且
在特定的切点做了增强处理,并回调原对象的方法
- AspectJ
静态代理,又称为编译时增强,AOP框架会在编译阶段生成AOP代理类,并将AspectJ切面
织入到Java字节码中,运行的时候就是增强之后的AOP对象
3、常见术语
- Joinpoint(连接点):程序执行期间的某一个点,例如执行方法或处理异常时候的点。在 Spring AOP 中,连接点总是表示方法的执行。
- Advice(通知):通知是指一个切面在特定的连接点要做的事情。通知分为方法执行前通知,方法执行后通知,环绕通知等。许多 AOP 框架(包括 Spring)都将通知建模为拦截器,在连接点周围维护一系列拦截器(形成拦截器链),对连接点的方法进行增强。
- Pointcut(切点):一个匹配连接点(Join point)的谓词表达式。通知(Advice)与切点表达式关联,并在切点匹配的任何连接点(Join point)(例如,执行具有特定名称的方法)上运行。切点是匹配连接点(Join point)的表达式的概念,是AOP的核心,并且 Spring 默认使用 AspectJ 作为切入点表达式语言。
- Aspect(切面):它是一个跨越多个类的模块化的关注点,它是通知(Advice)和切点(Pointcut)合起来的抽象,它定义了一个切点(Pointcut)用来匹配连接点(Join point),也就是需要对需要拦截的那些方法进行定义;它定义了一系列的通知(Advice)用来对拦截到的方法进行增强;
- Target object(目标对象):被一个或者多个切面(Aspect)通知的对象,也就是需要被 AOP 进行拦截对方法进行增强(使用通知)的对象,也称为被通知的对象。由于在 AOP 里面使用运行时代理,所以目标对象一直是被代理的对象。
- AOP proxy(AOP 代理):为了实现切面(Aspect)功能使用 AOP 框架创建一个对象,在 Spring 框架里面一个 AOP 代理要么指 JDK 动态代理,要么指 CgLIB 代理。
- Weaving(织入):是将切面应用到目标对象的过程,这个过程可以是在编译时(例如使用 AspectJ 编译器),类加载时,运行时完成。Spring AOP 和其它纯 Java AOP 框架一样,是在运行时执行植入。
- Advisor:这个概念是从 Spring 1.2的 AOP 支持中提出的,一个 Advisor 相当于一个小型的切面,不同的是它只有一个通知(Advice),Advisor 在事务管理里面会经常遇到
Advice增强处理类型
- 方法名处理增强
- before(前置通知):通知方法在目标方法调用之前执行
- after(后置通知):通知方法在目标方法返回或异常后调用,无论如何都会执行
- after-returning(返回后通知):通知方法会在目标方法返回后调用,如果抛出异常则不执行
- after-throwing(抛出异常通知):通知方法会在目标方法抛出异常后调用
- around(环绕通知):通知方法会将目标方法封装起来
注解的方式处理增强
- @Aspect 用于定义一个切面。
- @Pointcut 用于定义一个切入点。
- @Before 用于定义前置通知,相当于 BeforeAdvice。
- @AfterReturning 用于定义后置通知,相当于 AfterReturningAdvice。
- @Around 用于定义环绕通知,相当于MethodInterceptor。
- @AfterThrowing 用于定义抛出通知,相当于ThrowAdvice。
- @After 用于定义最终final通知,不管是否异常,该通知都会执行。
4、使用AOP的方式
4.1 自定义面向切面
a.要配置pom.xml文件
<!--aop依赖1:aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
<!--aop依赖2: aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver </artifactId>
<version>1.9.5</version>
</dependency>
b.创建代理类
public class UserAOP{
//前置增强
public void beforeAdvice(JoinPoint p){
System.out.println("前置增强"+p.getSignature().getName()+"方法");
}
//后置增强(最终增强)
public void afterAdvice(JoinPoint p){
//p.getSignature().getName()被增强的方法名
System.out.println("后置增强"+p.getSignature().getName()+"方法");
}
//后置返回增强
public Object afterReturnAdvice(Object obj)throwsThrowable{
System.out.println("后置返回增强"+obj);
return obj;
}
//环绕增强
public Object aroundAdvice(ProceedingJoinPoint p){
//p.getTarget()获取被增强方法的位置
//p.getArgs()获取被增强方法的参数
System.out.println("基于环绕增强"
+p.getTarget()
+"的"+p.getSignature().getName()
+"方法,参数:"
+Arrays.toString(p.getArgs()));
Object rs=null;
try{
rs=p.proceed();//调用目标方法返回目标方法的返回结果
}catch(Throwable e){
System.out.println("基于环绕增强:"
+p.getTarget()
+"的"
+p.getSignature().getName()
+"方法,参数:"
+Arrays.toString(p.getArgs())+"发生异常:"+e.getMessage());
}finally{
System.out.println("基于环绕增强的后置处理");
}
return rs;
}
//异常增强
public void afterThrowAdvice(JoinPoint p,RuntimeException e){
System.out.println("异常增强"
+p.getSignature().getName()+"发生了"
+e.getMessage()+"异常");
}
}
c.手动配置切面
<bean id="userAOP" class="com.lyt.demo03.UserAOP"></bean>
<bean id="userService" class="com.lyt.demo02.UserServiceImpl"></bean>
<!--面向切面-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.lyt.demo02.*.*(..))"/>
<aop:aspect ref="userAOP">
<!--前置增强-->
<aop:before method="beforeAdvice" pointcut-ref="pointcut"/>
<!--后置增强返回值returning="obj"-->
<aop:after-returning returning="obj" method="afterReturnAdvice" pointcut-ref="pointcut"/>
<!--环绕增强-->
<aop:around method="aroundAdvice" pointcut-ref="pointcut"/>
<!--异常增强异常throwing="e"-->
<aop:after-throwing throwing="e" method="afterThrowAdvice" pointcut-ref="pointcut"/>
<!--最终增强-->
<aop:after method="afterAdvice" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
4.2 注解的方式实现
第一步:开启扫描
<context:component-scan base-package="com.lyt"/>
第二步:开启注解切面
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
代理类
@Aspect //面向切面
@Component //将UserAOP注解为Bean
public class UserAOP{
@Pointcut("execution(*com.lyt.demo02.*.*(..))")
public void pointcut(){}
//前置增强
@Before("pointcut()")
public void beforeAdvice(JoinPoint p){
System.out.println("前置增强"+p.getSignature().getName()+"方法");
}
//后置增强(最终增强)
@After("pointcut()")
public void afterAdvice(JoinPoint p){
//p.getSignature().getName()被增强的方法名
System.out.println("后置增强"+p.getSignature().getName()+"方法");
}
//后置返回增强
@AfterReturning(value="pointcut()",returning="obj")
public Object afterReturnAdvice(Object obj)throwsThrowable{
System.out.println("后置返回增强"+obj);
return obj;
}
//环绕增强
@Around("pointcut()")
public Object aroundAdvice(ProceedingJoinPoint p){
//p.getTarget()获取被增强方法的位置
//p.getArgs()获取被增强方法的参数
System.out.println("基于环绕增强"+p.getTarget()+"的"+p.getSignature().getName()+"方法,参数:"
+Arrays.toString(p.getArgs()));
Object rs=null;
try{
rs=p.proceed();//调用目标方法返回目标方法的返回结果
}catch(Throwable e){
System.out.println("基于环绕增强:"+p.getTarget()+"的"+p.getSignature().getName()+"方法,参数:"
+Arrays.toString(p.getArgs())+"发生异常:"+e.getMessage());
}finally{
System.out.println("基于环绕增强的后置处理");
}
return rs;
}
//异常增强
@AfterThrowing(value="pointcut()",throwing="e")
public void afterThrowAdvice(JoinPoint p,RuntimeException e){
System.out.println("异常增强"
+p.getSignature().getName()+"发生了"
+e.getMessage()+"异常");
}
}