文章目录
1.AOP理解
作用:程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对已有方法进行增强
优势:减少重复代码 提高开发效率 维护方便
2.Spring中的AOP
2.1 AOP相关术语
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。
Target(目标对象):代理的目标对象。Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类。Aspect(切面):是切入点和通知(引介)的结合。
2.2 Spring做了什么
Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
3.基于XML的AOP
3.1步骤
1.配置通知类进入spring容器
bean or @Component
2.aop:config声明aop配置
就是标签
3.aop:aspect配置切面
4.aop:pointcut配置切入点表达式(不一定有)
可用下面aop:xxx配置通知类型中的pointcut属性代替
5.aop:xxx配置对应的通知类型
3.2通知类型
1.aop:before作用:用于配置前置通知.
指定增强的方法在切入点方法之前执行
属性:
method:用于指定通知类中的增强方法名称
ponitcut-ref:用于指定切入点的表达式的引用
poinitcut:用于指定切入点表达式
执行时间点:切入点方法执行之前执行
<aop:before method="beforePrintLog" pointcut="execution(* priv.xy.service.impl.*.*(..))"></aop:before>
2.aop:after-returning作用:用于配置后置通知.
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
执行时间点:切入点方法正常执行之后。
它和异常通知只能有一个执行
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pc1"></aop:after-returning>
3.aop:after-throwing作用:用于配置异常通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
执行时间点:切入点方法执行产生异常后执行。
它和后置通知只能执行一个
<aop:after-throwing method="afterthrowingPrintLog" pointcut-ref="pc1"></aop:after-throwing>
4.aop:after作用:用于配置最终通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
执行时间点:无论切入点方法执行时是否有异常,它都会在其后面执行。
<aop:after method="afterPrintLog" pointcut-ref="pc1"></aop:after>
5.aop:around:作用:用于配置环绕通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的
执行时间点:可以代替前面4种通知
引用说明:它是spring框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式。
注意:通常情况下,环绕通知都是独立使用的
<aop:around method="aroundPrintLog" pointcut-ref="pc1"></aop:around>
3.3切入点表达式说明
execution:匹配方法的执行(常用)
execution(表达式)
表达式语法:execution([修饰符]返回值类型包名.类名.方法名(参数))
访问修饰符可以省略
返回值可以使用 * 号,表示任意返回值
包名可以使用 * 号,表示任意包,但是有几级包,需要写几个 *
使用 . . 来表示当前包,及其子包
类名可以使用 * 号,表示任意类
方法名可以使用 * 号,表示任意方法
参数列表可以使用 * ,表示参数可以是任意数据类型,但是必须有参数
参数列表可以使用 . . 表示有无参数均可,有参数可以是任意类型
3.4Demo
applicationContext.xml
<!-- Logger为自己创建的通知类 -->
<bean id="logger" class="priv.xy.utils.Logger"></bean>
<aop:config>
<aop:pointcut id="pc1" expression="execution(* priv.xy.service.impl.*.*(..))"/>
<aop:aspect id="logAdvice" ref="logger">
<aop:before method="beforePrintLog" pointcut="execution(* priv.xy.service.impl.*.*(..))"></aop:before>
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pc1"></aop:after-returning>
<aop:after-throwing method="afterthrowingPrintLog" pointcut-ref="pc1"></aop:after-throwing>
<aop:after method="afterPrintLog" pointcut-ref="pc1"></aop:after>
<!-- 通常情况下,如下环绕通知都是独立使用的,这里只是为了方便 -->
<aop:around method="aroundPrintLog" pointcut-ref="pc1"></aop:around>
</aop:aspect>
</aop:config>
Logger.java
public class Logger {
/**
* 前置通知
*/
public void beforePrintLog(){
System.out.println("before开始任务。。。");
}
/**
* 后置通知
*/
public void afterReturningPrintLog(){
System.out.println("afterReturning开始任务。。。");
}
/**
* 异常通知
*/
public void afterthrowingPrintLog(){
System.out.println("afterthrowing开始任务。。。");
}
/**
* 最终通知
*/
public void afterPrintLog(){
System.out.println("after开始任务。。。");
}
/**
* 环绕通知
* spring框架为我们提供了一个接口:ProceedingJoinPoint,
* 它可以作为环绕通知的方法参数。
*/
public Object aroundPrintLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try {
Object[] args = pjp.getArgs();
System.out.println("around : before 开始任务。。。");
rtValue = pjp.proceed(args);
System.out.println("around : afterReturning 开始任务。。。");
return rtValue;
} catch (Throwable e) {
System.out.println("around : afterThrowing 开始任务。。。");
throw new RuntimeException(e);
} finally {
System.out.println("around : after开始任务。。。");
}
}
}
service部分省。。。
4.基于注解的AOP配置
4.1常用注解
@Aspect
作用:把当前类声明为切面类,也就是通知类
@Pointcut:指定切入点表达式
属性:value:指定表达式的内容
@EnableAspectJAutoProxy
作用:开启spring对注解AOP的支持相当于
<aop:aspectj-autoproxy/>
@Before
作用:把当前方法看成是前置通知。
属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@AfterReturning
作用:把当前方法看成是后置通知。
属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用
@AfterThrowing
作用:把当前方法看成是异常通知。
属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用
@After:把当前方法看成是最终通知。
属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用
@Around:把当前方法看成是环绕通知。
属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
4.2 Demo
appplicationContext.xml
<!-- 等同<aop:aspectj-autoproxy/> -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@Component
@Aspect
public class Logger {
@Pointcut("execution(* priv.xy.service.impl.*.*(..))")
private void pc1(){}
/**
* 前置通知
*/
// @Before("pc1()")
public void beforePrintLog(){
System.out.println("before开始任务。。。");
}
/**
* 后置通知
*/
// @AfterReturning("pc1()")
public void afterReturningPrintLog(){
System.out.println("afterReturning开始任务。。。");
}
/**
* 异常通知
*/
// @AfterThrowing("pc1()")
public void afterthrowingPrintLog(){
System.out.println("afterthrowing开始任务。。。");
}
/**
* 最终通知
*/
// @After("pc1()")
public void afterPrintLog(){
System.out.println("after开始任务。。。");
}
/**
* 环绕通知
*/
@Around("pc1()")
public Object aroundPrintLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try {
Object[] args = pjp.getArgs();
System.out.println("around : before 开始任务。。。");
rtValue = pjp.proceed(args);
System.out.println("around : afterReturning 开始任务。。。");
return rtValue;
} catch (Throwable e) {
System.out.println("around : afterThrowing 开始任务。。。");
throw new RuntimeException(e);
} finally {
System.out.println("around : after开始任务。。。");
}
}
}
5.使用建议
注解的AOP建议使用环绕通知,其他通知虽然简单但是会有顺序问题