切面日志类(内部存储通知 {增强} 的方法)
public class LogUtil {
/*
用于输出日志,想要在切入点(业务层)方法执行之前执行
*/
//前置通知
public void logStart() {
System.out.println("开始执行--前置通知");
}
//后置通知
public void logEnd() {
System.out.println("开始执行--后置通知");
}
//最终通知
public void logFinaly() {
System.out.println("开始执行--最终通知");
}
//异常通知
public void logException() {
System.out.println("开始执行--异常通知");
}
/*
* 环绕通知:
* 环绕通知类似于动态代理的全过程,并且环绕通知必须有返回值,返回值就是目标方法的返回值。
* 环绕通知可以调用其他的通知,类似于使用proxy创建动态代理时的 覆盖的incoke方法
*
* 通过spring的xml配置环绕通知 <aop:around> 后
* 程序运行时,切入点方法没有执行,而环绕通知执行了
* 原因:
* 通过之前的动态代理方式中的环绕通知,
* 发现动态代理中的环绕通知明确有调用业务层的切入点方法,而环绕通知中没有
* 解决:
* ProceedingJoinpoint:接口,proceed() 明确调用切入点方法,可以作为环绕通知的参数
* 在程序运行时,Spring框架会提供该接口的实现类给我们
*/
public Object logAround(ProceedingJoinpoint pjp) {
Object obj = null;
try {
Object[] args = pjp.getArgs(); //获取到方法执行的参数
System.out.println("前置");
//调用目标对象的切入点方法
obj = pjp.proceed(args);
System.out.println("后置");
} catch (Throwable e) {
System.out.println("异常");
e.printStackTrace();
}finally {
System.out.println("最终");
}
System.out.println("开始执行--环绕通知");
return obj;
}
}
在进行aop配置前,需要将aop的名称空间和schema约束引入xml配置文件中
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
spring-aop,xml配置的步骤:
1.把通知bean交给spring管理
2.使用aop:config 标签表明 开始aop配置
3.使用aop:config的子标签 aop:acpect标签表明开始配置切面
4.在aop:acpect中配置通知类型
xml配置:
1.首先将 被代理类,和通知bean交给Spring容器管理
<!-- 配置业务层的UserServiceImpl对象 (也就是被代理类) -->
<bean id="userService" class="com.aopBase.service.impl.UserServiceImpl"></bean>
<!-- 将通知(增强)—————切面日志类—————交给spring管理 -->
<bean id="logUtil" class="com.aopBase.utils.LogUtil"></bean>
2.开始aop配置
<!-- aop配置 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="pointcut1" expression="execution(*com.aopBase.service.impl.UserServiceImpl.*(..))"></aop:pointcut>
<!-- 开始配置切面-->
<aop:aspect id="logAdivce" ref="logUtil">
<!--配置通知 (before[前置] ,after-returning[后置] ,after-throwing[异常],after[最终]) -->
<!-- 配置前置通知 pointcut-ref:引入切入点, -->
<aop:before method="logStart" pointcut-ref="pointcut1"/>
<!-- 配置后置通知 -->
<aop:after-returning method="logEnd" pointcut-ref="pointcut1"/>
<!-- 配置异常通知 -->
<aop:after-throwing method="logException" pointcut-ref="pointcut1"/>
<!-- 配置最终通知 pointcut="execution(要增强的连接点)":切入点表达式 -->
<aop:after method="logFinaly" pointcut="execution(* com.aopBase.service.impl.UserServiceImpl.*(..))"/>
<!-- 配置环绕通知 环绕通知就是通知的整体
<aop:around method="logAround" pointcut-ref="pointcut1"/>
-->
</aop:aspect>
</aop:config>
配置通知语法:
method 属性:用于指定 切面类(LogUtil)中的那个方法作为:通知
pointcut 属性:用于指定切入点表达式,该表达式的含义代表对业务层中的哪些方法进行增强
pointcut-ref 属性:引用切入点 (pointcut和pointcut-ref 用一个就好了 )
通过pointcut-ref属性引用切入点,需要在<aop:config> 标签下配置aop切入点
<aop:pointcut id=" " expression="execution(切入点表达式)"></aop:pointcut>
切入点表达式语法: pointcut="execution(表达式)"
切入点表达式的写法:
权限修饰符 方法返回值 包名.类名.方法名(参数列表)
例如:public void com.aopBase.service.impl.UserServiceImpl.save()
原始表达式 到 全通配写法的转变
全通配写法:* *..*.*(..)
原始表达式:public void com.aopBase.service.impl.UserServiceImpl.save()
1.权限修饰符可以省略
void com.aopBase.service.impl.UserServiceImpl.save()
2.返回值有 * 替代
* com.aopBase.service.impl.UserServiceImpl.save()
3.包名可以使用通配符 . 表示包,但是有几级包就写几个 *.
* *.*.*.*.UserServiceImpl.save()
4. .. 表示当前包以及子包
* *..UserServiceImpl.save()
5.类名和方法名都可以使用通配符进行匹配
* *..*.*()
6.方法的参数可以使用..代表任意参数
* *..*.*(..)