目录
前言
AOP在spring中又叫“面向切面编程”,它可以说是对传统我们面向对象编程的一个补充,从字面上顾名思义就可以知道,它的主要操作对象就是“切面”,所以我们就可以简单的理解它是贯穿于方法之中,在方法执行前、执行时、执行后、返回值后、异常后要执行的操作。相当于是将我们原本一条线执行的程序在中间切开加入了一些其他操作一样。
利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP术语
AOP术语 | 解释 |
---|---|
横切关注点 | 从每个方法中抽取出来的同一类非核心业务 |
切面(Aspect) | 封装横切关注点信息的类 |
通知(Advice) | 切面必须完成的各个具体工作(类中的方法) |
目标(Target) | 被通知的对象 |
代理(Proxy) | 向目标的对象应用通知之后创建的代理对象 |
连接点(Joinpoint) | 横切关注点在程序代码中的具体体现,对应程序执行的某个特定位置 |
切入点(Pointcut) | 执行或找到连接点的一些方式 |
对于连接点和切入点可能不好理解,详细对比下
连接点:
- 连接点是一个应用执行过程中能够插入一个切面的点。
- 连接点可以是调用方法时、抛出异常时、甚至修改字段时、
- 切面代码可以利用这些点插入到应用的正规流程中
- 程序执行过程中能够应用通知的所有点。
切入点:
- 如果通知定义了“什么”和“何时”。那么切入点就定义了“何处”。切入点会匹配通知所要织入的一个或者多个连接点。
- 通常使用明确的类或者方法来指定这些切入点。
- 定义通知被应用的位置(在哪些连接点)
切面:
- 切面是通知和切入点的集合,通知和切入点共同定义了切面的全部功能——它是什么,在何时何处完成其功能。
五种通知类型
- Before——在方法调用之前调用通知
- After——在方法完成之后调用通知,无论方法执行成功与否
- After-returning——在方法执行成功之后调用通知
- After-throwing——在方法抛出异常后进行通知
- Around——通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
这五种通知后还可以跟切入点表达式,来指定哪一个切面方法在哪一个方法执行时触发,切入点表达式的语法格式规范是:
execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名] ([参数列表]))
其中在表达式中有两个常用的特殊符号:
- “ * ”代表所有的意思,星号还可以表示任意的数值类型
- “…”表示任意类型,或任意路径下的文件,
AOP接口的实现
引入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
通过Spring自定义类实现AOP
创建一个测试类
package com.mine.aop;
public class Demo {
public void testDemo1() {
System.out.println("测试用例1");
}
public void testDemo2() {
System.out.println("测试用例2");
}
public void testDemo3() {
System.out.println("测试用例3");
}
}
创建通知类,自定义三个通知方法
package com.mine.aop;
import org.aspectj.lang.ProceedingJoinPoint;
public class Aspect {
public void before(){
System.out.println("=====>前置通知");
}
public void after(){
System.out.println("=====>后置通知");
}
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("============>环绕通知:前");
pjp.proceed();
System.out.println("============>环绕通知:后");
}
}
Spring配置
<?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">
<!-- 将切入点所在的类与通知装配为受Spring管理的Bean-->
<bean id="demo" class="com.mine.aop.Demo"/>
<bean id="aspect" class="com.mine.aop.Aspect"/>
<!--配置切面-->
<aop:config>
<!--配置切面,通知+切入点-->
<aop:aspect ref="aspect">
<!--method:通知方法-->
<!--pointcut:切入点-->
<!--前置通知-->
<aop:before method="before" pointcut="execution(public void com.mine.aop.Demo.testDemo1())"/>
<aop:after method="after" pointcut="execution(public void com.mine.aop.Demo.testDemo2())"/>
<aop:around method="around" pointcut="execution(* *..*.testDemo3())"/>
<!--表达式可以通过通配符匹配切入点,上面那句话表示:任意返回类型(*) 任意包层级(*..)下的任意类(*.)的方法testDemo3-->
</aop:aspect>
</aop:config>
</beans>
最后编写测试类
import com.mine.aop.Demo;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDemo {
@Test
public void test1(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("Spring.xml");
Demo demo = (Demo)ac.getBean("demo");
demo.testDemo1();
System.err.println("—————————————————分割线—————————————————");
demo.testDemo2();
System.err.println("—————————————————分割线—————————————————");
demo.testDemo3();
}
}
文件层级
运行结果