SSM 学习第三天
AOP
AOP: 面向切面编程
- 连接点(JoinPoint) 程序执行过程中的任意位置, 粒度为执行方法, 抛出异常, 设置变量等.
- 在 SpringAOP 中, 理解为方法的执行
- 切入点(PointCut) 一个切入点可以只描述一个方法, 也可以匹配多个方法
- 通知(Advice) 在切入点处执行的方法, 也就是共性功能
- 在 SpringAOP 中, 功能最终以方法的形式呈现
- 通知类: 定义通知的类
- 切面(Aspect) 描述通知与切入点的对应关系
入门案例
- 资源目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14.
├── java
│ └── com
│ └── ljguo
│ ├── App.java
│ ├── aop
│ │ └── MyAdvice.java
│ ├── config
│ │ └── SpringConfig.java
│ └── dao
│ ├── BookDao.java
│ └── impl
│ └── BookDaoImpl.java
└── resources - pom.xml 导入依赖
1
2
3
4
5
6
7
8
9
10
11<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency> - MyAdvice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package com.ljguo.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
public class MyAdvice {
private void pointcut(){}
public void method() {
System.out.println(System.currentTimeMillis());
}
}
@Aspect
注解用于将该类定义为切面@Pointcu
注解用于定义一个切入点方法, 后面需要跟上一个空方法execution(void com.ljguo.dao.impl.BookDaoImpl.update())
叫做切入点表达式, 表示 void(空返回类型), com.ljguo.dao.impl.BookDaoImpl 类中的 update 空参方法@Before
注解用于定义一个通知方法, 在切入点方法之前执行
- SpringConfig
1
2
3
4
5
6
7
8
9
10
11package com.ljguo.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
public class SpringConfig {
}@EnableAspectJAutoProxy
注解用于表示这是一个用注解开发的 AOP - BookDao
1
2
3
4
5
6package com.ljguo.dao;
public interface BookDao {
public void save();
public void update();
} - BookDaoImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.ljguo.dao.impl;
import com.ljguo.dao.BookDao;
import org.springframework.stereotype.Repository;
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save...");
}
public void update() {
System.out.println("book dao update...");
}
} - App
1
2
3
4
5
6
7
8
9
10
11
12
13package com.ljguo;
import com.ljguo.config.SpringConfig;
import com.ljguo.dao.BookDao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = ac.getBean(BookDao.class);
bookDao.update();
}
}
结果
运行 App 后得到
1 | 1672128872130 |
深入 AOP
核心本质: 代理
AOP 工作流程
- Spring 容器启动
- 读取所有切面配置中的切入点(没有配置的切入点不被读取)
- 初始化 Bean, 判断 bean 中对应的类中方法是否匹配到任意切入点
- 匹配失败, 创建对象
- 匹配成功, 创建原始对象的代理对象
使用代理对象来实现增强功能, 切片
- 获取 bean 执行方法
- 获取 bean, 调用方法, 完成操作
- 获取的 bean 是代理对象时, 根据代理对象的运行模式运行原始方法与增强内容, 完成操作
切入点表达式
- 介绍
- 切入点: 要增强的方法
- 切入点表达式: 要进行增强的方法的描述方式
- 标准格式
- 动作关键字: 描述切入点的行为动作, 如
execution
表示执行到指定切入点 - 访问修饰符, 可以省略
- 返回值
- 包名
- 类名/接口
- 方法名
- 参数
- 异常名, 可以省略
- 动作关键字: 描述切入点的行为动作, 如
- 通配符
- * : 单个独立的任意符号,如:
1
execution (public * com.ljguo.*.UserService.select* (*))
- .. : 多个连续任意符号, 如:
1
execution (public void com.ljguo.dao.UserDao.add(..))
- + : 专用于匹配子类类型
1
execution (* *..*Service+.*(..))
- * : 单个独立的任意符号,如:
AOP 通知类型
AOP 的通知类型分为 5 种类型
- 前置通知:
@Before
- 后置通知:
@After
- 环绕通知:
@Around
- 返回后通知:
@AfterReturning
- 抛出异常后通知:
@AfterThrowing
主要掌握环绕通知:
- 环绕通知需要依赖形参
ProceedingJionPoint
才能实现对原始方法的调用, 如果没使用, 将会跳过原始方法的执行 - 如果原始方法没有返回值, 那么通知方法可以设置为 void 返回值类型, 如果有, 则必须设置为 Object 类型返回值
- 可以使用形参
pjp
调用peoceed
方法, 执行原始方法并返回值 - 由于无法预知原始方法是否有异常, 所以环绕通知方法必须抛异常
环绕通知详解
1 |
|
pjp.getSignature
获取到一个Signature
对象, 可以通过该对象得到一些原始方法的属性, 例如1
2
3
4signature.getClass();
signature.getDeclaringType();
signature.getDeclaringTypeName();
signature.getName();