一、AOP概述

AOP(Aspect Oriented Programming),即面向切面编程;实现AOP技术主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码;开发中使用场景:日志记录,性能统计,安全控制,事务处理,异常处理等等。

  • AOP的组成如下
  • Aspect(切面):通常是一个类,里面可以定义切入点和通知
  • JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用;
  • Advice(通知):AOP在特定的切入点上执行的增强处理,有前置通知(before),后置通知(after),返回通知(afterReturning),异常通知(afterThrowing),环绕通知(around);
  • Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式;
  • AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类;
二、SpringAOP注解实现
  1. AOP注解
    @Aspect:声明切面类
    @Pointcut:声明切入点
    @Before:前置通知
    @After:后置通知
    @AfterReturning:返回通知
    @AfterThrowing:异常通知
    @Around:环绕通知
    @EnableAspectJAutoProxy:切面代理对象的创建
  2. AOP开发步骤
    1)、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
    2)、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
    3)、开启基于注解的aop模式;@EnableAspectJAutoProxy
  3. 代码演示
    (1)项目结构:

    (2)pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ittzg</groupId>
    <artifactId>spring-aspect-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
    <build> 
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

(3)代码

  • 编写业务类(DemoService)
package com.ittzg.service;

import org.springframework.stereotype.Service;

/**
 * @email: tazhigang095@163.com
 * @author: ittzg
 * @date: 2019/5/12 15:55
 * @describe: 业务类创建
 */
@Service
public class DemoService {
    public int printMsg(int flag){
        if(flag==0)
            throw new RuntimeException("打印异常");
        System.out.println("========成功打印信息===========");
        return 1;
    }
}
  • 编写切面类
package com.ittzg.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;

/**
 * @email: tazhigang095@163.com
 * @author: ittzg
 * @date: 2019/5/12 15:49
 * @describe: 切面类创建
 */
@Aspect
public class LogAspect {
    @Pointcut(value = "execution(public int com.ittzg.service.*.*(..))")
    public void pointCut(){}

    @Before(value = "com.ittzg.aspect.LogAspect.pointCut()")
    public void logBefore(){
        System.out.println("log start.......");
    }

    @After(value = "com.ittzg.aspect.LogAspect.pointCut()")
    public void logAfter(){
        System.out.println("log end.......");
    }
    @AfterReturning(value = "com.ittzg.aspect.LogAspect.pointCut()",returning = "result")
    public void logReturning(JoinPoint joinPoint,Object result){
        System.out.println("log returning......result:"+result);
    }
    @AfterThrowing(value = "com.ittzg.aspect.LogAspect.pointCut()",throwing = "exception")
    public void logException(JoinPoint joinPoint,Exception exception){
        System.out.println("log Exception......exception:"+exception);
    }
//    @Around(value = "com.ittzg.aspect.LogAspect.pointCut()")
//    public void logAroud(JoinPoint joinPoint){
//        System.out.println("log aroud.......");
//    }
}
  • 编写配置类
package com.ittzg.config;

import com.ittzg.aspect.LogAspect;
import com.ittzg.service.DemoService;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;

/**
 * @email: tazhigang095@163.com
 * @author: ittzg
 * @date: 2019/5/12 15:56
 * @describe:
 */
@Configuration
@Import(value = {DemoService.class, LogAspect.class})
@EnableAspectJAutoProxy
public class AspectConfig {
}
  • 编写测试类
package com.ittzg;

import com.ittzg.config.AspectConfig;
import com.ittzg.service.DemoService;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @email: tazhigang095@163.com
 * @author: ittzg
 * @date: 2019/5/12 15:48
 * @describe:
 */
public class MainTest {
    @Test
    public void test01(){
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AspectConfig.class);
        DemoService demoService = annotationConfigApplicationContext.getBean(DemoService.class);
        demoService.printMsg(0);
    }
}
  • 测试结果:
1. 当服务出现异常时:
log start.......
log end.......
log Exception......exception:java.lang.RuntimeException: 打印异常
java.lang.RuntimeException: 打印异常
	at com.ittzg.service.DemoService.printMsg(DemoService.java:15)
	.......
2. 服务出现能正常运行时:
log start.......
========成功打印信息===========
log end.......
log returning......result:1
  • 结论:由以上测试结果、发现我们的切面起到了作用;同时前置和后置通知无论服务是否正常运行,都会被执行;而返回通知,只有在正常返回时才能执行,异常通知只有在发生异常时才能执行
三、AOP执行流程
  1. @EnableAspectJAutoProxy 开启AOP功能
  2. @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
  3. AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
  4. 创建容器
  5. 执行目标方法:
    代理对象执行目标方法
    CglibAopProxy.intercept();
    1)、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
    2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行;
    3)、效果:
    正常执行:前置通知-》目标方法-》后置通知-》返回通知
    出现异常:前置通知-》目标方法-》后置通知-》异常通知