文章目录
1. Spring AOP
1.1 AOP 概念
Aspect Oriented Programming 面向切面编程,是一种利用“横切”的技术(底层实现就是动态代理),对原有的业务逻辑进行拦截,并且可以在这个拦截的横切面上添加特定的业务逻辑,对原有 的业务进行增强。
基于动态代理实现在不改变原有业务的情况下对业务逻辑进行增强
在Spring框架中AOP的一个典型实现是 声明式事务
AOP底层基于 动态代理
:动态代理分为JDK动态代理和CGLIB动态代理,默认SpringAOP使用的是JDK的动态代理
1.2 Spring AOP框架部署
1.2.1 创建Maven项目
略
1.2.2 添加依赖
在pom.xml文件中添加
- context
- aspects
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.16.RELEASE</version>
</dependency>
</dependencies>
1.2.3 创建spring配置文件
需要引入aop的命名空间
applicationContext.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">
</beans>
1.3 AOP配置—基于XML
在DAO的方法添加开启事务和提交事务的逻辑
AOP开发步骤 :
- 创建切面类,在切面类定义切点方法
- 将切面类配置给Spring容器
- 声明切入点
- 配置AOP的通知策略
1.3.1 创建一个类,定义要添加的业务逻辑 (切面类)
public class TxManager {
public void begin(){
System.out.println("-----------开启事务");
}
public void commit(){
System.out.println("-----------提交事务");
}
}
1.3.2 配置aop
术语 | 说明 |
---|---|
Aspect | 切面:在SpringAOP中,切面就是一个普通的类,里面封装的就是一些需要增强的代码(功能) |
Join Point | 连接点:程序执行过程中的一点,SpringAOP中呢,连接点通常代表一个方法的运行 |
Advice | 通知:指的就是拦截到具体的Join Point之后要做什么,什么时候做,理解为具体增强了什么 |
Pointcut | 切点:配合切点表达式来定位在哪些方法和类上执行 |
Weaving | 织入:指的就是将通知( Advice )应用到 目标对象 (Target Object)上,形成 代理对象 (AOP proxy)的过程 |
applicationContext.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">
<bean id="bookDAO" class="com.cg.dao.BookDAOImpl"></bean>
<bean id="studentDAO" class="com.cg.dao.StudentDAOImpl"></bean>
<!---->
<bean id="txManager" class="com.cg.utils.TxManager"></bean>
<aop:config>
<!--声明切入点-->
<aop:pointcut id="book_all" expression="execution(* com.cg.dao.*.*
(..))"/>
<!--声明txManager为切面类-->
<aop:aspect ref="txManager">
<!--通知策略-->
<aop:before method="begin" pointcut-ref="book_all"/>
<aop:after method="commit" pointcut-ref="book_all"/>
</aop:aspect>
</aop:config>
</beans>
1.4 切入点的声明
- 使用aop:pointcut标签声明切入点:切入点可以是一个方法
- aop:pointcut的属性expression="execution(类型 包.类.方法())"指向切点
- 类型:表示返回值为对应的类型的方法,* 代表全部类型
- 包:表示是对应包下面的切点
- 类:表示是对应类下面的切点
- 方法():表示是对应具体的的方法切点,* 代表该类的所有方法
- 方法():表示无参数的方法
- 方法(. .):表示方法(无论有没有参数都符合)
1.4.1 各种切入点声明方式
<!--使用aop:pointcut标签声明切入点:切入点可以是一个方法-->
<!--BookDAOImpl类中的insert方法-->
<aop:pointcut id="book_insert" expression="execution(* com.cg.dao.BookDAOImpl.insert())"/>
<!--BookDAOImpl类中所有无参数无返回值的方法-->
<aop:pointcut id="book_pc1" expression="execution(void com.cg.dao.BookDAOImpl.*())"/>
<!--BookDAOImpl类中所有无返回值的方法-->
<aop:pointcut id="book_pc2" expression="execution(void com.cg.dao.BookDAOImpl.*(..))"/>
<!--BookDAOImpl类中所有无参数的方法-->
<aop:pointcut id="book_pc3" expression="execution(* com.cg.dao.BookDAOImpl.*())"/>
<!--BookDAOImpl类中所有方法-->
<aop:pointcut id="book_pc4" expression="execution(* com.cg.dao.BookDAOImpl.*(..))"/>
<!--dao包中所有类中的所有方法-->
<aop:pointcut id="pc5" expression="execution(* com.cg.dao.*.*(..))"/>
<!--dao包中所有类中的insert方法-->
<aop:pointcut id="pc6" expression="execution(* com.cg.dao.*.insert(..))"/>
<!--dao包中所有类中的insert方法-->
<aop:pointcut id="pc7" expression="execution(* *(..))"/>
1.4.2 AOP使用注意事项
- 如果要使用Spring aop面向切面编程,调用切入点方法的对象必须通过Spring容器获取
- 如果一个类中的方法被声明为切入点并且织入了切点之后,通过Spring容器获取该类对象,实则获取到的是一个代理对象
- 如果一个类中的方法没有被声明为切入点,通过Spring容器获取的就是这个类真实创建的对象
//BookServiceImpl bookService = new BookServiceImpl();
BookServiceImpl bookService = (BookServiceImpl)
context.getBean("bookServiceImpl");
bookService.addBook();
1.5 AOP通知策略
AOP通知策略:就是声明将切面类中的切点方法如何织入到切入点
- before 前置通知,切入到指定切入点之前
- after 后置通知,切入到指定切入点之后
- after-throwing 异常通知,切入点抛出异常之后
- after-returning 方法返回值返回之后
对于一个Java方法而言return返回值也是方法的一部分
因此“方法返回值返回之后”和“方法执行结束”是同一个时间点,随意after 和 afterreturning根据配置的顺序决定执行顺序 - around 环绕通知
环绕通知的切点方法,必须准守如下的定义规则:- 必须带有一个ProceedingJoinPoint类型的参数
- 必须有Object类型的返回值
- 在前后增强的业务逻辑之间执行Object v = point.proceed();
- 方法最后返回v
1.5.1 定义切面类
public class MyAspect {
public void method1(){
System.out.println("~~~~~~~method1");
}
public void method2(){
System.out.println("~~~~~~~method2");
}
public void method3(){
System.out.println("~~~~~~~method3");
}
public void method4(){
System.out.println("~~~~~~~method4");
}
//环绕通知的切点方法,必须准守如下的定义规则:
//1.必须带有一个ProceedingJoinPoint类型的参数
//2.必须有Object类型的返回值
//3.在前后增强的业务逻辑之间执行Object v = point.proceed();
//4.方法最后返回v
public Object method5(ProceedingJoinPoint point) throws Throwable {
System.out.println("~~~~~~~method5---before");
//此代码的执行,就表示切入点方法的执行
Object v = point.proceed();
System.out.println("~~~~~~~method5---after");
return v;
}
}
1.5.2 配置切面类
<bean id="myAspect" class="com.cg.utils.MyAspect"></bean>
<aop:config>
<!--使用aop:pointcut标签声明切入点:切入点可以是一个方法-->
<aop:pointcut id="book_insert" expression="execution(* com.cg.dao.BookDAOImpl.insert())"/>
<aop:aspect ref="myAspect">
<!--aop:before 前置通知,切入到指定切入点之前-->
<aop:before method="method1" pointcut-ref="book_insert"/>
<!--aop:after 后置通知,切入到指定切入点之后-->
<aop:after method="method2" pointcut-ref="book_insert"/>
<!--aop:after-throwing 异常通知,切入点抛出异常之后-->
<aop:after-throwing method="method3" pointcut-ref="book_insert"/>
<!--aop:after-returning 方法返回值返回之后,对于一个Java方法而言return返回值也是方法的一部分
因此“方法返回值返回之后”和“方法执行结束”是同一个时间点,随意after 和 afterreturning根据配置的顺序决定执行顺序-->
<aop:after-returning method="method4" pointcut-ref="book_insert"/>
<aop:around method="method5" pointcut-ref="book_insert"/>
</aop:aspect>
</aop:config>