Spring框架复习笔记——Spring框架之AOP

目录

1.AOP面向切面编程

1.1.AOP介绍

1.2AOP图解

2.AOP术语

3.1 Spring AOP基于xml

3.2Spring中AOP基于注解

4.Spring基于XML的通知执行顺序

4.1.XML文件配置说明

4.2.各种通知说明

4.3.在xml配置同一个切入点且不出现异常时的执行顺序

4.3.1.情况一

4.3.2.情况二

4.3.3.情况三

4.3.4.情况四

4.3.5.小结

4.4.Spring基于注解的通知执行顺序

4.4.1.正常情况

4.4.2.异常情况

4.4.3结论


1.AOP面向切面编程

1.1.AOP介绍

OOP(Object Oriented Programming ) 面向对象编程,万物皆对象!

AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"\",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性

使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证日志打印、事务处理。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

1.2AOP图解

AOP编程底层代理设计模式!Spring框架底层使用的代理设计模式来完成AOP!

 

 通过动态代理,可以在指定位置执行对应流程。这样就可以将一些横向的功能抽离出来形成一个独立的模块,然后在指定位置插入这些功能。这样的思想,被称为面向切面编程,亦即AOP。

 

 

2.AOP术语

 1.target目标类,需要被代理的类。例如:UserServiceImpl

 2.Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法

 3.PointCut 切入点:已经被增强的连接点。例如:addUser()

 4.advice 通知/增强,增强代码。例如:after、before

 5. Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.

 6.proxy 代理类:通知+切入点(由动态代理自动生成的类)

 7. Aspect(切面): 是切入点pointcut和通知advice的结合

    8.引介(Introduction):引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

具体可以根据下面这张图来理解:

3.1 Spring AOP基于xml

准备操作对象

/**
 * 目标类
 */
public class CarServiceImpl implements CarService {

    public Integer add(Car c) {
        System.out.println("新增Car到数据库:"+c);
        return 1;
    }

    public Integer update(Car c) {
        System.out.println("更新Car到数据库:"+c);
        return 1;
    }
}

 在想要的server前面添加增强类

public class TransactionTx {

    /**
     * 前置增强(通知)
     * JoinPoint joinPoint 连接点(封装了目标方法信息)
     */
    public void openTx(JoinPoint joinPoint){
        Object target = joinPoint.getTarget(); //目标类对象
        Object proxyObject = joinPoint.getThis(); //代理类对象
        Signature signature = joinPoint.getSignature(); //目标方法
        String name = signature.getName();//目标方法名字
        int modifiers = signature.getModifiers();//目标方法修饰符
        Object[] args = joinPoint.getArgs(); // 方法的参数
        System.out.println("-前置增强(通知)---开启事务---,目标方法的方法名:"+name+",方法参数:"+ Arrays.toString(args));
    }

    /**
     * 返回增强(返回通知)  后置通知(目标方法如果执行有异常,那么异常通知执行,返回通知不执行!)
     */
    public void commitTx(JoinPoint joinPoint,Object result){
        System.out.println("--返回增强(返回通知)--提交事务---:返回值:"+result);
    }

    /**
     * 异常增强(通知)
     * 目标方法如果执行有异常,那么异常通知执行,返回通知不执行!
     * 目标方法没有异常,那么正常执行,执行返回通知
     */
    public void rollbackTx(JoinPoint joinPoint,Throwable e){
        System.out.println("--异常增强(通知)--回滚事务--- 异常啦:"+e.getMessage());
    }

    /**
     * 最终增强(通知):无论目标方法是否有异常,都会执行!
     */
    public void finnalMethod(JoinPoint joinPoint){
        System.out.println("--最终增强(通知)--释放系统资源--");
    }


    /**
     * 环绕通知
     * @param joinPoint
     * @return
     */
    public Object aroundMethod(ProceedingJoinPoint joinPoint) {
        Object target = joinPoint.getTarget(); //目标类对象
        Object proxyObject = joinPoint.getThis(); //代理类对象
        Signature signature = joinPoint.getSignature(); //目标方法
        String name = signature.getName();//目标方法名字
        int modifiers = signature.getModifiers();//目标方法修饰符
        Object[] args = joinPoint.getArgs(); // 方法的参数

        Object result=null;
        try {
            System.out.println("---环绕(前)---开启事务---,目标方法的方法名:"+name+",方法参数:"+ Arrays.toString(args));
            //调用目标类目标方法
            result=joinPoint.proceed();
            System.out.println("---环绕(返回)---提交事务---:返回值:"+result);
        } catch (Throwable e) {
            //e.printStackTrace();
            System.out.println("--环绕(异常)--回滚事务--- 异常啦:"+e.getMessage());
        } finally {
            System.out.println("--环绕(最终)--释放系统资源--");
        }
        return result;
    }

织入目标对象(xml)

   <!--增强类对象-->
    <bean id="tx" class="com.it.tx.TransactionTx"/>

    <!--目标类对象-->
    <bean id="carService" class="com.it.service.impl.CarServiceImpl"/>

    <!--配置切面(告诉Spring框架,哪些类中的哪些方法需要被代理,如何代理)-->
    <aop:config>
        <!--pointcut:切入点表达式(哪个类中哪个方法需要被代理)-->
        <aop:pointcut id="pc" expression="execution(* com.it.service.impl.*.*(..))"/>
        <!--如何代理,目标方法之前还是之后?-->
        <aop:aspect ref="tx">
            <!--前置增强-->
            <aop:before method="openTx" pointcut-ref="pc"/>
            <!-- 返回增强(返回通知)  后置通知-->
            <aop:after-returning method="commitTx" returning="result" pointcut-ref="pc"/>
            <!--环绕通知-->
            <aop:around method="aroundMethod" pointcut-ref="pc"/>
            <!--异常通知-->
            <aop:after-throwing method="rollbackTx" throwing="e" pointcut-ref="pc"/>
            <!--最终通知-->
            <aop:after method="finnalMethod" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

    <!--默认值false 表示使用JDK动态代理
        true 使用CGLIB动态代理,但是如果没有接口,不管ture false 都是CGLIB代理!
    -->
    <aop:aspectj-autoproxy proxy-target-class="false"/>

切入点表达式(可以使用通配符) 

 测试

   @Test
    public void test1(){
        ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext("cars.xml");
        CarService carService = factory.getBean(CarService.class);
        ComputerService computerService = factory.getBean(ComputerService.class);

        Integer count = carService.add(new Car(1, "BMW", "川A3244"));
        System.out.println(count>0?"新增成功":"新增失败");
   }

3.2Spring中AOP基于注解

添加配置类

@ComponentScan("com.it")
@EnableAspectJAutoProxy(proxyTargetClass = false) //默认使用JDK动态代理
public class SpringConfig {

}

添加操作对象

@Service
public class CarServiceImpl implements CarService {

    public Integer add(Car c) {
        System.out.println("新增Car到数据库:"+c);
        return 1;
    }

    public Integer update(Car c) {
        System.out.println("更新Car到数据库:"+c);
        return 1;
    }
}

添加增强类

@Aspect  //增强类
@Component
@Order(1)
public class TransactionTx {

    /**
     * 前置增强(通知)
     */
    @Before(value = "execution(* com.it.service.impl.*.*(..))")
    public void openTxB(JoinPoint joinPoint){
        Object target = joinPoint.getTarget(); //目标类对象
        Object proxyObject = joinPoint.getThis(); //代理类对象
        Signature signature = joinPoint.getSignature(); //目标方法
        String name = signature.getName();//目标方法名字
        int modifiers = signature.getModifiers();//目标方法修饰符
        Object[] args = joinPoint.getArgs(); // 方法的参数
        System.out.println("-TransactionTx--B--前置增强(通知)---开启事务---,目标方法的方法名:"+name+",方法参数:"+ Arrays.toString(args));
    }

    @Before(value = "execution(* com.it.service.impl.*.*(..))")
    public void openTxA(JoinPoint joinPoint){
        Object target = joinPoint.getTarget(); //目标类对象
        Object proxyObject = joinPoint.getThis(); //代理类对象
        Signature signature = joinPoint.getSignature(); //目标方法
        String name = signature.getName();//目标方法名字
        int modifiers = signature.getModifiers();//目标方法修饰符
        Object[] args = joinPoint.getArgs(); // 方法的参数
        System.out.println("-TransactionTx--A--前置增强(通知)---开启事务---,目标方法的方法名:"+name+",方法参数:"+ Arrays.toString(args));
    }



    /**
     * 返回增强(返回通知)  后置通知(目标方法如果执行有异常,那么异常通知执行,返回通知不执行!)
     */
    //@AfterReturning(value = "execution(* com.it.service.impl.*.*(..))",returning = "result")
    public void commitTx(JoinPoint joinPoint,Object result){
        System.out.println("--返回增强(返回通知)--提交事务---:返回值:"+result);
    }

    /**
     * 异常增强(通知)
     * 目标方法如果执行有异常,那么异常通知执行,返回通知不执行!
     * 目标方法没有异常,那么正常执行,执行返回通知
     */
    //@AfterThrowing(value = "execution(* com.it.service.impl.*.*(..))",throwing = "e")
    public void rollbackTx(JoinPoint joinPoint,Throwable e){
        System.out.println("--异常增强(通知)--回滚事务--- 异常啦:"+e.getMessage());
    }

    /**
     * 环绕通知
     * @param joinPoint
     * @return
     */
    //@Around(value = "execution(* com.it.service.impl.*.*(..))")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) {
        Object target = joinPoint.getTarget(); //目标类对象
        Object proxyObject = joinPoint.getThis(); //代理类对象
        Signature signature = joinPoint.getSignature(); //目标方法
        String name = signature.getName();//目标方法名字
        int modifiers = signature.getModifiers();//目标方法修饰符
        Object[] args = joinPoint.getArgs(); // 方法的参数

        Object result=null;
        try {
            System.out.println("---环绕---开启事务---,目标方法的方法名:"+name+",方法参数:"+ Arrays.toString(args));
            //调用目标类目标方法
            result=joinPoint.proceed();
            System.out.println("---环绕---提交事务---:返回值:"+result);
        } catch (Throwable e) {
            //e.printStackTrace();
            System.out.println("--环绕--回滚事务--- 异常啦:"+e.getMessage());
        } finally {
            System.out.println("--环绕--释放系统资源--");
        }
        return result;
    }

    /**
     * 最终增强(通知):无论目标方法是否有异常,都会执行!
     */
    //@After(value = "execution(* com.it.service.impl.*.*(..))")
    public void finnalMethod(JoinPoint joinPoint){
        System.out.println("--最终增强(通知)--释放系统资源--");
    }

}

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TestSpringAop {

    @Autowired
    CarService carService;

    @Test
    public void test1(){
        Integer count = carService.add(new Car(1, "BMW", "川A3244"));
        System.out.println(count>0?"新增成功":"新增失败");
    }
}

4.Spring基于XML的通知执行顺序

4.1.XML文件配置说明

在这里插入图片描述

4.2.各种通知说明

配置前置通知:在切入点方法执行之前执行

配置后置通知(返回通知):在切入点方法正常执行之后执行。它和异常通知永远只能执行一个

配置异常通知:在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个

配置最终通知:无论切入点方法是否正常执行,它都会在其后面执行

配置环绕通知:可以在代码中手动控制增强方法何时执行

注意:后置(返回通知)通知和最终通知的区别:后置通知时在方法成功执行后会执行的,如果出现异常就不执行。而最终通知时无论是否出现异常都会执行的,感觉类似于finally

4.3.在xml配置同一个切入点且不出现异常时的执行顺序

在这里插入图片描述

注意,椭圆中顺序不固定,具体顺序与配置文件的申明顺序有关

结论:XML配置AOP,通知的执行顺序和Spring版本无关。和通知的声明顺序有关,只能确定前置通知一定在目标方法之前!

4.3.1.情况一

<!--3.2.配置通知-->
 <aop:aspect ref="tx">
     <!--前置通知-->
     <aop:before method="openTx" pointcut-ref="pointcut"/>
     <!--环绕通知-->
     <aop:around method="aroundAdvice" pointcut-ref="pointcut"/>
     <!--后置通知(返回通知)-->
     <aop:after-returning method="CommitTx" pointcut-ref="pointcut" returning="value"/>
     <!--最终通知-->
     <aop:after method="finnallyMethod" pointcut-ref="pointcut"/>
     <!--异常通知-->
     <aop:after-throwing method="Rollback" pointcut-ref="pointcut" throwing="ex"/>
 </aop:aspect>

顺序: 在这里插入图片描述

4.3.2.情况二

<!--3.2.配置通知-->
 <aop:aspect ref="tx">
     <!--环绕通知-->
     <aop:around method="aroundAdvice" pointcut-ref="pointcut"/>
     <!--前置通知-->
     <aop:before method="openTx" pointcut-ref="pointcut"/>
     <!--后置通知(返回通知)-->
     <aop:after-returning method="CommitTx" pointcut-ref="pointcut" returning="value"/>
     <!--最终通知-->
     <aop:after method="finnallyMethod" pointcut-ref="pointcut"/>
     <!--异常通知-->
     <aop:after-throwing method="Rollback" pointcut-ref="pointcut" throwing="ex"/>
 </aop:aspect>

顺序: 在这里插入图片描述

结论一:前置通知和环绕通知的顺序和申明顺序有关,申明在前的先执行

4.3.3.情况三

<aop:aspect ref="tx">
    <!--环绕通知-->
    <aop:around method="aroundAdvice" pointcut-ref="pointcut"/>
    <!--前置通知-->
    <!--<aop:before method="openTx" pointcut-ref="pointcut"/>-->
    <!--后置通知(返回通知)-->
    <aop:after-returning method="CommitTx" pointcut-ref="pointcut" returning="value"/>
    <!--最终通知-->
    <aop:after method="finnallyMethod" pointcut-ref="pointcut"/>
    <!--异常通知-->
    <aop:after-throwing method="Rollback" pointcut-ref="pointcut" throwing="ex"/>
</aop:aspect>

顺序: 在这里插入图片描述

4.3.4.情况四

<aop:aspect ref="tx">
    <!--环绕通知-->
    <aop:around method="aroundAdvice" pointcut-ref="pointcut"/>
    <!--前置通知-->
    <!--<aop:before method="openTx" pointcut-ref="pointcut"/>-->
    <!--最终通知-->
    <aop:after method="finnallyMethod" pointcut-ref="pointcut"/>
    <!--后置通知(返回通知)-->
    <aop:after-returning method="CommitTx" pointcut-ref="pointcut" returning="value"/>
    <!--异常通知-->
    <aop:after-throwing method="Rollback" pointcut-ref="pointcut" throwing="ex"/>
</aop:aspect>

顺序: 在这里插入图片描述

4.3.5.小结

结论:Spring基于XML的申明式通知的执行顺序与配置文件中的申明顺序有关

4.4.Spring基于注解的通知执行顺序

在这里插入图片描述 我们在网上查找关于SpringAop执行顺序的的资料,大多数时候,你会查到如下的答案:

4.4.1.正常情况

在这里插入图片描述

4.4.2.异常情况

在这里插入图片描述

上述测试结果是在Spring的5.2.6.RELEASE版本下进行测试,换成5.2.7.RELEASE版本测试结果就不同了!

从Spring5.2.7开始,在相同@Aspect类中,通知方法将根据其类型按照从高到低的优先级进行执行:@Around,@Before ,@AfterReturning, @After,@AfterThrowing。

4.4.3结论

经过上面的资料文档查阅,我能给出的结论是:

从Spring5.2.7开始,Spring AOP不再严格按照AspectJ定义的规则来执行advice,而是根据其类型按照从高到低的优先级进行执行:

没有异常:@Around,@Before ,@AfterReturning, @After

有异常:@Around,@Before , @AfterThrowing @After

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李仙桎

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值