Spring学习笔记(5)

Spring框架的AOP

AOPAspect-Oriented Programming),面向方面的编程

Spring框架的一个关键组件就是面向方面的编程(AOP)框架。面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

面向方面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。主要功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。

AOP几个相关概念:

名称

说明

切面(Aspect

一个关注点的模块化,这个关注点可能会横切多个对象。

连接点(Joinpoint

程序执行过程中的某个特定的点

通知(Advice

在切面的某个特定的连接点上执行的动作

切入点(Pointcut

匹配连接点的断言,在AOP中通知和一个切入点表达式关联

引入(Introduction

在不修改类代码的前提下,为类添加新的方法和属性

目标对象(Target Object

被一个或者多个切面所通知的对象

AOP代理(AOP Proxy

AOP框架创建的对象,用来实现切面契约(Aspect contract)(包括通知方法执行等功能)

织入(Weaving

把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入、执行时织入

通知的类型

Spring 方面可以使用下面提到的五种通知工作:

通知

描述

前置通知(Before advice)

在某连接点(join point)之前执行的通知,但不能组织连接点之前的执行(除非它跑出一个异常)

返回后通知(After returning advice)

在某连接点(join point)正常完成后执行的通知

抛出异常后通知(After throwing advice)

在一个方法抛出异常时执行的通知

后置通知(After(finally)advice)

在某连接点退出的时候执行的通知(不论是正常返回还是异常退出)

环绕通知(Around Advice)

包围一个连接点(join point)的通知

  • Spring AOP默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者接口集)都可以被代理
  • Spring AOP中也可以使用CGLIB代理(如果一个业务对象并没有实现一个接口)

使用xml配置aop:

随便写一个业务类,如

package com.mine.springdemo.service.impl;

public class UserServiceImpl {

    public void save(int i) {
        System.out.println("保存成功"+i);
    }
    
}

然后是配置通知方法,

package com.mine.springdemo.utils;

import org.aspectj.lang.ProceedingJoinPoint;

public class Logging {

    public void before() {
        System.out.println("logging");
    }
    
    public void afterReturning() {
        System.out.println("afterReturning");
    }
    
    public void afterThrowing() {
        System.out.println("afterThrowing");
    }
    
    public void after() {
        System.out.println("after(finally)");
    }
    
    public Object around(ProceedingJoinPoint pjp) {
        Object obj = null;
        try {
            System.out.println("around1");
            obj = pjp.proceed();
            System.out.println("around2");
        } catch (Throwable e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        return obj;
    }
}

值得一提的是after-throwing方法必须要有异常才会执行,而这时候after-returning不会执行,相反after-returning执行时,after-throwing是不会执行的

其次是around方法,around方法必须传入一个ProceedingJoinPoint参数。

xml文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    
    <bean id="userService" class="com.mine.springdemo.service.impl.UserServiceImpl"></bean>

    <bean id="logging" class="com.mine.springdemo.utils.Logging"></bean>
    
    <!-- aop的配置 -->
    <aop:config>
        <!-- 配置aop的切面 -->
        <aop:aspect id="logadvice" ref="logging">
            <!-- 配置切入点 
                expression属性:
                    使用execution配置方法,其例如下:
                    execution(public * *(..)) 切入点为执行所有public方法时
                    execution(* set*(..)) 切入点为执行所有set方法时
                    execution(* com.mine.service.AccountService.*(..)) 切入点为执行AccountService类下所有方法时
                    execution(* com.mine.service..(..)) 切入点为service包下的所有方法时
                    execution(* com.mine.service...(..)) 切入点为service包及其子包下的所有方法时
                id属性.
            -->
            <aop:pointcut expression="execution(public void com.mine.springdemo.service.impl.UserServiceImpl.save(int))" id="user"/>
            <!-- 五种通知   -->
            <aop:before method="before"  pointcut-ref="user"/>        
            <aop:after-returning method="afterReturning" pointcut-ref="user"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="user"/>
            <aop:after method="after" pointcut-ref="user"/>
            <aop:around method="around" pointcut-ref="user"/>
        </aop:aspect>
    </aop:config>
    
</beans>

输出结果如下:

(正常输出)

当把aop:around 注释掉后在userService里的save方法中手动抛出异常时,代码和执行结果如下,

  public void save(int i) {
        System.out.println("保存成功"+i);
        throw new RuntimeException();
    }
    

输出中没有了after-returning方法,而是after-throwing的方法

猜你喜欢

转载自www.cnblogs.com/HJYDR/p/12383599.html