AOP (Aspect Oriented Programming) 即面向切面编程
作用: 可以在不修改原有代码的基础上,对目标方法进行扩展增强。
-
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
特点
-
非侵入式编程: 在不修改源码的情况下对已有方法进行增强
-
提高代码复用: 增强的内容抽象成方法或者对象可重复使用
-
统一管理维护: 抽象成独立的方法或对象方便后期维护管理
原理
-
Spring AOP 实现的原理是动态代理技术
-
底层支持两种动态代理
-
当目标实现接口时采用JDK动态代理
-
当目标没有实现接口采用Cglib动态代理(可配置统一使用Cglib)
-
优势:
•可重用性
•开发效率更高
•可维护性
•可扩展性
XML实现AOP配置
1.配置依赖
<!--aspectj: 第三方面向切面的组件包,spring整合aspectj实现AOP
aop需要导入2个jar包: spring-aop(spring-context已包含,不需要单独导入) 、 aspectjweaver
-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
2.编写记录日志的切面类:LogAspect
public class LogAspect {
//编写通知:增强日志功能
public void insertLog(){
System.out.println("写入日志...");
}
3.bean.xml 中完成Aop配置
实现步骤:
1.创建切面类LogAspect对象加入IOC容器
2.进行aop配置,将通知(增强的代码)给到切入点(增强的位置)去增强
-->
<!--1.创建切面类LogAspect对象加入IOC容器-->
<bean class="com.itheima.aop.LogAspect" id="logAspect"></bean>
<!--2.进行aop配置,将通知(增强的代码)给到切入点(增强的位置)去增强-->
<aop:config>
<!--2.1 配置切入点表达式(定义增强的位置,配置增强目标对象save()和update())-->
<aop:pointcut id="pt" expression="execution(* save()) || execution(* update())"/>
<!--2.2 配置切面类=通知(增强的代码)+切入点规则-->
<aop:aspect ref="logAspect">
<!--2.2.1 将通知给到指定的切入点去增强-->
<aop:before method="insertLog" pointcut-ref="pt"></aop:before>
</aop:aspect>
</aop:config>
切入点表达式
-
作用:定义增强代码的位置
-
语法
? 零或1
execution(
modifiers-pattern? 方法的访问修饰符(可选)
ret-type-pattern 方法返回值类型(必填)
declaring-type-pattern?name-pattern(param-pattern) 包名类名(可选)方法名称(参数)
throws-pattern? 方法的异常(可选)
)应用
expression="execution(* save(..))"/>
含义: 当前项目中所有的save方法全部增强
切入点函数:
切入点表达式--通配符:
切入点表达式--常用格式:
1. execution(* com.itheima.service.impl.*.*(..)) 精确包的业务的所有实现类所有方法
2. execution(* com.itheima.service.impl.*ServiceImpl.*(..)) 精确包的业务的所有实现类(类结尾有要求)所有方法
3. execution(* com..service.impl.*.*(..)) 精确首尾包业务的所有实现类所有方法,适合不同的业务模块在不同包
通知类型:
XML中配置标签:
通知--实现步骤:
1.修改切面类 LogAspect,添加通知方法
public class LogAspect {
//前置通知方法
public void before(){
System.out.println("前置通知...写入日志");
}//后置通知方法
public void afterReturning(){
System.out.println("后置通知...写入日志");
}//异常通知方法
public void afterThrowing(){
System.out.println("异常通知...写入日志");
}//最终通知方法
public void after(){
System.out.println("最终通知...写入日志");
}
}
2.Aop配置
<bean class="com.itheima.aop.LogAspect" id="logAspect"></bean>
<aop:config>
<!--配置切入点表达式-->
<aop:pointcut id="pt" expression="execution(* com.itheima.service.impl.*.*(..))"/>
<aop:aspect ref="logAspect">
<!--
配置各种通知类型
位置:
try{
【前置通知】
//目标方法执行
【后置通知】
}catch(Exception e){
【异常通知】
}finally{
【最终通知】
}
-->
<!--前置通知-->
<aop:before method="before" pointcut-ref="pt"></aop:before>
<!--后置通知-->
<aop:after-returning method="afterReturning" pointcut-ref="pt"></aop:after-returning>
<!--异常通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"></aop:after-throwing>
<!--最终通知-->
<aop:after method="after" pointcut-ref="pt"></aop:after><!--注意:不要将<aop:after>放到<aop:after-returning>前面,否则执行的顺序受影响-->
</aop:aspect>
</aop:config>
环境通知介绍:
环绕通知的功能
1. 可以环绕目标方法来执行
2. 可以获取目标方法的各种信息
3. 可以修改目标方法的参数和返回值
环绕通知和其他通知的区别
1. 可以决定是否调用目标方法
2. 可以控制方法返回的对象值
Spring框架提供了ProceedingJoinPoint接口,作为环绕通知的参数。在环绕通知执行的时候,Spring框架会提供接口的对象,我们直接使用即可。
环绕通知---实现步骤:
1.切面类LogAspect,添加环绕通知方法
public class LogAspect {
//环绕通知=上面4个通知
//优势
// 1.操作目标方法是否执行
// 2.获取目标方法信息,例如:方法的参数、名字
// 3.可以修改目标方法使用的参数与返回值
public Object around(ProceedingJoinPoint joinPoint){Object result = null;
try {
//环绕前置通知输出
System.out.println("[环绕前置通知...写入日志]");//获取目标方法的参数数组
Object[] args = joinPoint.getArgs();//将参数值“播仔”修改为“播妞”
args[0]="播妞";//获取目标方法的名字
String name = joinPoint.getSignature().getName();
System.out.println("目标对象方法名字:"+name);//执行目标方法
//result = joinPoint.proceed(); //按照目标方法原来参数执行
result = joinPoint.proceed(args); //按照新的参数数组执行目标方法//环绕后置通知输出
System.out.println("[环绕后置通知...写入日志]");
} catch (Throwable throwable) {
throwable.printStackTrace();//环绕异常通知输出
System.out.println("[环绕异常通知...写入日志]");
} finally {//环绕最终通知输出
System.out.println("[环绕最终通知...写入日志]");
}return result;
}
}
2.bean.xml 配置环绕通知
<bean class="com.itheima.aop.LogAspect" id="logAspect"></bean>
<aop:config>
<!--配置切入点表达式-->
<aop:pointcut id="pt" expression="execution(* com.itheima.service.impl.*.*(..))"/>
<aop:aspect ref="logAspect">
<!--环绕通知-->
<aop:around method="around" pointcut-ref="pt"></aop:around>
</aop:aspect>
</aop:config>