Spring AOP 详解以及 Spring 使用 XML 配置 AOP

一、概述

1、AOP

  • 全称是 Aspect Oriented Programming 即:面向切面编程
  • AOP 就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。
  • AOP:面向切面编程,aop就是在某一个类或方法执行前后打个标记,声明在执行到这里之前要先执行什么,执行完这里之后要接着执行什么,插入新的执行方法。在Spring中,它是以JVM的动态代理技术为基础,然后设计一系列AOP横切实现,比如前置通知、返回通知、异常通知等,同时Pointcut接口来匹配切入点,可以使用现有切入点来设计横切面,也可以扩展相关方法根据需求进行切入。

2、作用:

  • 在程序运行期间,不修改源码对已有方法进行增强。

3、优势:

  • 减少重复代码
  • 提高开发效率
  • 维护方便

4、AOP 的实现方式

  • 使用动态代理技术
    关于动态代理请点击

二、AOP(面向切面编程)

1、AOP 相关术语

  • Joinpoint(连接点) : 是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。
  • Pointcut(切入点) : 是指我们要对哪些 Joinpoint 进行拦截的定义。
  • Advice(通知/增强) : 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。
    通知的类型 :前置通知,后置通知,异常通知,最终通知,环绕通知
  • Introduction(引介) : 是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。
  • Target(目标对象) : 代理的目标对象。
  • Weaving(织入) : 是指把增强应用到目标对象来创建新的代理对象的过程。
    spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
  • Proxy(代理): 一个类被 AOP 织入增强后,就产生一个结果代理类。
  • Aspect(切面) : 是切入点和通知(引介)的结合。

2、spring 中的 AOP 要明确的事

a、开发阶段(程序员做的)

  • 编写核心业务代码(开发主线):大部分程序员来做,要求熟悉业务需求。
  • 把公用代码抽取出来,制作成通知。(开发阶段最后再做):AOP 编程人员来做。
  • 在配置文件中,声明切入点与通知间的关系,即切面。:AOP 编程人员来做。

b、运行阶段(Spring 框架完成的)

  • Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

三、XML 配置 AOP 步骤

第一步:配置要搜索的包

<context:component-scan base-package="cn.lemon"></context:component-scan>

第二步:使用 aop:config 声明 aop 配置

  • 作用:用于声明开始 aop 的配置
<!--aop 配置
     # proxy-target-class="true":使用CGLib代理,默认是 接口代理(JDK代理)
-->
<aop:config proxy-target-class="true">
        <!--配置的代码都写在此处-->
</aop:config>

第三步:使用 aop:aspect 配置切面

  • 作用:用于配置切面。
  • 属性:id:给切面提供一个唯一标识。ref:引用配置好的通知类 bean 的 id。
<aop:aspect ref="loggerAdvice">
     <!--配置通知的类型要写在此处-->
</aop:aspect>

第四步:使用 aop:pointcut 配置切入点表达式

  • 作用:用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强。
  • 属性:expression:用于定义切入点表达式。id:用于给切入点表达式提供一个唯一标识
<!--切入点表达式:定义那些方法织入增强-->
<aop:pointcut id="serviceMethod" expression="execution(* cn.lemon.service.impl.*.*(..))"></aop:pointcut><!--切入点-->

切入点表达式说明
表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
全匹配方式:

public void cn.lemon.service.impl.AccountServiceImpl.addAccount(cn.lemon.domain.Account)

访问修饰符可以省略

void cn.lemon.service.impl.AccountServiceImpl.addAccount(cn.lemon.domain.Account)

返回值可以使用*号,表示任意返回值

* cn.lemon.service.impl.AccountServiceImpl.addAccount(cn.lemon.domain.Account)

包名可以使用*号,表示任意包,但是有几级包,需要写几个*

 * *.*.*.*.AccountServiceImpl.addAccount(cn.lemon.domain.Account)

使用..来表示当前包,及其子包

 * cn..AccountServiceImpl.addAccount(cn.lemon.domain.Account)

类名可以使用*号,表示任意类

 * cn..*.addAccount(cn.lemon.domain.Account)

方法名可以使用*号,表示任意方法:

 * cn..*.*( cn.lemon.domain.Account)

参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数:

 * com..*.*(*)

参数列表可以使用..表示有无参数均可,有参数可以是任意类型:

 * com..*.*(..)

全通配方式:

 * *..*.*(..)

注意:
通常情况下,我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层实现类。

execution(* com.lxs.service.impl.*.*(..))

第五步:使用 aop:xxx 配置对应的通知类型

<aop:before method="before" pointcut-ref="serviceMethod"></aop:before><!--织入前置增强-->
<aop:after-returning method="afterReturning" pointcut-ref="serviceMethod" returning="result"></aop:after-returning><!--后置增强-->
<aop:around method="around" pointcut-ref="serviceMethod"></aop:around><!--环绕增强-->
<aop:after-throwing method="exception" pointcut-ref="serviceMethod" <aop:after method="after" pointcut-ref="serviceMethod"></aop:after><!--最终增强-->

Spring AOP 详解以及 Spring 使用 XML 配置 AOP

四、Spring 使用 XML 配置 AOP

1、新建持久层(dao),接口以及实现类

package cn.lemon.dao;

public interface IAccountDao {
    void in(Double money);

    void out(Double money);
}
package cn.lemon.dao.impl;

import cn.lemon.dao.IAccountDao;
import org.springframework.stereotype.Repository;

@Repository
public class AccountDaoImpl implements IAccountDao {
    @Override
    public void in(Double money) {
        System.out.println("成功转入" + money + "元钱");
    }

    @Override
    public void out(Double money) {
        System.out.println("成功转出" + money + "元钱");
    }
}

2、新建业务层(service),接口以及实现类

package cn.lemon.service;

public interface IAccountService {
    void transfer(Double money);
}
package cn.lemon.service.impl;

import cn.lemon.dao.IAccountDao;
import cn.lemon.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountServcieImpl implements IAccountService {
    @Autowired
    private IAccountDao iAccountDao;

    @Override
    public void transfer(Double money) {
        iAccountDao.in(money);
        iAccountDao.out(money);
    }
}

3、增强类

package cn.lemon.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

@Component
public class LoggerAdvice {
    /**
     *
     * @param joinPoint 得到业务方法的信息
     *                  getSignature() 表示签名
     */
    public void before(JoinPoint joinPoint){
        System.out.println("开启事务:前置增强,方法名为:" + joinPoint.getSignature().getName());
    }
    public void afterReturning(JoinPoint joinPoint,Object result){
        System.out.println("提交事务:后置增强,方法名为:" + joinPoint.getSignature().getName() + "返回值为:" + result);
    }

    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕前置增强.......");
        //执行业务方法
        Object result = proceedingJoinPoint.proceed();
        System.out.println("环绕后置增强。。。。。。");
        return result;
    }

    public void exception(JoinPoint joinPoint,Exception e){
        System.out.println("异常增强,异常信息为:" + e.getMessage());
    }

    public void after(JoinPoint joinPoint){
        System.out.println("最终增强,方法名为;" + joinPoint.getSignature().getName());
    }
}

4、AOP 的 XML 配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:component-scan base-package="cn.lemon"></context:component-scan>

    <!--aop 配置
     # proxy-target-class="true":使用CGLib代理,默认是 接口代理(JDK代理)
    -->
    <aop:config proxy-target-class="true">
        <!--切入点表达式:定义那些方法织入增强-->
        <aop:pointcut id="serviceMethod" expression="execution(* cn.lemon.service.impl.*.*(..))"></aop:pointcut><!--切入点-->
        <aop:aspect ref="loggerAdvice">
            <aop:before method="before" pointcut-ref="serviceMethod"></aop:before><!--织入前置增强-->
            <aop:after-returning method="afterReturning" pointcut-ref="serviceMethod" returning="result"></aop:after-returning><!--后置增强-->
            <aop:around method="around" pointcut-ref="serviceMethod"></aop:around><!--环绕增强-->
            <aop:after-throwing method="exception" pointcut-ref="serviceMethod" throwing="e"></aop:after-throwing><!--异常增强-->
            <aop:after method="after" pointcut-ref="serviceMethod"></aop:after><!--最终增强-->
        </aop:aspect>
    </aop:config>
</beans>

5、测试类

package cn.lemon.service.impl;

import cn.lemon.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/applicationContext.xml")
public class AccountServcieImplTest {
    @Autowired
    private IAccountService iAccountService;

    @Test
    public void transfer() {
        iAccountService.transfer(1000d);
    }
}

五、Spring 使用注解&XML 配置 AOP

上一篇:DS内排—2-路归并排序


下一篇:13VUE非父子组件传值