文章目录
一、Spring中的AOP的相关术语
1.JointPoint(连接点)和PointCut(切入点)
1.1 JointPoint(连接点)
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
1.2 PointCut(切入点)
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
1.3 二者区别
2.Advice(通知/增强):
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
3.Introduction(引介)
引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。
4.Target(目标对象)
代理的目标对象。即是被代理对象
5.Weaving(织入)
指把增强应用到目标对象来创建新的代理对象的过程。
spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
- 采用动态代理前的service无法实现事务的支持,加入动态代理,到返回代理对象的加入事务支持这个过程称为织入。
6.Proxy(代理)
- 一个类被AOP织入增强后,就产生一个结果代理类。就是代理对象
7.Aspect(切面)
是切入点和通知(引介)的结合。
二、基于XML的AOP配置
1.pom.xml
<dependencies>
<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>
</dependencies>
2.必要的代码
2.1业务层接口和实现类
public interface IAccountService {
/**
* 模拟保存账户
*/
void saveAccount();
/**
* 模拟更新账户
* @param i
*/
void updateAccount(int i);
/**
* 删除账户
* @return
*/
int deleteAccount();
}
public class AccountService implements IAccountService {
public void saveAccount() {
System.out.println("执行了保存");
}
public void updateAccount(int i) {
System.out.println("执行了更新"+i);
}
public int deleteAccount() {
System.out.println("执行了删除");
return 0;
}
}
2.2工具类:提供日志类
public class Logger {
/**
* 用于打印日志:计划让其在切入点方法执行之前执行(切入点方法就是业务层方法)
*/
public void printLog(){
System.out.println("Logger类中的pringLog方法开始记录日志了.......");
}
}
3.bean.xml
Spring的任何代码都依赖于IOC
<!--先配置service层方法-->
<bean id="accountService" class="com.xpt.service.impl.AccountServiceImpl"></bean>
<bean id="logger" class="com.xpt.utils.Logger"></bean>
<!--配置AOP-->
<aop:config>
<!--配置切面-->
<aop:aspect id="logAdvice" ref="logger">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
<aop:before method="printLog" pointcut="execution( public void com.xpt.service.impl.AccountServiceImpl.saveAccount())"></aop:before>
</aop:aspect>
</aop:config>
4.测试类
上面完成了AOP的最基本的配置,下面开始测试
public static void main(String[] args) {
//.1获取IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.获取业务层对象
IAccountService as = ac.getBean("accountService",IAccountService.class);
//3.测试方法
as.saveAccount();
System.out.println("-----------");
as.deleteAccount();
}
4.1结果
5. 切入点表达式的写法
上面完成了一次简单的AOP的配置过程,下面详细的说明切入点表达式的写法
5.1各种写法
5.2开发中建议写法
6.通知类型
6.1增加日志类方法
public class Logger {
/**
* 前置通知
*/
public void beforePrintLog(){
System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。");
}
/**
* 后置通知
*/
public void afterReturningPrintLog(){
System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。");
}
/**
* 异常通知
*/
public void afterThrowingPrintLog(){
System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。");
}
/**
* 最终通知
*/
public void afterPrintLog(){
System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。");
}
}
6.2增强配置信息
<!--配置AOP-->
<aop:config>
<!--配置切面-->
<aop:aspect id="logAdvice" ref="logger">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
<!-- 配置前置通知:在切入点方法执行之前执行-->
<aop:before method="beforePrintLog" pointcut="execution( * com.xpt.service.impl.*.*(..))"></aop:before>
<!-- 配置后置通知:在切入点方法正常执行之后值。它和异常通知永远只能执行一个-->
<aop:after-returning method="afterPrintLog" pointcut="execution( * com.xpt.service.impl.*.*(..))"></aop:after-returning>
<!--<!– 配置异常通知:在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个-->
<aop:after-throwing method="afterThrowingPrintLog" pointcut="execution( * com.xpt.service.impl.*.*(..))"></aop:after-throwing>
<!--<!– 配置最终通知:无论切入点方法是否正常执行它都会在其后面执行-->
<aop:after method="afterPrintLog" pointcut="execution( * com.xpt.service.impl.*.*(..))"></aop:after>
</aop:aspect>
</aop:config>
6.3测试效果
6.4 产生异常,查看效果
- 注意点
7 通用化切入点表达式
- 前面的切入点配置中有大量的重复配置
- 通用化切入点就是来解决这个问题的
- 效果
8.环绕通知
8.1 日志类定义方法
8.2 bean.xml配置
<aop:config>
<aop:pointcut id="pt1" expression="execution( * com.xpt.service.impl.*.*(..))"></aop:pointcut>
<!--配置切面-->
<aop:aspect id="logAdvice" ref="logger">
<aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
</aop:aspect>
</aop:config>
8.3效果
8.4小结
- Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
- 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
三、基于注解的AOP配置
1.bean.xml
2.注解部分
3.环绕通知的注解部分
四、基于xml配置的AOP实现银行转账案例中的事务控制
1.bean.xml中配置AOP
<!--配置AOP Config-->
<!--配置AOP config类-->
<!--配置事务管理器-->
<bean id="txManager" class="com.xpt.utils.TransactionManager">
<!--注入connectionUtils-->
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<aop:config>
<aop:pointcut id="pt1" expression="execution(* com.xpt.service.impl.*.*(..))"></aop:pointcut>
<aop:aspect id="transaction" ref="txManager">
<!--在执行数据库操作之前 应该开启事务-->
<aop:before method="beginTransaction" pointcut-ref="pt1"></aop:before>
<!--完成数据库操作后 应该提交事务-->
<aop:after-returning method="commit" pointcut-ref="pt1"></aop:after-returning>
<!--如果出现异常 应该进行回滚-->
<aop:after-throwing method="rollback" pointcut-ref="pt1"></aop:after-throwing>
<!--正常提交后 应该进行资源的释放-->
<aop:after method="release" pointcut-ref="pt1"></aop:after>
</aop:aspect>
</aop:config>
2.事务控制类添加代码,方便观察
3.测试类修改
4.测试效果
5.小结
- 基于AOP来进行事务控制,只需要在配置中进行相关配置和写好相关的事务控制代码
- 不需要自己实现动态代理过程
6.环绕通知实现上述
public Object aroundControl(ProceedingJoinPoint pjp){
Object rtValue = null;
try {
//得到方法的执行参数
Object[] args = pjp.getArgs();
//开启业务层方法之前 开启事务
beginTransaction();
//调用业务层方法
rtValue = pjp.proceed(args);
//完成业务方法后 提交事务
commit();
return rtValue;
} catch (Throwable throwable) {
//出现异常 回滚事务
rollback();
throwable.printStackTrace();
throw new RuntimeException(throwable);
}finally {
//释放资源
release();
}
}
五、基于注解配置的AOP实现银行转账案例中的事务控制
1.bean.xml
2.业务层添加注解
3.持久层添加注解
4.事务管理层添加注解
注意只使用环绕通知
5.效果
- 可以实现转账需求,并实现事务控制。