一、需求
模拟一个账户实现类,要求,在业务层实现类之前,需要执行打印日志,即相当于公共的代码
/**
* 账户的业务层实现类
*/
public class AccountServiceImpl implements AccountService {
@Override
public void saveAccount() {
System.out.println("执行了保存");
}
@Override
public void updateAccount(int i) {
System.out.println("执行了更新"+i);
}
@Override
public int deleteAccount() {
System.out.println("执行了删除");
return 0;
}
}
/**
* 用于记录日志的工具类,它里面提供了公共的代码
*/
public class Logger {
/**
* 用于打印日志:计划让其在切入点方法执行之前执行(切入点方法就是业务层方法)
*/
public void printLog(){
System.out.println("Logger类中的pringLog方法开始记录日志了。。。");
}
}
二、基于XML的AOP配置步骤
(1)把通知 Bean 也交给spring来管理:这里就是将logger添加到spring容器中
<!-- 配置Logger类 -->
<bean id="logger" class="spring.utils.Logger"></bean>
(2)使用aop:config
标签表明开始AOP的配置
<aop:config>
<!-- 配置的代码都写在此处 -->
</aop:config>
(3)使用aop:aspect
标签表明配置切面
* id属性:是给切面提供一个唯一标识
* ref属性:是指定通知类bean的Id。
<aop:aspect id="logAdvice" ref="logger">
<!--配置切入点的表达式-->
<aop:pointcut id="pt" expression="execution(* spring.service.impl.*.*(..))"/>
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
<aop:before method="printLog" pointcut-ref="pt"></aop:before>
</aop:aspect>
(4)在使用 aop:pointcut
配置切入点表达式
aop:pointcut:
作用:用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强。
属性:
* expression:用于定义切入点表达式。
* id: 用于给切入点表达式提供一个唯一标识
<!--配置切入点的表达式-->
<aop:pointcut id="pt" expression="execution(* spring.service.impl.*.*(..))"/>
此标签写在aop:aspect标签内部只能当前切面使用。它还可以写在aop:aspect外面,此时就变成了所有切面可用
(5)使用 aop:xxx
配置对应的通知类型
我们现在示例是让printLog方法在切入点方法执行之前之前:所以是前置通知
* method:用于指定通知类中的增强方法名称
* ponitcut-ref:用于指定切入点的表达式的引用
* poinitcut:用于指定切入点表达式
作用相同:不同的表达方式:
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
<aop:before method="printLog" pointcut-ref="pt"></aop:before>
<aop:before method="printLog" pointcut="execution(* spring.service.impl.*.*(..))"></aop:before>
三、切入点表达式说明
execution:匹配方法的执行(常用)
execution(表达式)表达式语法: execution([修饰符] 返回值类型 包名.类名.方法名(参数))
写法说明:
全匹配方式:public void spring.service.impl.AccountServiceImpl.saveAccount()
(1)访问修饰符可以省略void spring.service.impl.AccountServiceImpl.saveAccount()
(2)返回值可以使用通配符,表示任意返回值* spring.service.impl.AccountServiceImpl.saveAccount()
(3)包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
* *.*.*.AccountServiceImpl.saveAccount())
(4)包名可以使用..
表示当前包及其子包
* *..AccountServiceImpl.saveAccount())
(5)类名和方法名都可以使用*
来实现通配 * *..*.*()
参数列表,可以直接写数据类型:
基本类型直接写名称 int
引用类型写包名.类名的方式 java.lang.String
可以使用通配符表示任意类型,但是必须有参数
可以使用..表示有无参数均可,有参数可以是任意类型
(6)全通配写法: * *..*.*(..)
(7)实际开发中切入点表达式的通常写法:切到业务层实现类下的所有方法
* spring.service.impl.*.*(..)