3.2 开发环绕通知
需要用到的DeptDAO接口
public interface DeptDAO {
// 增
void save(String name);
// 改
void update(String name);
// 删
void delete(Integer id);
// 查
String find(String name);
}
实现了DeptDAO接口的DeptDAOImpl类
public class DeptDAOImpl implements DeptDAO{
@Override
public void save(String name) {
System.out.println("save DAO :" + name);
}
@Override
public void update(String name) {
System.out.println("update DAO :" + name);
}
@Override
public void delete(Integer id) {
System.out.println("delete DAO :" + id);
}
@Override
public String find(String name) {
System.out.println("find DAO :" + name);
return name;
}
}
需要用到的DeptService接口
public interface DeptService {
void save(String name);
void update(String name);
void delete(Integer id);
String find(String name);
}
实现了DeptService接口的实现类DeptServiceImpl
// 业务层组件
public class DeptServiceImpl implements DeptService{
// 需要DAO组件
private DeptDAO deptDAO;
public void setDeptDAO(DeptDAO deptDAO) {
this.deptDAO = deptDAO;
}
@Override
public void save(String name) {
System.out.println("处理save业务逻辑");
deptDAO.save(name);
}
@Override
public void update(String name) {
System.out.println("处理update业务逻辑");
deptDAO.update(name);
}
@Override
public void delete(Integer id) {
System.out.println("处理delete业务逻辑");
deptDAO.delete(id);
}
@Override
public String find(String name) {
System.out.println("处理find业务逻辑");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return deptDAO.find(name);
}
}
自定义的环绕通知类
// 自定义环绕通知用来记录目标方法的执行时长
// 自定义环绕通知必须实现MethodInterceptor接口,这样才能让系统直到它是环绕通知
// MethodInterceptor接口用org.aopalliance.intercept.MethodInterceptor包下的
public class MethodInvokeTimeAdvice implements MethodInterceptor {
@Override
// 参数:methodInvocation 获取当前执行的方法 获取当前执行方法的参数 获取目标对象 放行目标方法的执行
// 因为在目标方法之前执行的操作执行完之后需要放行让目标方法去执行
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("========进入环绕通知============");
System.out.println("当前执行方法:" + methodInvocation.getMethod().getName());
// methodInvocation.getArguments()是取参数数组,后面的[0]代表取第一个参数
System.out.println("方法的参数:" + methodInvocation.getArguments()[0]);
System.out.println("获取当前的目标对象:" + methodInvocation.getThis());
long start = new Date().getTime();
// 放行目标方法
Object proceed = methodInvocation.proceed();// 继续处理(执行目标方法)
long end = new Date().getTime();
System.out.println("方法:" + methodInvocation.getMethod().getName() + ",本次执行了 [" + (end - start) + "] ms!");
return proceed;
}
}
配置文件
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--管理DAO组件-->
<bean class="com.baizhi.dao.DeptDAOImpl" id="deptDAO"></bean>
<!--管理Service组件-->
<bean class="com.baizhi.service.DeptServiceImpl" id="deptService">
<!--property中name写成员变量的名字-->
<property name="deptDAO" ref="deptDAO"/>
</bean>
<!--注册通知类-->
<bean class="com.baizhi.advices.MethodInvokeTimeAdvice" id="methodInvokeTimeAdvice"></bean>
<!--配置切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pc" expression="execution(* com.baizhi.service.*ServiceImpl.*(..))"/>
<!--组装切面-->
<!--advice-ref:用哪个通知 pointcut-ref:这个通知用于哪个组件-->
<aop:advisor advice-ref="methodInvokeTimeAdvice" pointcut-ref="pc"/>
</aop:config>
</beans>
测试和结果
环绕通知是最强大的,环绕通知还可以设置使其具有前置通知、后置通知、异常通知的作用
3.3 切入点表达式
# spring (pointcut)切入点表达式
作用:主要是用来决定项目中哪些组件中哪些方法需要加入通知
expression="切入点表达式"
1. execution 切入点表达式 --->方法级别的切入点表达式 控制粒度: 方法级别 效率低
完整语法:
1. execution(访问权限修饰符 返回值 包名.类名.方法名(参数类型))
因为接口内的方法一般是公开方法,所以权限修饰符一般都省略了
2. execution(返回值 包名.类名.方法名(参数类型))
* 任意多个字符
1). execution(* com.baizhi.service.*.*(..)) [使用比较多]
包: com.baizhi.service
类: 任意类
方法: 任意方法
参数: 参数任意
返回值: 任意返回类型
2). execution(String com.baizhi.service.*ServiceImpl.*(..))
包: com.baizhi.service
类: 以ServiceImpl结尾的类
方法: 方法名任意
参数: 参数任意
返回值: 返回值必须是String类型
3). execution(String com.baizhi.service.*Service*.*(String))
包: com.baizhi.service
类: 类名中包含Service关键字的类
方法: 任意
参数: 参数只有一个类型必须是String
返回值: 返回值必须是String
4). execution(* com.baizhi.service..*.*(String)) [使用比较多]
包: com.baizhi.service及子包(含子包的子包...)
类: 任意类
方法: 任意方法
参数: 任意参数
返回值: 任意类型
5). execution(* com.baizhi.service..*ServiceImpl.*(..)) [使用比较多]
包: com.baizhi.service包
类: 以ServiceImpl结尾的类
方法: 任意方法
参数: 任意参数
返回值: 任意类型
6). execution(* *.*(..))
第二个*把包和类一起包含了
包: 项目中所有包
类: 项目中所有类
方法: 所有方法
参数: 所有参数
返回值: 任意类型
注意:尽可能精确切入 避免不必要的切入
2. within 切入点表达式 ---->类级别的切入点表达式 控制粒度:类级别 效率高
expression="within()"
完整语法:
1. within(包.类名)
1). within(com.baizhi.service.*ServiceImpl)
3.4 开发后置通知
需要用到的ClazzService接口
public interface ClazzService {
void save(String name);
String find(String name);
}
实现了ClazzService接口的实现类ClazzServiceImpl
package afterthrows;
public class ClazzServiceImpl implements ClazzService{
@Override
public void save(String name) {
System.out.println("Service save调用" + name);
}
@Override
public String find(String name) {
System.out.println("Service find调用" + name);
return name;
}
}
自定义后置通知,需要实现系统定义的AfterReturningAdvice接口
// 后置通知
public class MyAfterAdvice implements AfterReturningAdvice {
@Override
// 参数1:目标方法返回值 参数2:当前执行的方法 参数3:方法参数 参数4:目标对象
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("=======进入后置通知======");
System.out.println("返回值:" + o);
System.out.println("方法名:" + method.getName());
System.out.println("方法参数:" + objects[0]);
System.out.println("目标对象" + o1);
}
}
配置文件
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--管理Service组件-->
<bean class="afterthrows.ClazzServiceImpl" id="clazzService"></bean>
<!--注册通知-->
<bean class="afterthrows.MyAfterAdvice" id="myAfterAdvice"></bean>
<!--配置切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pc" expression="within(afterthrows.*ServiceImpl)"/>
<!--组装切面-->
<aop:advisor advice-ref="myAfterAdvice" pointcut-ref="pc"/>
</aop:config>
</beans>
测试和结果
3.5 开发异常通知
需要用到的ClazzDAO接口
public interface ClazzDAO {
void save(String name);
}
实现了ClazzDAO接口的实现类ClazzDAOImpl
public class ClazzDAOImpl implements ClazzDAO{
@Override
public void save(String name) {
throw new RuntimeException("save 方法调用出错了");
}
}
自定义后置通知,实现系统定义的后置通知的接口ThrowsAdvice
public class MyThrowAdvice implements ThrowsAdvice {
// 出现异常时的处理
//出现异常时执行通知处理
// 参数1:方法 参数2:参数 参数3:目标对象 参数4:异常
public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
System.out.println("===========进入异常通知============");
System.out.println("方法名: "+method.getName());
System.out.println("方法的参数: " + args[0]);
System.out.println("目标对象: " +target);
System.out.println("异常信息: "+ex.getMessage());
}
}
- 实现ThrowsAdvice接口不需要实现任何方法,但是我们要定义方法以应对出现异常时的处理(具体可看官方文档)
配置文件
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--管理Service组件-->
<bean class="afterthrows.ClazzServiceImpl" id="clazzService"></bean>
<!--注册通知-->
<bean class="afterthrows.MyAfterAdvice" id="myAfterAdvice"></bean>
<!--配置切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pc" expression="within(afterthrows.*ServiceImpl)"/>
<!--组装切面-->
<aop:advisor advice-ref="myAfterAdvice" pointcut-ref="pc"/>
</aop:config>
<bean class="afterthrows.ClazzDAOImpl" id="clazzDAO"></bean>
<bean class="afterthrows.MyThrowAdvice" id="myThrowAdvice"></bean>
<aop:config>
<aop:pointcut id="pcx" expression="within(afterthrows.ClazzDAOImpl)"/>
<aop:advisor advice-ref="myThrowAdvice" pointcut-ref="pcx"/>
</aop:config>
</beans>
测试和结果