Spring AOP概述

1 AOP简介

(1) AOP概念

Spring有两个核心的概念,一个是IOC/DI,一个是AOP。
AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构。
SpringAOP是在不改变原有设计(代码)的前提下对其进行增强的,它的底层采用的是代理模式实现的,所以要对原始对象进行增强,就需要对原始对象创现了增强,这就是我们所说的代理(Proxy)。
SpringAOP的本质或者可以说底层实现是通过代理模式。

(2) AOP作用

在不惊动原始设计的基础上为其进行功能增强,前面咱们有技术就可以实现这样的功能即代理模式。

(3) AOP核心概念

连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等。
切入点(Pointcut):匹配连接点的式子,即对于需要增强的方法(原始方法)。
//一个切入点可以描述一个具体方法,也可也匹配多个方法。
//连接点包含切入点。
通知(Advice):将共性功能抽取到一个方法中,共性功能是在切入点处执行的操作,即原始方法所增强的内容。
通知类:通知是一个方法,方法不能独立存在需要被写在一个类中,这个类即为通知类。 
切面(Aspect):描述通知和切入点之间的对应关系,即哪个切入点需要添哪个通知。

 

2 AOP入门案例

//为BookDao的save方法增强功能,在执行前打印系统时间。

(1) 添加依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
<!--因为spring-context中已经导入了spring-aop,所以不需要再单独导入spring-aop。-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
<!--导入AspectJ的jar包,AspectJ是AOP思想的一个具体实现,Spring有自己的AOP实现,但是相比于AspectJ来说比较麻烦,所以直接采用Spring整合ApsectJ的方式进行AOP开发。-->

(2) 定义接口与实现类

//即BookDao和BookDaoImpl。

(3) 定义通知类和通知

//通知就是将共性功能抽取出来后形成的方法,共性功能指的就是当前系统时间的打印。

public class MyAdvice {
    //类名任意
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

(4) 定义切入点

//BookDaoImpl中有两个方法,分别是save和update,要增强的是save方法。

public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.save())")
    private void pt(){}
    //切入点定义依托一个不具有实际意义的方法进行,即无参数、无返回值、方法体无实际逻辑。

    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

(5) 制作切面

//绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置。

public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}

    @Before("pt()")
    //@Before翻译过来是之前,也就是说通知(method()方法)会在切入点方法(即save()方法)执行之前执行
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

(6) 将通知类配给容器并标识其为切面类

@Component
//标识为一个bean。
@Aspect
//告诉配置类这是一个切面类,来做AOP的。
public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}

    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
        //打印当前系统时间
    }
}

(7) 开启注解格式AOP功能

@Configuration
@ComponentScan("com.itheima")
//@ComponentScan扫描bean组件包括刚刚创建的aop包。
@EnableAspectJAutoProxy
//开启AOP功能
//找不到Bean加上@EnableAspectJAutoProxy(proxyTargetClass=true)
public class SpringConfig {
}

(8) 运行程序

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        bookDao.save();
    }
}


3 AOP工作流程

//本质上,是在所有Bean创建前进行一个筛选,若需要被增强则创建代理对象,若不需要被增强则创建原始对象。

(1) Spring容器启动

容器启动需要去加载bean,如需要被增强的类、通知类等,但注意此时bean对象还没有创建成功。
//容器创建时,先加载bean,再读取bean中的信息,最后创建和实例化bean。

(2) 读取所有切面配置中的切入点

但若切入点未与通知绑定对应关系,即没有切面,那么该切入点则不会被读取。

(3) 初始化bean

判定所要初始化的bean对应的类中方法是否匹配到任意切入点。
若即将被实例化的bean有方法与所读取的切入点相匹配,说明需要增强,则创建原始对象的代理对象,置于Ioc容器中。
//因为要对目标对象进行功能增强,而采用的技术是动态代理,所以会为其创建一个代理对象,最终运行的是代理对象的方法,在该方法中会对原始方法进行功能增强。
//就是将通知塞到目标对象所产生的代理对象的接入点里,重新生成一个切入点,来运行这个通知和原始代码。
//如果目标对象中的方法会被增强,那么容器中将存入的是目标对象的代理对象。
若即将被实例化的bean有方法与所读取的切入点不匹配,说明不需要增强,则创建原始对象,置于Ioc容器中。
//如果目标对象中的方法不被增强,那么容器中将存入的是目标对象本身。

(4) 获取bean执行方法

获取的bean是原始对象时,调用方法并执行,完成操作。
获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作。
//注意,此时代理对象运行的是增强方法,返回值为增强方法的返回值,即通知(一般为环绕通知)的返回值,是AOP重新组织的返回值。

(5) 验证容器中是否为代理对象

要执行的方法,不被定义的切入点包含,即不要增强,打印当前类的getClass()方法。
要执行的方法,被定义的切入点包含,即要增强,打印出当前类的getClass()方法。
不能直接打印对象,直接打印对象走的是对象的toString方法,否则结果都是一样的,原因是内部对toString方法进行了重写。

posted @ 2023-10-17 21:02  10kcheung  阅读(57)  评论(0)    收藏  举报