zoukankan      html  css  js  c++  java
  • 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)
  • 相关阅读:
    Oracle decode函数
    Flink笔记
    httpclient之put 方法(参数为json类型)
    XMLHTTPRequest的理解 及 SpringMvc请求和响应xml数据
    SQL获取本周,上周,本月,上月第一天和最后一天 注:本周从周一到周天
    Other
    Sql根据起止日期生成时间列表
    sql 在not in 子查询有null值情况下经常出现的陷阱
    sql 判断一个表的数据不在另一个表中
    查看系统触发器
  • 原文地址:https://www.cnblogs.com/wqff-biubiu/p/12391875.html
Copyright © 2011-2022 走看看