之前练习了前置通知
1.创建前置通知类
package aop;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* User: 晨风
* <p>
* Author: 晨风
* Date: 22:59
*/
// 自定义自己录业务方法名称的前置通知 前置通知:目标方法执行之前先执行的额外操作
public class MyBeforeAdvice implements MethodBeforeAdvice {
// before 参数 1.当前执行方法对象 2.当前执行方法的参数 3.目标对象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("当前执行方法:" + method.getName());
System.out.println("当前执行方法参数:" + args[0]);
System.out.println("目标对象:" + target);
}
}
2.配置文件中,组装切面
<!-- 前置通知-->
<!-- 注册通知-->
<bean class="com.pcf.aop.MyBeforeAdvice" id="myBeforeAdvice"></bean>
<!--组装切面-->
<aop:config>
<!-- 配置切入点pointcut
id:切入点在工厂的唯一标识
expression:用来制定切入项目中那些组件中哪些方法
execution(返回值 包.类名.*(..))
-->
<aop:pointcut id="pc" expression="execution(* aop.EmpServiceImpl.*(..))"/>
<!-- 配置切面
advice-ref:工厂中通知id pointcut-ref:工厂切入点唯一标识
-->
<aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="pc"/>
</aop:config>
可见实现了MethodBeforeAdvice接口,在before重写方法中定义自己在执行业务功能之前要实现的额外操作
环绕通知
1.创建环绕通知类
package com.pcf.advices;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.util.Date;
/**
* User: 晨风
* <p>
* @Author: 晨风
* @Date: 22:58
*
* 自定义环绕通知用来记录目标方法的执行时长
*/
public class MethodInvokeTimeAdvice implements MethodInterceptor {
/**
* Interceptor 就是相当于拦截器
* @param invocation 获取当前执行方法 获取当前执行方法参数 获取目标对象 放行目标方法
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("==========进入环绕通知==========");
try {
long startTime = System.currentTimeMillis();
// 放行目标方法
Object proceed = invocation.proceed();
long endTime = System.currentTimeMillis();
long processTime = endTime - startTime;
System.out.println("方法 :" + invocation.getMethod().getName() + "执行了 [" + processTime + "]ms !");
System.out.println("==========离开环绕通知==========");
return proceed;
} catch (Exception e) {
e.printStackTrace();
System.out.println("环绕通知--异常通知");
}
return null;
}
}
2.配置文件中,组装切面
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<bean class="com.pcf.dao.DeptDAOImpl" id="deptDAO"></bean>
<bean class="com.pcf.service.DeptServiceImpl" id="deptService">
<property name="deptDAO" ref="deptDAO"></property>
</bean>
<!-- 注册通知-->
<!-- 前置通知-->
<bean class="com.pcf.aop.MyBeforeAdvice" id="beforeAdvice"></bean>
<bean class="com.pcf.advices.MethodInvokeTimeAdvice" id="invokeTimeAdvice"></bean>
<!-- 组装切面-->
<aop:config>
<!-- 配置切入点pointcut
id:切入点在工厂的唯一标识
expression:用来制定切入项目中那些组件中哪些方法
execution(返回值 包.类名.*(..))
-->
<!-- 配置切面-->
<aop:pointcut id="invokeTimePc" expression="execution(* com.pcf.service.*ServiceImpl.*(..))"></aop:pointcut>
<!-- 组装切面-->
<aop:advisor advice-ref="invokeTimeAdvice" pointcut-ref="invokeTimePc"></aop:advisor>
</aop:config>
</beans>
在这里我们看到,环绕通知和前置通知不一样之处在于,创建环绕通知类时实现的是MethodInterceptor接口,而重写的方法invoke。回顾一下之前学习的动态代理的代码实现
@Override
/**
* @Param proxy Object
* 当前创建好的代理对象
* @Param method Method
* 当前代理对象执行的方法对象
* @Param args Object[]
* 当前代理对象执行的方法的参数
*
* @Note 通过动态代理对象调用自己里面代理方法时会优先执行InvocationHandle类中的invoke
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 调用目标类中业务方法前的操作
System.out.println("==============InvocationHandle类中的invoke=================");
System.out.println("当前执行的方法: " + method.getName());
System.out.println("当前执行的方法的参数: " + args[0]);
// 调用目标类中业务方法通过反射机制 调用目标类中当前方法
Object invoke = method.invoke(new UserServiceImpl(), args);
// 调用目标类中业务方法前的操作
...
return invoke;
}
没错,几乎一模一样。环绕通知类实现的MethodInterceptor接口中重写的invoke方法,正是我们之前学习动态代理时的invoke方法。当时我们还做出了注释
/**
* @Note 通过动态代理对象调用自己里面代理方法时会优先执行InvocationHandle类中的invoke
*/
所有的通知,即附加功能都是在InvocationHandle类中的invoke方法中实现的。
只是,我们之前学习动态代理时,并不知道除了执行目标方法之外的语句都叫做通知。
在环绕通知类中,有这样一行注释: // 放行目标方法 这是为什么呢?
在前置通知中,框架完全可以通过执行顺序,去先执行前置类,再执行目标方法。
但是在环绕通知中,前置通知和后置通知都存在的时候,前置通知要在执行目标方法前执行,后置通知要在执行目标方法后执行。所以,在invoke中,会先拦截目标方法的执行,当你前置通知执行完了,通过invocation.proceed()放行目标方法后,目标方法才会执行,再之后执行后置方法。
从接口名称中也可以看出来,MethodInterceptor是继承了Interceptor的,因此这个接口本质上也是一个拦截器。
**
至于环绕通知实现了MethodInterceptor接口和InvocationHandle接口的关系,目前咱这水平暂时探究不到。这里先记个待办!