03-AOP
execute 英 /ˈeksɪkjuːt/ vt. 执行(法令);执行;实施;(尤指依法)处决,处死;实行;制作,做成(艺术品);成功地完成(技巧或动作)
execution 英 /ˌeksɪˈkjuːʃn/ n.(尤指遗嘱的)执行;执行;实施;处决;实行;(艺术品的)制作;表演;(乐曲的)演奏
- AOP(Aspect_Oriented_Programming)面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率
- AOP在不修改代码的情况下,对程序功能进行拓展,往往用于日志处理、权限控制、性能检测、事务控制等
- 实现原理 => 动态代理
- 有接口,JDK动态代理
- 没有接口,Cglib动态代理
1. 重要概念
- 连接点(Joint_point)
- 类里面那些可以被增强的方法
- 在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它Joint_point
- 切入点(Pointcut)
- 实际被增强的方法
- 表示一组Joint_point,这些Joint_point或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,定义了相应的Advice将要发生的地方
- 通知(Advice)
- 实际增强的逻辑部分(增加的功能)
- 定义了在Pointcut具体要做的操作
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
- 目标对象(Target)
- 被增强功能的对象(被代理对象)
- 切面(Aspect)
- 表现为功能相关的一些advice方法放在一起声明成的一个Java类
- Aspect声明类似于Java中的类声明,在Aspect中会包含着一些Pointcut以及相应的Advice
- 织入(Weaving)
- 创建代理对象、实现增强功能的声明并运行的过程
2. pom.xml
- AspectJ本身并不是Spring框架组成部分,是一个独立的AOP框架,一般把AspectJ和Spring框架的AOP依赖一起使用,要导入一个独立的依赖
<!-- spring切面包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.5</version>
</dependency>
<!-- 织入包 spring-aspects 已经导入该包,这里可以不导入,第三方的jar -->
<!--<dependency>-->
<!-- <groupId>org.aspectj</groupId>-->
<!-- <artifactId>aspectjweaver</artifactId>-->
<!-- <version>1.9.6</version>-->
<!--</dependency>-->
<!-- AOP联盟包 -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
3. Pointcut
- 通过一个表达式来确定AOP要增强的哪些方法
execution([权限修饰符][返回值类型][类的全路径名][方法名](参数列表))
// 返回值类型,(..)任意形参
execution(* com.listao.spring.aop.dao.IAop.add(..))) // IAop.add()
execution(* com.listao.spring.aop.dao.IAop.*(..))) // IAop接口下所有方法
execution(* com.listao.spring.dao.*.*(..)) // dao包下所有类所有方法
execution(* com.listao.spring.dao.*.add(..)) // dao包下所有类的add()
execution(* com.listao.spring.dao.*.add*(..)) // dao包下所有类的add开头方法
// 定义一个公共切点,切点表达式直接指向接口
@Pointcut("execution(* com.listao.spring.aop.dao.IAop.toEnhance(..))")
public void pointCut() {
}
@Before("pointCut()")
4. Advice
// 1. 前置通知:切点方法前执行
@Before
// 2. 后置通知(最终通知),finally{}
@After
// 3. 返回通知,try{}中return后执行
@AfterReturning
// 4. 异常通知,catch{}
@AfterThrowing
// 5. 环绕通知,切点之前和之后增强
@Around
// 切面排序,Order越大越先被代理,越小越先被执行
@Order(2)
@Aspect
@Component
@Order(2)
public class AopAspect {
// 定义一个公共切点,切点表达式直接指向接口
@Pointcut("execution(* com.listao.spring.aop.dao.IAop.toEnhance(..))")
public void pointCut() {
}
/**
* 1. 前置通知:切点方法前执行的功能
* 2. JoinPoint代表add(..)切点对象
* 3. jp.getArgs()入参
*/
@Before("execution(* com.listao.spring.aop.dao.IAop.toEnhance(..))")
public void before(JoinPoint jp) {
// 实参
Object[] args = jp.getArgs();
System.out.println("==> before, args = " + Arrays.asList(args));
}
/**
* 1. 后置通知(最终通知),finally{}
* 2. 异常也执行
*/
@After("pointCut()")
public void after(JoinPoint jp) {
System.out.println("==> after");
}
/**
* 1. 返回通知,try{}
* 2. return之后增强,异常不执行
*
* @param jp jp
* @param res res 返回值
*/
// @AfterReturning(value = "pointCut()", returning = "res")
@AfterReturning(pointcut = "pointCut()", returning = "res")
public void afterReturning(JoinPoint jp, Object res) {
System.out.println("==> afterReturning 返回值: " + res);
}
/*
* 1. 异常通知,catch{}
* 2. 接收切点方法抛出的异常对象
*/
// @AfterThrowing(value = "pointCut()", throwing = "e")
@AfterThrowing(pointcut = "pointCut()", throwing = "e")
public void afterThrowing(Exception e) {
System.out.println("==> afterThrowing 异常: " + e.getMessage());
}
/*
* 1. 环绕通知,切点之前和之后增强
* 2. ProceedingJoinPoint代表切点,手动控制切点执行
* 3. return必须为Object,并继续向上返回
*/
@Around(value = "pointCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("==> around_before");
// 切点方法执行
Object res = pjp.proceed();
System.out.println("==> around_after");
return res;
}
}
@Aspect
@Component
@Order(1) // Order越大越先被代理,越小越先被执行
public class AopAspect_order {
@Before("execution(* com.listao.spring.aop.dao.IAop.toEnhance(..))")
public void before(JoinPoint jp) {
System.out.println("==> @Order(1)");
}
}
1. JoinPoint
- JoinPoint对象封装了Aop中切面方法的信息。在切面方法中添加JoinPoint形参,就可以获取到JoinPoint对象
方法名 | 功能 |
---|---|
Signature signature = jp.getSignature(); | 署名信息对象,对象中有目标方法名,所属类Class |
Object[] args = jp.getArgs(); | 入参 |
Object target = jp.getTarget(); | 被代理对象 |
Object o = jp.getThis(); | 代理对象 |
2. ProceedingJoinPoint
- JoinPoint子接口,只在
@Around
切面方法形参中
方法名 | 功能 |
---|---|
public Object proceed() throws Throwable; | 执行目标方法 |
public Object proceed(Object[] args) throws Throwable; | 传入新的参数去执行目标方法 |
3. xml + 注解
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" ...>
<!-- 注解扫描 -->
<context:component-scan base-package="com.listao.spring.aop"/>
<!-- AOP切面编程自动生成代理对象 -->
<aop:aspectj-autoproxy/>
</beans>
@Repository
public class AopImpl implements IAop {
@Override
public int toEnhance(String name, Integer age) {
System.out.println(StrUtil.format("=========>>>>>>>> AopImpl.toEnhance({})...", name));
// int i = 1 / 0;
return age;
}
}
public class T3_AOP {
/**
* 1. 注释@Aspect,得到AopImpl对象
* 2. @Aspect,得到$Proxy24代理对象
*/
@Test
public void aopXml1() {
ApplicationContext ac = new ClassPathXmlApplicationContext("x6_aop.xml");
IAop bean = ac.getBean(IAop.class);
bean.toEnhance("ooxx", 18);
// $Proxy24 => Jdk_proxy
System.out.println("getSimpleName() = " + bean.getClass().getSimpleName());
}
@Test
public void aopConfig() {
ApplicationContext ac = new AnnotationConfigApplicationContext(Cfg2_AOP.class);
IAop bean = ac.getBean(IAop.class);
bean.toEnhance("ooxx", 18);
// $Proxy24
System.out.println("getSimpleName() = " + bean.getClass().getSimpleName());
}
@Test
public void aopXml2() {
ApplicationContext ac = new ClassPathXmlApplicationContext("x7_aop2.xml");
IAop bean = ac.getBean(IAop.class);
bean.toEnhance("ooxx", 18);
System.out.println("getSimpleName() = " + bean.getClass().getSimpleName());
}
}
4. 完全注解
@Configuration
@ComponentScan("com.listao.spring.aop")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class Cfg2_AOP {
}
5. 完全xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" ...>
<!-- 目标对象 -->
<bean id="aopImpl" class="com.listao.spring.aop.dao.impl.AopImpl"/>
<!-- 切面对象 -->
<bean id="daoAspect" class="com.listao.spring.aop.aspect.AopAspect"/>
<!-- 配置AOP增强功能 -->
<aop:config>
<!-- 配置切点 -->
<aop:pointcut id="pointCut" expression="execution(* com.listao.spring.aop.dao.IAop.toEnhance(..))"/>
<!-- 配置切面 -->
<aop:aspect ref="daoAspect">
<aop:before method="before" pointcut-ref="pointCut"/>
<aop:after method="after" pointcut-ref="pointCut"/>
<aop:around method="around" pointcut-ref="pointCut"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointCut" returning="res"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointCut" throwing="e"/>
</aop:aspect>
</aop:config>
</beans>