AOP: 面向切面编程
配置文件的头信息
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
AOP的核心组件:
切面(Aspect):切面是封装通用业务逻辑的组件,可以作用到其他组件上。
切入点(Pointcut): 用于指定哪些组件哪方法使用切面组件,Spring提供表达式来实现该制定。
通知(Advice):用于指定组件作用到目标组件的具体位置。
AOP前置通知:在目标组件的方法执行前执行的程序。
切入点程序:
代码语言:javascript复制package model;
public class Chengxu {
public void talk(String str){
System.out.println("str=" str);
}
}
切面程序:
代码语言:javascript复制package model;
import org.aspectj.lang.JoinPoint;
public class Charu {
public void log(JoinPoint jp){
System.out.println("日志的方法");
System.out.println("获取目标函数参数方式:" jp.getArgs()[0]);
//得到切入点程序中方法的参数
System.out.println("获取目标对象:" jp.getTarget().getClass().getName());
//得到切入点程的 包名.类名
System.out.println("获取目标函数的java反射对象:" jp.getSignature());
// 得到切入点程的 返回类型 包名.类名.方法名()
System.out.println("执行前置通知");
}
}
Applicationcontext.xml省略文件头信息
代码语言:javascript复制<!--切入点程序 -->
<bean id="chengxu" class="model.Chengxu"></bean>
<!--切面程序 -->
<bean id="charu" class="model.Charu"></bean>
<!-- AOP配置跟标签 -->
<aop:config>
<!-- 切入点配置:expression切入点表达式
* model....不限返回类型,如果方法没返回值也得加*
(*)方法里面是什么参数类型都可以,如果方法没有参数,不加这个*
-->
<aop:pointcut expression="execution(* model.Chengxu.talk(*))" id="chengxuPointCut"/>
<!-- 切面配置 -->
<!-- aspect切面程序依赖于谁 -->
<aop:aspect ref="charu">
<!-- 前置通知 -->
<!--切面程序的方法 切入点依赖于谁 (简单的说,把这个方法前置在哪个方法前面) -->
<aop:before method="log" pointcut-ref="chengxuPointCut"/>
</aop:aspect>
</aop:config>
AOP后置通知:在目标组件的方法正常执行并返回参数后执行的程序。
切入点程序:
代码语言:javascript复制package model;
public class Chengxu {
//此方法也可以没有返回值
public String talk(String str){
System.out.println("str=" str);
return "保存成功";
}
}
切面程序:
代码语言:javascript复制package model;
import org.aspectj.lang.JoinPoint;
public class Charu {
public void log(JoinPoint jp,Object ret){
//如果切入点方法没返回值,那么这个Object ret省略不写
System.out.println("后置");
System.out.println("obj为目标组件方法返回值:" ret);
}
}
Applicationcontext.xml
代码语言:javascript复制<!--切入点程序 -->
<bean id="chengxu" class="model.Chengxu"></bean>
<!--切面程序 -->
<bean id="charu2" class="model.Charu2"></bean>
<!-- AOP配置跟标签 -->
<aop:config>
<!-- 切入点配置:expression切入点表达式
* model....不限返回类型
(*)方法里面是什么参数类型都可以,方法没有参数,那就不加这个*
-->
<aop:pointcut expression="execution(* model.Chengxu.talk(*))" id="chengxuPointCut"/>
<!-- 切面配置 -->
<!-- aspect切面程序依赖于谁 -->
<aop:aspect ref="charu2">
<!-- 前置通知 -->
<!--切面程序的方法 切入点依赖于谁 (简单的说,把这个方法前置在哪个方法前面) -->
<aop:after-returning method="log2" pointcut-ref="chengxuPointCut"
returning="ret"/>
<!--如果切入点程序没有返回值那么returning 就可以不写 -->
</aop:aspect>
</aop:config>
异常通知:在目标组件的方法抛出异常信息后执行的程序。
注:当前置通知和后置通知发生异常时,这个异常通知是不执行的,只有当目标组件发生异常时才执行。
切面程序:
代码语言:javascript复制package model;
import org.aspectj.lang.JoinPoint;
public class Charu3 {
public void log3(JoinPoint jp,Throwable t){
System.out.println("异常通知");
System.out.println("t为目标组件发生异常的信息:" t);
}
}
切入点程序:
代码语言:javascript复制package model;
public class Chengxu {
//有返回值的方法
public String talk(String str){
System.out.println("str=" str);
System.out.println(10/0);//发生异常的地方
return "保存成功";
}
}
Application.xml
代码语言:javascript复制<!--切入点程序 -->
<bean id="chengxu" class="model.Chengxu"></bean>
<!--切面程序 前置-->
<bean id="charu" class="model.Charu"></bean>
<!--切面程序2 后置-->
<bean id="charu2" class="model.Charu2"></bean>
<!--切面程序3 异常-->
<bean id="charu3" class="model.Charu3"></bean>
<!-- AOP配置跟标签 -->
<aop:config>
<!-- 切入点配置:expression切入点表达式 * model....不限返回类型 (*)方法里面是什么参数类型都可以,方法没有参数也可以那就不加这个*
-->
<aop:pointcut expression="execution(* model.Chengxu.talk(*))" id="chengxuPointCut"/>
<!-- 切面配置 -->
<!-- aspect切面程序依赖于谁 -->
<aop:aspect ref="charu">
<!-- 前置通知 -->
<!--切面程序的方法 切入点依赖于谁 (简单的说,把这个方法前置在哪个方法前面) -->
<aop:before method="log" pointcut-ref="chengxuPointCut"/>
</aop:aspect>
<aop:aspect ref="charu2">
<!-- 前置通知 -->
<!--切面程序的方法 切入点依赖于谁 (简单的说,把这个方法前置在哪个方法前面) -->
<aop:after-returning method="log2" pointcut-ref="chengxuPointCut"
returning="ret"/>
</aop:aspect>
<aop:aspect ref="charu3">
<!-- 异常通知 -->
<aop:after-throwing method="log3" pointcut-ref="chengxuPointCut"
throwing="t"/>
</aop:aspect>
</aop:config>
最终通知:在目标组件的方法正常执行后执行,或在异常通知之前执行。
注:也就是当目标组件发生没发生异常,都会被执行。
如果目标组件异常时,那后置通知不会执行,异常通知会执行。最终通知会在异常通知之前执行。
如果目标组件正常时,那最终通知会在后置通知之前执行。
Application.xml
代码语言:javascript复制<!-- 最终通知 -->
<aop:after method="log4" pointcut-ref="chengxuPointCut"/>
</aop:aspect>
环绕通知: 切面程序负责调用目标组件的运行,与struts中的拦截器功能类似,可以完全取代之前的几个通知。
切面程序:在类型为环绕通知的切面程序函数中,参数为org.aspectj.lang.ProceedingJoinPoint是JoinPoint的子类,扩展了JoinPoint类,提供了proceed()函数,该函数的作用是调用目标组件,并返回目标组件返回的值。
Application.xml
代码语言:javascript复制<aop:aspect ref="charu5">
<!-- 环绕通知 -->
<aop:around method="log5" pointcut-ref="chengxuPointCut"/>
</aop:aspect>
切面程序:
代码语言:javascript复制public class Charu5 {
public void log5(ProceedingJoinPoint pjp) {
System.out.println("前置通知");
try {
Object ob = pjp.proceed();//调用目标组件
System.out.println("目标函数的返回值:" ob);
System.out.println("后置通知");
} catch (Throwable e) {
System.out.println("异常通知");
}
System.out.println("最终通知");
}
}
注释方式实现以上内容
XML中增加
<context:component-scan base-package="com.china.model"></context:component-scan>
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
@Aspect:代表切面程序
在切面类下添加下面代码实现各自的功能
前置通知:@Before(value="execution(* com.china.model.Chengxu.talk(*))")
后置通知:@AfterReturning(value="execution(*com.china.model.Chengxu.talk(*))",returning="ret")
异常通知:@AfterThrowing(value="execution(* com.china.model.Chengxu.talk(*))", throwing="e")
最终通知:@After(value="execution(* com.china.model.Chengxu.talk(*))")
环绕通知:@Around(value="execution(* com.china.model.Chengxu.talk(*))")