首页 > 代码库 > Spring总结 3.AOP(xml)

Spring总结 3.AOP(xml)

本随笔内容要点如下:

  • 什么是AOP
  • AOP术语解释
  • Spring中AOP的xml实现

一、什么是AOP

  AOP(Aspect Oriented Programming),即面向切面编程。那什么是面向切面编程呢?切面又是什么呢?

  如下图,本来存在ServiceA、ServiceB、ServiceC的,一刀把它们给切了。那这个面就是切面,切面切面,就是切开的面。

技术分享

  面向切面编程呢,就是在这个大切面上插入代码,使得ServiceA、ServiceB、ServiceC都能够实现插入的逻辑。在编码中,经常会在一个类中抽取公共代码形成一个独立的方法。而面向切面编程呢,则是抽取多个类中多个方法的相同代码形成一个独立的模块,而这些相同的代码就是横切关注点。例如事务,我们可能会在这些Service中的方法添加事务,而这些事务又与具体的业务逻辑无关,不属于Service的职责,那我们可以将它抽取出来形成一个类,在通过AOP编程来实现原本的逻辑。如下图:

技术分享

二、AOP术语解释

  • 通知(Advice):要被织入到指定位置的代码,上图的抽取出来的事务就是通知。通知除了定义了切面做什么还定义了这些它什么时候被使用。是在执行方法之前还是执行完方法还是抛出异常后。
  • 连接点(Join point):切面代码被织入的位置。上图事务代码要被织入的方法就是连接点了
  • 切点(Pointcut):用来描述拥有哪些连接点,描述了一个连接点的集合
  • 切面(Aspect):通知和切点的结合
  • 织入(Weaving):把切面应用到指定对象并产生代理对象的过程,抽象地理解为把切面代码插入到指定对象的指定位置

  对于织入,有一下几种形式:

  • 编译器织入:在编译前真真正正地代码插入到指定位置。但这种需要特定的编译器
  • 类加载器织入:当类加载器加载目标类时再织入,即使用字节码来增强。需要特定的类加载器
  • 运行期织入:在运行期间动态地织入,通过代理技术生成代理对象,而代理对象是织入完代码后的对象

  Spring只支持运行期织入,AspectJ三种都支持

三、Spring中AOP的实现

  本随笔使用xml实现,下篇随笔再使用注解实现。

  Spring中有两种方式来实现代理。第一种是Java自带的代理,它要求被代理类必须实现接口。第二种是使用了CGLib,即使被代理类没有实现接口也能被代理。除了引入基础的spring jar包之外,还需要引入aspectjweaver包。我使用的spring版本时4.3.2.RELEASE,当使用aspectjweaver1.8.9时会报错,降到1.8.6就可以正常使用了。

  首先定义一个接口IService以及其实现类ServiceImpl

public interface IService {
    public void doSomething(int num);
}


public class ServiceImpl implements IService {
    @Override
    public void doSomething(int num) {
        System.out.println("doSomething --" + num);
    }
}

  把公共代码提取出来作为一个单独的模块即切面,那就需要定义一个切面,切面也是一个普通的bean,如下:

public class Aspect {
    public void before() {
        System.out.println("before");
    }
}

  接着写配置文件

<?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: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/aop
        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
        ">

    <bean id="aspect" class="cn.powerfully.aspect.Aspect" />

    <bean id="service" class="cn.powerfully.service.ServiceImpl" />

    <aop:config>
        <aop:aspect ref="aspect">
            <aop:pointcut expression="execution(* cn.powerfully.service.*.*(..))"
                id="p" />
            <aop:before method="before" pointcut-ref="p" />
        </aop:aspect>
    </aop:config>

</beans>

  首先先使用<bean>标签定义两个类,接着使用<aop:config>标签,关于切面的都要放在里面。

  定义切面使用<aop:aspect>,切面呢,是由通知和切点组成的,所以定义通知和切点。<aop:before>用来定义一个前置通知,即在执行代码前执行。

  除了前置通知,spring还提供了一下几种通知:

  • 后置通知(after)
  • 返回通知(after-returning)
  • 异常通知(after-throwing)
  • 环绕通知(around)

  除了环绕通知外,其他通知的使用都类似前置通知。环绕通知的使用如下:

  切面类中:

public Object around(ProceedingJoinPoint pjp) throws Throwable {
    long time = System.currentTimeMillis();
    Object obj = null;
    obj = pjp.proceed();
    System.out.println(System.currentTimeMillis() - time);
    return obj;
}

  该方法有类型ProceedingJoinPoint的参数,通过调用该对象中的proceed()方法可以继续执行被代理对象的逻辑代码。xml配置代码类似前置通知配置。

Spring总结 3.AOP(xml)