目录标题
Spring AOP
AOP:[Aspect Oriented Programming] 面向切面编程。
我们很容易想到OOP, AOP是OOP 的延伸,我们从如下角度来思考:
①. OOP 从 “各司其职”的角度为划分"对象",每个对象都有特定的功能, 比如:
- StudentService 对象包含如下的方法
- add 方法
- 核心业务逻辑, 做数据的添加操作
- 非核心业务逻辑, 日志、权限审核、事务、异常处理
- update 方法
- 核心业务逻辑, 做数据的更新操作
- 非核心业务逻辑, 日志、权限审核、事务、异常处理
- findAll 方法
- 核心业务逻辑, 做数据的查询操作
- 非核心业务逻辑, 日志、权限审核、事务、异常处理
②. AOP 是基于OOP的基础之上,进一步去“切割”非核心业务逻辑,把非核心逻辑当做一个切面,可以按需注入到目标对象中。
AOP的核心概念
切面[aspect]
只是一个统称,它相当于是 Advice + PointCut 的综合体,也就是匹配规则 + 增强代码 合在一起才叫 一个 切面。
如何去开发一个切面?
写一个类如下:@Aspect //表明我是一个“切面” @Component //切面 也要是一个 JavaBean, 需要被IOC容器管理 public class LogAdvice { //定义你自己需要添加的增强代码 @PointCut(value="execution(.... .... .... ....)") public void init() { //... //... } @Before(value="init()") public void beforeLog() { //.... } }
代码增强[advice]
它是切面的代码载体,也就是我们要书写的代码,如:日志、事务、异常处理等 ,也就是 一个又一个类型,一般叫 xxxAdvice、xxxAspect, 如: LogAdvice.java
切入点[PointCut]
它是一组规则【一般采用正则表达式】,它用来配置 Advice, 在符合我们定义的规则的情况下,注入到目标业务对象的方法中。【它可以定位到 目标对象的目标方法中】 其语法为:
@PointCut(value=“execution(pubilc * 包名.类名.方法名(…))”)
注:一个 PointCut 有多个连接点【5个】
连接点[JoinPoint]
当代码增强匹配到目标方法后,我们的增强代码的执行时机 , 这个执行时机有:
- 在目标方法执行之前, before => @Before
- 在目标方法执行之后,after => @After
- 在目录方法执行过程中招聘了异常, afterThrowing => @AfterThrowing
- 在目录方法返回之后【相当于finally块】执行, afterReturing => @AfterReturning
- 环绕 [aroud],也就是上面4种的综合体, aroud => @Around
织入[Weaving]
它是表达织入的过程,把增强代码注入到目标方法中的过程叫织入, 它是一个动作。
AOP的开发步骤
- 写一个 切面类,打上两个注解: @Aspect 和 @Component 注解
package com.hc.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/***********************
* 日志代码增强
*/
@Aspect
@Component
public class LogAdvice {
//定义了匹配目标对象的规则【正则表达式】
@Pointcut("execution(public * com.hc.service.impl.StudentServiceImpl.*(..))")
public void aop_ruler(){
//nothing!
}
//定义在目标方法执行之前要记的日志
@Before(value = "aop_ruler()") //在调用PointCut规则匹配成功后的目标方法之前调用此方法
public void pre_target(JoinPoint jp) {
//通过 JointPoint 来获取目标对象、目标方法
String targetClass = jp.getTarget().getClass().getSimpleName();
String mName = jp.getSignature().getName(); //方法名
Object[] args = jp.getArgs(); //获取参数
//
System.out.printf("--before: 调用[%s]对象的[%s]方法\n",targetClass,mName);
if(args != null) {
//说明方法有参数
System.out.println("\t --before: 参数值有(");
for(Object arg : args) {
System.out.printf("\t\t%s ",arg);
}
System.out.println(")");
}
}
@After("execution(public * com.hc.service.impl.StudentServiceImpl.findAll(..))")
public void after_target(JoinPoint jp) {
System.out.printf("--after: 【%s】执行了\n",jp.getSignature().getName());
}
//目标方法出现异常时才会调用此方法
@AfterThrowing(value="aop_ruler()",throwing = "e")
public void after_throwing(JoinPoint jp, Throwable e) {
System.out.println("--afterThrowing,表示目标方法出异常了:");
System.out.println("--afterThrowing,"+e);
}
/******
* 这个方法是相当于目标已经返回后【1.正常返回,2.异常返回】,再执行
* @param jp
* @param result
*/
@AfterReturning(value = "aop_ruler()",returning = "result")
public void after_return(JoinPoint jp, Object result) {
System.out.println("--afterReturning: 目标方法返回后执行此方法,结果是:"+result);
}
}
- 在pom.xml中,加入 spring-aspects 的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
- 在 AppConfig.java中,添加打开AOP的注解开头
@Configuration //表示这是一个spring 注解配置类
@ComponentScan({"com.hc.service.impl","com.hc.dao.impl"
,"com.hc.advice"})
@PropertySource(value = "classpath:db.properties") //表示读取指定的属性文件,
// 并存放在Environment对象中
@EnableAspectJAutoProxy //启用 AOP 机制
public class AppConfig {
}
- 定义一个方法,使用 @Before, @After, @AfterThrowing, @AfterReturning, @Around注解来定义PointCut
- 单元测试
AOP相关的注解
- @EnableAspectJAutoProxy
- @ Aspect
- @Component
- @PointCut
- @Before
- @After
- @AfterThrowing
- @AfterReturning
- @Around
AOP相关的API
- JoinPoint 连接点
- Signature 方法签名
- ProceedingJoinPoint 环绕连接点
AOP 的原理
动态代理, Spring支持两种方式
- 基于JDK自带的代理生成机制,它要求目标对象要有实现的接口,否则,不能生成代理对象
- 基于第三方类库 CGLIB 来生成代理,它不要求目标对象要实现接口。
Note:
欢迎点赞,留言,转载请在文章页面明显位置给出原文链接
知者,感谢您在茫茫人海中阅读了我的文章
没有个性 哪来的签名!
详情请关注点我
持续更新中
© 2020 09 - Guyu.com | 【版权所有 侵权必究】 |