Spring(二):Spring AOP 理解与应用
AOP
:面向切面编程,是一种对OOP
思想再增强的编程思想。它是使用动态代理的技术对一些公共的代码进行统一管理,在需要增强时,使用动态代理对业务方法进行增强。
AOP 适合那些具有横切逻辑的应用场合,如性能监控,访问控制,事务管理及日志记录,通过横向抽取机制为这类无法通过纵向继承体系进行抽象的重复性代码提供解决方案。
AOP 将分散在各个业务逻辑代码中的相同代码通过横向切割的方式抽取到一个独立的模块中。
AOP中的术语
- 连接点:业务层接口中的业务核心方法就是连接点。
它就是公共代码和业务主线之间的纽带。 - 切入点:指实际被增强过的连接点。
切入点一定是连接点。连接点不一定是切入点。 - 通知:指提供了增强代码的类。(提供了公共代码的类)
通知中的方法分不同的类型:- 前置通知:以业务核心方法(切入点方法)为定位点。在切入点方法之前执行的就是前置通知。
- 后置通知:以业务核心方法(切入点方法)为定位点。在切入点方法之后执行的就是后置通知。
- 例外通知/异常通知:以业务核心方法(切入点方法)为定位点。写在catch里面的就是异常通知。
- 最终通知:以业务核心方法(切入点方法)为定位点。写在finally里面的就是最终通知。
- 环绕通知:整个invoke方法叫做环绕通知。
- 切面:指通知所关心的某个方面。比如:事务是一个切面。日志又是另外一个切面。
- 织入:业务核心方法(切入点方法)被增强的那一瞬间叫做织入。
基于XML的AOP配置
- 搭建SpringAOP的开发环境
- 步骤:
- 导入必备的jar包
- spring 的所有组件运行都需要 IoC 的支持。
- 必须先导入 IoC 的 jar 包。
- 再导入 aop 的 jar 包。aop 一共有4个jar包。
- 创建spring的核心配置文件
- 在类的根路径下创建一个 bean.xml 文件
- 导入 schema 约束
注意:不仅要导入 IoC 的约束,还要导入 aop 的约束。 - 把资源都交给 spring 来管理
- 导入必备的jar包
- aop 配置
aop:config
标签是用于开始 aop 的配置aop:aspect
标签指定使用的 bean。- 属性:
id
:指定一个唯一标识符ref
:指定通知类 bean 的 id
- 属性:
aop:before
标签:用于指定通知的类型是前置通知。- 属性:
method
:指定通知类中哪个方法用于增强pointcut
:指定切入点表达式pointcut-ref
:引用通用切入点表达式
- 属性:
aop:pointcut
标签:用于定义通用的切入点表达式- 属性:
id
:指定表达式的唯一标识符expression
:指定表达式
- 属性:
- 步骤:
- 通知的类型:
- 前置通知:
aop:berfore
- 后置通知:
aop:after-returning
- 异常通知:
aop:after-throwing
- 最终通知:
aop:after
- 环绕通知:
aop:around
Spring 框架为提供的一种可以在代码中手动控制通知执行时间点的方式。
- 前置通知:
切入点表达式
XML 配置 AOP 需要配置 pointcut 属性指定 切入点,注解式配置需要配置 execution 来指定切入点,切入点可以通过一个表达式来指定。
切入点表达式:指定在业务层实现类中哪个方法上织入增强的代码。切入点表达式的关键字:execution
(表达式)
表达式写法
1
(访问修饰符 返回值 包名.包名....类名.方法名(参数列表))
精准匹配
1
(public void com.itheima.service.impl.CustomerServiceImpl.saveCustomer())
访问修饰符可以省略
1
(void com.itheima.service.impl.CustomerServiceImpl.saveCustomer())
返回值可以使用星号代替,表示任意返回值
1
(* com.itheima.service.impl.CustomerServiceImpl.saveCustomer())
包名可以使用星号来代替,表示任意包名称。但是不能表示所有,也就是说有几级包,就需要写几个星号
1
(* *.*.*.*.CustomerServiceImpl.saveCustomer())
包名也可以使用两个点来代替,表示当前包及其子包
1
(* com..CustomerServiceImpl.saveCustomer())
类名可以使用星号号代替,表示任意类名称
1
(* com..*.saveCustomer())
方法名称可以使用星号号代替,表示任意方法
1
(* com..*.*())
参数列表可以使用号代替,表示任意数据类型,但是有一个号就对应一个参数
1
(com..*.*(*))
参数类别可以使用两个点代替,表示有无参数均可。有参数的话,参数可以是任意数据类型
1
(com..*.*(..))
全通配
1
(* *..*.*(..))
实际开发中:一般都切到业务层实现类所在的包,使用部分匹配的方式。如下示例:
例如:execution(* com.itheima.service.impl.*.*(..))
基于注解的AOP配置
- 步骤:
- 导入必备的 6 个 ioc 的和 4 个 aop 的jar包
- 在类的根路径下创建一个 bean.xml 的配置文件
- 导入 ioc / aop 和 context 三个约束
- 把资源都用注解的方式让 Spring 来管理
- 使用注解来配置切面
注解:@Aspect
,指定当前类是一个切面类 - 使用
@Before
注解来配置前置通知 - 使用注解 AOP,必须在 XML 配置文件中开启切面代理
<aop:aspectj-autoproxy/>
- AOP 常用注解
- @Aspect:指定当前类是一个切面
- @Before:指定当前方法前置通知,属性 value用于指定切入点表达式 | 切入点表达式引用
- @AfterReturning:指定当前方法后置通知,属性 value用于指定切入点表达式 | 切入点表达式引用
- @AfterThrowing:指定当前方法异常通知,属性 value用于指定切入点表达式 | 切入点表达式引用
- @After:指定当前方法是最终通知,属性 value用于指定切入点表达式 | 切入点表达式引用
- @Around:指定当前方法是环绕通知,属性 value用于指定切入点表达式 | 切入点表达式引用
Spring之XML配置AOP示例
创建bean.xml文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<beans xmlns="http://www.springframework.org/schema/beans";
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
xmlns:p="http://www.springframework.org/schema/p"
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";>
<!-- 把资源交给spring管理 ,创建spring容器时,
把Dao对象存放spring容器 UserDaoImpl userDao = new UserDaoImpl(); -->
<bean id="customerService" class="com.service.impl.CustomerImpl"></bean>
<!-- 把通知类交给spring来管理 -->
<bean id="logger" class="com.log.Logger"></bean>
<!-- aop配置 -->
<aop:config>
<!-- aop:pointcut 定义通用切入点表达工,后面切面可用 -->
<aop:pointcut expression="execution(* com.service.impl.*.*(..))"
id="pt1" />
<!-- 配置切面 -->
<aop:aspect id="logAdvice" ref="logger">
<aop:before method="printLog" pointcut-ref="pt1" />
</aop:aspect>
</aop:config>
</beans>创建日志工具类
1
2
3
4
5public class Logger {
public void printLog() {
System.out.println("Logger开始记录日 志。。。");
}
}创建 Service 接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14public interface ICustomerService {
/**
* 保存用户
*/
void saveCustomer();
/**
* 更新用户
*/
void updateCustomer(int a);
/**
* 删除用户
*/
void deleteCustomer();
}创建Service接口实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class CustomerImpl implements ICustomerService {
public void saveCustomer() {
System.out.println("执行了保存客户");
}
public void updateCustomer(int a) {
System.out.println("执行了更新客户");
}
public void deleteCustomer() {
System.out.println("执行了删除除客户");
}
}主函数调用Service方法,并实现日志记录
1
2
3
4
5
6
7
8
9
10public class Client {
public static void main(String[] args) {
// 获得容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
ICustomerService cs = (ICustomerService) ac.getBean("customerService");
cs.saveCustomer();
cs.updateCustomer(10);
cs.deleteCustomer();
}
}
Spring(二):Spring AOP 理解与应用