前言:
学习内容来自于黑马的SSM学习。从零学习SSM,目前是spring内容。这内容主要是 Spring中的AOP。
黑马程序员SSM框架教程_Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术_哔哩哔哩_bilibili
目标:
- 理解并且掌握AOP的概念
- 掌握AOP使用流程
- 掌握Spring对于AOP的注解
1、AOP作用:
作用:在不惊动原始设计的基础上为其进行功能增强,前面咱们有技术就可以实现这样的功能即代理模式。
@Repository
public class BookDaoImpl implements BookDao {
public void save() {
//记录程序当前执行执行(开始时间)
Long startTime = System.currentTimeMillis();
//业务执行万次
for (int i = 0;i<10000;i++) {
System.out.println("book dao save ...");
}
//记录程序当前执行时间(结束时间)
Long endTime = System.currentTimeMillis();
//计算时间差
Long totalTime = endTime-startTime;
//输出信息
System.out.println("执行万次消耗时间:" + totalTime + "ms");
}
public void update(){
System.out.println("book dao update ...");
}
public void delete(){
System.out.println("book dao delete ...");
}
public void select(){
System.out.println("book dao select ...");
}
}
运行结果
2、AOP使用
添加依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
定义接口
public interface BookDao {
public void save();
public void update();
}
@Repository
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println(System.currentTimeMillis());
System.out.println("book dao save ...");
}
public void update(){
System.out.println("book dao update ...");
}
}
定义类、通知、定义切入点和标识切面类
@Component
@Aspect //标识切面类
public class MyAdvice {
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
开启注解AOP格式
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}
运行结果
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = ctx.getBean(BookDao.class);
bookDao.update();
}
}
可以发现本来update 应该只有输出“book dao update” 但是上面多了一行输出,其实这个就是SpringAOP 对程序进行了加强。
3、AOP切入点表达式
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
-
切入点:要进行增强的方法
-
切入点表达式:要进行增强的方法的描述方式
切入点的标准格式:
execution(public User com.itheima.service.UserService.findById(int))
-
execution:动作关键字,描述切入点的行为动作,例如execution表示执行到指定切入点
-
public:访问修饰符,还可以是public,private等,可以省略
-
User:返回值,写返回值类型
-
com.itheima.service:包名,多级包使用点连接
-
UserService:类/接口名称
-
findById:方法名
-
int:参数,直接写参数的类型,多个类型用逗号隔开
-
异常名:方法定义中抛出指定异常,可以省略
通配符
我们使用通配符描述切入点,主要的目的就是简化之前的配置,具体都有哪些通配符可以使用?
-
*
:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现execution(public * com.itheima.*.UserService.find*(*))
匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
-
..
:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写execution(public User com..UserService.findById(..))
匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法
-
+
:专用于匹配子类类型execution(* *..*Service+.*(..))
这个使用率较低,描述子类的,咱们做JavaEE开发,继承机会就一次,使用都很慎重,所以很少用它。*Service+,表示所有以Service结尾的接口的子类。
下面学习注解
@Before
在运行方法前调用
@After
设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行
@AfterReturning
在返回语句的上一句进入aop方法,所以叫返回后通知
@AfterThrowing
异常后通知,只有在原始方法异常才会调用
正常:
原始方法异常:
@Around
环绕通知需要在原始方法的前后进行增强,所以环绕通知就必须要能对原始操作进行调用,用
pjp.proceed()调用运行原始方法。
如果有2个aop同时对一个方法进行增强,先执行的顺序是按照aop的字母排序来执行,并且是pjp.proceed()调用运行原始方法前,进行另一个aop。
A、B方法交换书写位置发现还是一样输出结果
复习知识点:
@EnableAspectJAutoProxy
名称 | @EnableAspectJAutoProxy |
---|---|
类型 | 配置类注解 |
位置 | 配置类定义上方 |
作用 | 开启注解格式AOP功能 |
@ Aspect
名称 | @Aspect |
---|---|
类型 | 类注解 |
位置 | 切面类定义上方 |
作用 | 设置当前类为AOP切面类 |
@Pointcut
名称 | @Pointcut |
---|---|
类型 | 方法注解 |
位置 | 切入点方法定义上方 |
作用 | 设置切入点方法 |
属性 | value(默认):切入点表达式 |
@Before
名称 | @Before |
---|---|
类型 | 方法注解 |
位置 | 通知方法定义上方 |
作用 | 设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行 |
@AfterReturning
名称 | @AfterReturning |
---|---|
类型 | 方法注解 |
位置 | 通知方法定义上方 |
作用 | 设置当前通知方法与切入点之间绑定关系,当前通知方法在原始切入点方法正常执行完毕后执行 |
@AfterThrowing
名称 | @AfterThrowing |
---|---|
类型 | 方法注解 |
位置 | 通知方法定义上方 |
作用 | 设置当前通知方法与切入点之间绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行 |
@Around
名称 | @Around |
---|---|
类型 | 方法注解 |
位置 | 通知方法定义上方 |
作用 | 设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前后运行 |