前言:
学习内容来自于黑马的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 |
---|---|
类型 | 方法注解 |
位置 | 通知方法定义上方 |
作用 | 设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前后运行 |