SpringAOP——概念与简单实现

参考官网5-6+百度翻译...

一、概念

1、OOP与AOP

面向切面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP)。

OOP:面向对象编程,关键单元是对象,面向的是一个个对象。

AOP:面向切面编程,关键单元是切面,面向的是一个个切面。切面关注点可能是多个不同类型的对象的相同点,也就是说切面可以跨域多个类型和对象。

OOP与AOP两种不同的编程思想,为什么说AOP是OOP的补充呢?将切面视为一个对象,面向切面也就是面向对象。(面向对象就是吊,跟马哲似的。)

2、SpringIOC与SpringAOP

Spring两大核心组件:SpringIOC与SpringAOP,SpringIOC是OOP思想实现的框架组件,SpringAOP则是AOP思想实现框架组件。

经过上面理解先猜想一下:

① SpringAOP的一个切面的关注点可以横跨SpringIOC容器里多个对象。

② SpringAOP的切面本身也是一个对象,会放入到SpringIOC容器中。

3、AspectJ与SpringAOP

AspectJ与SpringAOP都是AOP思想实现的框架。区别在于:

AspectJ:编译期通过字节码技术修改class文件实现切面,运行期内存中仅有一个对象。

SpringAOP:编译器不修改class文件,运行期运用动态代理生成代理类完成切面。内存中会有两个对象(target object & proxy object)

AspectJ的一些切入点、表达式解析简单,SpringAOP吸取了这一优点,使用AspectJ的库来解释与AspectJ 5相同的注释和表达式(例如pointcut的匹配表达式)。因此项目中使用SpringAOP时,会依赖AspectJ框架的jar包——aspectJ.jar,但是这个jar包仅仅是解析匹配作用,AOP运行时仍然是纯Spring AOP(代理实现),并且不依赖于AspectJ编译器或编织器,总结:SpringAOP=AspectJ的注解表达式解析+SpringAOP动态代理实现;SpringAOP与AspectJ框架语法大致相同。

3、术语

AOP术语

AspectJ:切面

Join point:程序执行过程中的一个点,切面关注点包含的一个对象。

Advice:切面在关注点处采取的操作

pointcut:比Join point大的概念,切面关注点包含的所有对象。advice与pointcut表达式关联,并在与该切入点匹配的任何Join point处运行(例如,执行具有特定名称的方法)。

pointcut表达式匹配的join point的概念是AOP的核心,Spring默认使用AspectJ切入点表达语言

introduction:给类型声明其他方法或字段。Spring AOP中允许向任何切入关键点有关的对象引入新的接口(和相应的实现)。例如,您可以使用introduction 使Bean实现 IsModified接口,以简化缓存。

Target object:一个或多个切面Advice的对象。也称为“advised object”。由于Spring AOP是使用运行时代理实现的,因此该对象始终是代理对象。

AOP proxy:由AOP框架创建的一个代理,用于执行切面的协定(advice方法执行等)。在Spring Framework中,AOP代理是JDK动态代理或CGLIB代

Weaving:将切面与其他类型或对象链接以创建代理对象。这可以在编译时(例如,使用AspectJ编译器),加载时或在运行时完成。Spring AOP在运行时执行编织

SpringAOP拓展术语

before advice:在连接点之前运行的advice,但是它不能阻止执行流程继续进行到join point(除非它引发异常)。

after returning advice:在join point正常完成之后要运行的advice(例如,如果方法返回而没有引发异常)

after throwing advice:如果方法因抛出异常而退出,则执行advice

after (finally) advice:无论join point退出的方式如何(正常或特殊返回),均应执行advice

around advice:围绕jion point的advice,例如方法调用。这是最有力的advice。around advice可以在方法调用之前和之后执行自定义行为。它还负责选择是返回join point还是通过返回其自身的返回值或引发异常来捷径建议的方法执行

生涩难懂的概念:理解起来真是麻烦,自己建模成一个数学几何问题理解一下。方法调用是纵向的,将线程执行视为一条垂线。

 

4、springAOP实现

Spring AOP默认将标准JDK动态代理用于AOP代理,也可以使用CGLIB代理。

如果要代理的目标对象实现至少一个接口,则使用JDK动态代理。代理了由目标类型实现的所有接口。如果目标对象未实现任何接口,则将创建CGLIB代理(CGLIB继承实现代理所以方法不能被final修饰)

public interface Xxable {
    void xx();
}

@Component
public class A implements Xxable{
    @Override
    public void xx() {
        System.out.println("I am A xx");
    }
}

@Component
public class B{

    public void xx() {
        System.out.println("I am b xx");
    }
}

@Component//切面必须放到IOC容器中
@org.aspectj.lang.annotation.Aspect//aspecjweaver.jar SpringAOP的注解是AspectJ解析的
public class Aspect {
    @Pointcut("execution(* xx())")
    public void pointCut(){

    }

    @Before("pointCut()")//aspecjweaver.jar SpringAOP的表达式也是AspectJ解析的
    public void beforeAdvice(){
        System.out.println("before advice");
    }

    @After("pointCut()")
    public void afterAdvice(){
        System.out.println("after advice");
    }
}

@Configuration
@EnableAutoConfiguration
@EnableAspectJAutoProxy//启用SpringAOP
public class Main {

    public static void main(String[] args){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.scan("com.app.aop");
        context.refresh();
        A a = (A)context.getBean("a");
        B b = (B)context.getBean("b");
        a.xx();
        b.xx();
        System.out.println(a.getClass().getName());//本来只是想随便打印一下具体类型
        System.out.println(b.getClass().getName());//结果发现spring5.x默认jdk动态代理,但是springboot2.x默认cglib代理
    }
}

简单步骤:

① 需要启用SpringAOP支持,@EnableAspectJAutoProxy

② 声明一个aspect切面,必须且切面也是一个对象,需要注入到容器中。@Aspect & @Component

③ 声明关注点@pointcut

④ 声明advice

5、用上面例子验证Advice执行

//无exception
around before advice
before advice
I am A xx
around after advice
after advice
AfterReturning advice
//有exception 不会执行aroud after 
around before advice
before advice
I am A xx
after advice
AfterThrowing advice
exception

6、pointcut表达式解析

  • execution:模糊匹配。用于匹配方法执行的join points。这是使用Spring AOP时要使用的主要切入点指示符。

  • within:包或类型匹配。将匹配限制为某些类型内的join points(使用Spring AOP时,在匹配类型内声明的方法的执行)。

  • this:指定代理对象(proxy object)。限制匹配到join points(使用Spring AOP时方法的执行)的匹配,其中bean引用(Spring AOP代理)是给定类型的实例。

  • target:指定目标对象(target object)。在目标对象(代理的应用程序对象)是给定类型的实例的情况下,将匹配限制为join points(使用Spring AOP时方法的执行)。

  • args:指定方法参数。在参数为给定类型的实例的情况下,将匹配限制为join points(使用Spring AOP时方法的执行)。

  • @target:在执行对象的类具有给定类型的注释的情况下,将匹配限制为join points(使用Spring AOP时方法的执行)。

  • @args:限制匹配的join points(使用Spring AOP时方法的执行),其中传递的实际参数的运行时类型具有给定类型的注释。

  • @within:将匹配限制为具有给定注释的类型内的join points(使用Spring AOP时,使用给定注释的类型中声明的方法的执行)。

  • @annotation:匹配注解(自定义注解的良好使用)。将匹配限制在join points的主题(Spring AOP中正在执行的方法)具有给定注释的连接点上。

官方给出的表达式格式及列举的常用pointcut表达式:

//modifiers-pattern? 方法访问修饰符public private等匹配
//ret-type-pattern 方法返回类型匹配
//declaring-type-pattern  方法声明类型匹配
//name-pattern(param-pattern) 方法名称匹配
// thows-pattern 方法声明异常throws匹配
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

//常用表达式
//所有public 方法
execution(public * *(..))
//所有set开头的方法
execution(* set*(..))
//AccountService接口定义的所有方法
execution(* com.xyz.service.AccountService.*(..))
//service包下的所有方法
execution(* com.xyz.service.*.*(..))
//service包或其子包的所有方法
execution(* com.xyz.service..*.*(..))
//service包下的所有方法
within(com.xyz.service.*)
//service包或其子包的所有方法
within(com.xyz.service..*)
//代理对象实现AccountService接口的所有方法
this(com.xyz.service.AccountService)
//目标对象实现AccountSerivce接口的所有方法
target(com.xyz.service.AccountService)
//采用单个参数的所有方法
args(java.io.Serializable)
//目标对象的声明类型带有@Transactional注解的所有方法
@target(org.springframework.transaction.annotation.Transactional)
//目标对象的声明类型带有@Transactional注解的所有方法
@within(org.springframework.transaction.annotation.Transactional)
//带有@Transactional注解的所有方法
@annotation(org.springframework.transaction.annotation.Transactional)
//任何采用单个参数的方法,且参数类型被@Classified注解
@args(com.xyz.security.Classified)
//beanName=tradeService的bean的所有方法
bean(tradeService)
//beanName=*Service的bean的所有方法
bean(*Service)
posted on 2020-03-01 20:40  FFStayF  阅读(190)  评论(0编辑  收藏  举报