Spring AOP是Spring框架体系结构中非常重要的功能模块之一,该模块提供了面向切面编程实现。面向切面编程在事务处理、日志记录、安全控制等操作中被广泛使用。
    1.AOP的概念
    AOP(Aspect-Oriented Programming),即面向切面编程。它与OOP(Object-Oriented Programming,面向对象编程) 相辅相成,提供了与 OOP 不同的抽象软件结构的视角。在 OOP 中,以类作为程序的基本单元,而AOP中的基本单元是Aspect(切面)。

    AOP采取横向抽取机制,即将分散在各个方法中的重复代码提取出来,然后在程序编译或运行阶段,再将这些抽取出来的代码应用到需要执行的地方。这种横向抽取机制,采用传统的OOP是无法办到的,因为OOP实现的是父子关系的纵向重用。但是AOP不是OOP的替代品,而是OOP的补充,它们相辅相成。

 

 

AOP的术语

在Spring AOP框架中,涉及以下常用术语。
(1)切面
切面(Aspect)是指封装横切到系统功能(如事务处理)的类。
(2)连接点
连接点(Joinpoint)是指程序运行中的一些时间点,如方法的调用或异常的抛出。
(3)切入点
切入点(Pointcut)是指那些需要处理的连接点。在Spring AOP 中,所有的方法执行都是连接点,而切入点是一个描述信息,它修饰的是连接点,通过切入点确定哪些连接点需要被处理。切面、连接点和切入点的关系如图所示。

 

 

(4)通知(增强处理)
由切面添加到特定的连接点(满足切入点规则)的一段代码,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面开启后,切面的方法。因此,通知是切面的具体实现。
(5)引入
引入(Introduction)允许在现有的实现类中添加自定义的方法和属性。
(6)目标对象
目标对象(Target Object)是指所有被通知的对象。如果AOP 框架使用运行时代理的方式(动态的AOP)来实现切面,那么通知对象总是一个代理对象。
(7)代理
代理(Proxy)是通知应用到目标对象之后,被动态创建的对象。
(8)组入
组入(Weaving)是将切面代码插入到目标对象上,从而生成代理对象的过程。根据不同的实现技术,AOP织入有三种方式:编译器织入,需要有特殊的Java编译器;类装载期织入,需要有特殊的类装载器;动态代理织入,在运行期为目标类添加通知生成子类的方式。Spring AOP框架默认采用动态代理织入,而AspectJ(基于Java语言的AOP框架)采用编译器织入和类装载器织入。
基于注解开发AspectJ

先了解一下Spring的通知类型。根据Spring中通知在目标类方法的连接点位置,可以分为6种如下类型:

1.环绕通知
环绕通知是在目标方法执行前和执行后实施增强,可以应用于日志记录、事务处理等功能。
2.前置通知
前置通知是在目标方法执行前实施增强,可应用于权限管理等功能。
3.后置返回通知
后置返回通知是在目标方法成功执行后实施增强,可应用于关闭流、删除临时文件等功能。

4.后置(最终)通知
后置通知是在目标方法执行后实施增强,与后置返回通知不同的是,不管是否发生异常都要执行该通知,可应用于释放资源。
5.异常通知
异常通知是在方法抛出异常后实施增强,可以应用于处理异常、记录日志等功能。
6.引入通知
引入通知是在目标类中添加一些新的方法和属性,可以应用于修改目标类(增强类)。

 

 

基于注解开发AspectJ的过程
1.使用Eclipse创建Web应用并导入JAR包

2.创建接口及实现类

3.创建切面类

4.创建配置类

5.创建测试类

6.运行测试类
package aspectj.dao;

public interface TestDao {
    public void save();
    public void modify();
    public void delete();

}
package aspectj.dao;

import org.springframework.stereotype.Repository;

@Repository("testDao")
public class TestDaoImpl implements TestDao {
    @Override
    public void save() {
        System.out.println("保存");
    }

    @Override
    public void modify() {
        System.out.println("修改");
    }

    @Override
    public void delete() {
        System.out.println("删除");
    }
}
package aspectj.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面类,在此类中编写各种类型通知
 */
@Aspect // @Aspect声明一个切面
@Component // @Component让此切面成为Spring容器管理的Bean
public class MyAspect {
    /**
     * 定义切入点,通知增强哪些方法。 "execution(* aspectj.dao.*.*(..))" 是定义切入点表达式,
     * 该切入点表达式的意思是匹配aspectj.dao包中任意类的任意方法的执行。
     * 其中execution()是表达式的主体,第一个*表示的是返回类型,使用*代表所有类型;
     * aspectj.dao表示的是需要匹配的包名,后面第二个*表示的是类名,使用*代表匹配包中所有的类; 第三个*表示的是方法名,使用*表示所有方法;
     * 后面(..)表示方法的参数,其中“..”表示任意参数。 另外,注意第一个*与包名之间有一个空格。
     */

    @Pointcut("execution(* aspectj.dao.*.*(..))")
    private void myPointCut() {
    }

    /**
     * 前置通知,使用Joinpoint接口作为参数获得目标对象信息
     */
    @Before("myPointCut()") // myPointCut()是切入点的定义方法
    public void before(JoinPoint jp) {
        System.out.print("前置通知:模拟权限控制");
        System.out.println(",目标类对象:" + jp.getTarget() + ",被增强处理的方法:" + jp.getSignature().getName());
    }

    /**
     * 后置返回通知
     */
    @AfterReturning("myPointCut()")
    public void afterReturning(JoinPoint jp) {
        System.out.print("后置返回通知:" + "模拟删除临时文件");
        System.out.println(",被增强处理的方法:" + jp.getSignature().getName());
    }

    /**
     * 环绕通知 ProceedingJoinPoint是JoinPoint子接口,代表可以执行的目标方法 返回值类型必须是Object
     * 必须一个参数是ProceedingJoinPoint类型 必须throws Throwable
     */
    @Around("myPointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // 开始
        System.out.println("环绕开始:执行目标方法前,模拟开启事务");
        // 执行当前目标方法
        Object obj = pjp.proceed();
        // 结束
        System.out.println("环绕结束:执行目标方法后,模拟关闭事务");
        return obj;
    }

    /**
     * 异常通知
     */
    @AfterThrowing(value = "myPointCut()", throwing = "e")
    public void except(Throwable e) {
        System.out.println("异常通知:" + "程序执行异常" + e.getMessage());
    }

    /**
     * 后置(最终)通知
     */
    @After("myPointCut()")
    public void after() {
        System.out.println("最终通知:模拟释放资源");
    }

}
package aspectj.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration // 声明一个配置类
@ComponentScan("aspectj") // 自动扫描aspectj包下使用的注解
@EnableAspectJAutoProxy // 开启Spring对AspectJ的支持
public class AspectjAOPConfig {

}
package aspectj.config;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import aspectj.dao.TestDao;

public class AOPTest {

    public static void main(String[] args) {
        // 初始化Spring容器ApplicationContext
        AnnotationConfigApplicationContext appCon = new AnnotationConfigApplicationContext(AspectjAOPConfig.class);
        // 从容器中,获取增强后的目标对象
        TestDao testDaoAdvice = appCon.getBean(TestDao.class);
        // 执行方法
        testDaoAdvice.save();
        System.out.println("================");
        testDaoAdvice.modify();
        System.out.println("================");
        testDaoAdvice.delete();
        appCon.close();
    }

}