Spring-AOP

152 阅读3分钟

AOP介绍

AOP 是什么

AOP(Aspect Oriented Programing)面向切面编程,它是一种设计思想,的目的就是在不修改源代码的基础上,对原有功能进行增强。

思路分析

编写目标对象:AcccountServiceImpl

编写增强功能(通知): Logger

配置切面:描述通知和切点之间的关系,增强功能添加到哪个位置

切点表示式

切入点表达式是一个快速匹配方法的通配格式,类似于正则表达式

切点表达式的组成:

  • 关键字(访问修饰符 返回值 包名.类名.方法名(参数)异常名)

    • 关键字:描述表达式的匹配模式

      • execution表达式可以用于明确切面粒度最小是达到方法级别(重要)
  • 访问修饰符:方法的访问控制权限修饰符(一般会省略)

  • 返回值类型::* ,表示任意类型

  • 路径:方法所在的类(此处也可以配置接口名称)

通配符

*:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现

.. :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写

切点数量和位置

切点表达式可以配置在不同位置:公共切点、局部切点、私有切点

通知类型

AOP的通知类型共5种

  • 前置通知:原始方法执行前执行,如果通知中抛出异常,阻止原始方法运行 应用:数据校验<aop:before>
  • 后置通知:原始方法执行后执行,无论原始方法中是否出现异常,都将执行通知 应用:资源释放(线程关闭、连接关闭) <aop:after>
  • 返回后通知:原始方法正常执行完毕并返回结果后执行,如果原始方法中抛出异常,无法执行 应用:返回值相关数据处理<aop:after-returning>
  • 抛出异常后通知:原始方法抛出异常后执行,如果原始方法没有抛出异常,无法执行 应用:对原始方法中出现的异常信息进行处理 <aop:after-throwing>
  • 环绕通知:在原始方法执行前后均有对应执行执行,能够实现前面4个类型通知的功能 应用:十分强大,可以做前4种类型通知

通知的执行顺序(了解)

当同一个切入点配置了多个通知时,通知会存在运行的先后顺序,该顺序以通知配置顺序为优先 注解:顺序无法调换,固定

<aop:config>
    <!--配置切点-要对哪些方法增强-->
    <!--aop:pointcut:配置切点表达式-->
    <!--expression: 表达式,表示后面是配置切点表达式-->
    <!--
    方法所在的路径
    com.eponine.aop.service.impl.AccountServiceImpl.*: 匹配AccountServiceImpl中的所有方法
    com.eponine.aop.service.impl.*.* : 对service.impl包下所以类中的所有方法都匹配
    com.eponine..impl.*.* : ..在这里表示0-N个包
    com.eponine..impl.*.update*(..): 此时update* 表示对update开头的方法进行匹配
    方法的参数列表:
    (..): ..表示忽略参数的数量还有类型 () (String str) (Integer id,Account account)
    (*) : 此时表示参数个数只有1个,但是忽略类型 ()这个是不行的,(Account account)OK  (String str)OK (Integer id,Account account)不行
    -->
    <!--公共切点:所有切面都可以使用-->
    <aop:pointcut id="pt"
                  expression="
                  execution(
                  public * com.eponine.AOP.service.impl.AccountServiceImpl.*(..)
                  )"/>
    <aop:aspect ref="myLogger">
        <!--局部切点:只能当前切面配置可以使用-->
        <aop:pointcut id="pt3" expression="execution(* com.eponine.AOP.service.impl.AccountServiceImpl.*(..))"/>
        <!--私有的切点:只有当前通知可以使用-->
        <aop:before method="beforeTime" pointcut-ref="pt" pointcut="execution(* com.itheima.aop.service.impl.AccountServiceImpl.*(..))">></aop:before>
        <aop:after-returning method="afterReturnningTime" pointcut-ref="pt"></aop:after-returning>
        <aop:after-throwing method="throwingMethod" pointcut-ref="pt"></aop:after-throwing>
        <aop:after method="afterMethod" pointcut-ref="pt"></aop:after>
        <aop:around method="arountMethod" pointcut-ref="pt"></aop:around>
    </aop:aspect>
</aop:config>

AOP注解版

增强类中完成注解配置

  • AccountServiceImpl加上@Service注解,此步骤省略
  • 增强类MyLogger注解配置
  1. @EnableAspectJAutoProxy开启AOP注解支持
  2. 使用注解@Aspect表示这是一个增强类
  3. 配置切点方法
  4. 配置通知注解
public class TxManager {
    ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
    @Autowired
    private DataSource dataSource;
    @Pointcut("execution(* com.eponine.spring.service.impl.*ServiceImpl.*(..))")
    public void pt(){}

    @Around("pt()")
    public Object arroundTran(ProceedingJoinPoint pjp){
        Object result = null;
        try {
            start();
            result = pjp.proceed();
            commit();
        }catch (Throwable throwable) {
            throwable.printStackTrace();
            rollBack();
        } finally {
            close();
        }
        return result;
    }
    //获取连接
    public Connection getConnection(){
        Connection connection = threadLocal.get();
        if (connection==null) {
            try {
                connection = dataSource.getConnection();
                threadLocal.set(connection);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return connection;
    }
    //设置手动提交事务
    public void start(){
        Connection connection = threadLocal.get();
        if (threadLocal.get()==null){
            try {
                connection = dataSource.getConnection();
                threadLocal.set(connection);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        try {
            threadLocal.get().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    //提交事务
    public void commit(){
        try {
            threadLocal.get().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    //回滚事务
    public void rollBack(){
        try {
            threadLocal.get().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    //关闭资源
    public void close(){
        try {
            threadLocal.get().close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}