一、AOP概述
AOP(Aspect Oriented Programming),即面向切面编程;实现AOP技术主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码;开发中使用场景:日志记录,性能统计,安全控制,事务处理,异常处理等等。
- AOP的组成如下:
- Aspect(切面):通常是一个类,里面可以定义切入点和通知
- JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用;
- Advice(通知):AOP在特定的切入点上执行的增强处理,有前置通知(before),后置通知(after),返回通知(afterReturning),异常通知(afterThrowing),环绕通知(around);
- Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式;
- AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类;
二、SpringAOP注解实现
- AOP注解
@Aspect:声明切面类
@Pointcut:声明切入点
@Before:前置通知
@After:后置通知
@AfterReturning:返回通知
@AfterThrowing:异常通知
@Around:环绕通知
@EnableAspectJAutoProxy:切面代理对象的创建 - AOP开发步骤
1)、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
2)、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
3)、开启基于注解的aop模式;@EnableAspectJAutoProxy - 代码演示
(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执行流程
- @EnableAspectJAutoProxy 开启AOP功能
- @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
- AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
- 创建容器
- 执行目标方法:
代理对象执行目标方法
CglibAopProxy.intercept();
1)、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行;
3)、效果:
正常执行:前置通知-》目标方法-》后置通知-》返回通知
出现异常:前置通知-》目标方法-》后置通知-》异常通知