AOP:全称是 Aspect Oriented Programming 即:面向切面编程。
简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。
如何使用Spring中的AOP来实现动态代理,简化代码
Step1:文件导入坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
这里的aspectjweaver为后面的切入点表达式做准备
Step2:配置相关的bean.xml
<?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,Service中需要用到Dao-->
<bean id="IUserService" class="com.imis.service.impl.UserServiceImpl">
<property name="iUserDao" ref="IUserDao"></property>
</bean>
<!--配置Dao,Dao中又继承BaseDao,BaseDao中需要用到QueryRunner-->
<bean id="IUserDao" class="com.imis.dao.impl.UserDaoImpl">
<property name="queryRunner" ref="runner"></property>
</bean>
<!-- <context:component-scan base-package="com.imis"></context:component-scan>-->
<!--QueryRunner配置,注入数据源,由于QueryRunner中没有关于数据源的set方法,但提供了构造函数,所以用构造函数的方法来注入-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/firstmybatis?serverTimezone=Asia/Shanghai"/>
<property name="user" value="root"/>
<property name="password" value="zhengyunyu524"/>
</bean>
<!-- 把log放进ioc容器中 -->
<bean id="logger" class="com.imis.log.Log">
</bean>
<!--配置aop-->
<aop:config>
<!-- 配置切面指定类 -->
<aop:aspect id="logAdvice" ref="logger">
<!-- 配置切面指定类的方法及要切入的类的方法 -->
<!-- 前置 -->
<aop:before method="before" pointcut="execution(public * com.imis.service.impl.UserServiceImpl.queryAll())"></aop:before>
<!-- 后置 -->
<aop:after-returning method="after" pointcut="execution(public * com.imis.service.impl.UserServiceImpl.queryAll())"></aop:after-returning>
<!-- 异常 -->
<aop:after-throwing method="afterexception" pointcut="execution(public * com.imis.service.impl.UserServiceImpl.queryAll())"></aop:after-throwing>
<!-- 最终 -->
<aop:after method="end" pointcut="execution(public * com.imis.service.impl.UserServiceImpl.queryAll())"></aop:after>
</aop:aspect>
</aop:config>
</beans>
spring中基于XML的AOP配置步骤
1、把通知Bean也交给spring来管理
2、使用aop:config标签表明开始AOP的配置
3、使用aop:aspect标签表明配置切面
id属性:是给切面提供一个唯一标识
ref属性:是指定通知类bean的Id。
4、在aop:aspect标签的内部使用对应标签来配置通知的类型
aop:before:表示配置前置通知
aop:after-returning:后置通知
aop:after-throwing:异常通知
aop:after:最终通知
method属性:用于指定Logger类中哪个方法是前置通知
pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
切入点表达式的写法:
关键字:execution(表达式)
表达式:访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
标准的表达式写法:
public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
示例Log代码:
public class Log {
public void before(){
System.out.println("执行前");
}
public void after(){
System.out.println("执行后");
}
public void afterexception(){
System.out.println("有异常");
}
public void end(){
System.out.println("最终结束");
}
}
此时在配置AOP基础上我们运行queryAll方法,可以得到以下结果
执行前
[User{
username='zzy', userid=1}, User{
username='yxx', userid=2}, User{
username='ss', userid=3}]
执行后
最终结束
切入点表达式的写法:
关键字:execution(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
标准的表达式写法:
public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以省略
void com.itheima.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配符,表示任意返回值
* com.itheima.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
* *.*.*.*.AccountServiceImpl.saveAccount())
包名可以使用..表示当前包及其子包
* *..AccountServiceImpl.saveAccount()
类名和方法名都可以使用*来实现通配
* *..*.*()
参数列表:
可以直接写数据类型:
基本类型直接写名称 int
引用类型写包名.类名的方式 java.lang.String
可以使用通配符表示任意类型,但是必须有参数
可以使用..表示有无参数均可,有参数可以是任意类型
全通配写法:
* *..*.*(..)
实际开发中切入点表达式的通常写法:
切到业务层实现类下的所有方法
* com.itheima.service.impl.*.*(..)
通用切入点表达式
<aop:pointcut id="query" expression="execution(public * com.imis.service.impl.UserServiceImpl.queryAll())"/>
<!-- 配置切面指定类的方法及要切入的类的方法 -->
<!-- 前置 -->
<aop:before method="before" pointcut-ref="query"></aop:before>