目录
一.什么是AOP?
二.为什么使用AOP?
三.如何使用AOP?
一.什么是AOP?
(1)概述:就是在程序运行的时候,能够动态的将代码切入到类的指定方法,指定位置的编程思想就是面向切面编程。
(2)概念:
- 切面,切入系统的切面,比如事务管理是一个切面,日志分析也是一个切面。
- 连接点,也就是可以横向切入的位置。
- 通知,切面在某个连接点执行的操作(分为Before advice ,After returning advice,After throwing advice,After(finally) advice,Around advice)
- 切点,符合切点表达式的连接点,也就是真正被切入的地方。
- BeforeAdvice:指的是前置增强,指的是在被增强的方法前执行。
- After returning advice: 指的是在被增强的方法执行完返回结果的时候执行
- After throwing advice: 指的是增强的方法报异常的时候执行。
- After(finally) advice: 指的是最终一定会执行的。
- Around advice:可以通过一个在joinpoint执行前后做一些事情的机会,可以决定什么时候,怎么样去执行joinpoint,甚至可以决定是否真的执行joinpoint的方法调用,只执行一次,并不是在joinpoint执行前后各调用一次pointcut,而是在 pointcut中把 joinpoint给around起来,可以通过Around advice 返回一些共享的参数值。
(3)AOP的实现原理:
AOP分为静态AOP和动态AOP。
静态AOP是指AspectJ实现的AOP,他是将切面代码直接编译到Java类文件中。
动态AOP是指将切面代码进行动态织入实现的AOP。
Spring的AOP为动态AOP,实现的技术为: JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术) 。尽管实现技术不一样,但 都是基于代理模式 , 都是生成一个代理对象 。
(4) 什么是JDK提供的动态代理技术?
内容引自:
动态代理的意义在于生成一个 占位(代理对象),用来代理真实的对象,从而控制真实对象的访问。
代理对象的作用就是:在真实对象访问之前或者之后加入对应的 逻辑,或者根据其他规则控制是否使用真实对象。
JDK动态代理是JDK自带的功能,是java.lang.reflect.*包提供的方式,他必须借助一个接口才能提供代理对象。
JDK动态代理技术的实现步骤:
- 创建接口
- 创建接口的实现类:此类的实例对象是真实对象
- 创建代理类:此类必须实现InvocationHandler接口
- 测试
二.为什么使用AOP?
举个例子,我要做一个操作日志的拦截功能,需要记录用户添加和编辑的数据,如果只是一个模块,我们可以在这个模板的添加编辑方法中加入日志操作的代码。每增加一个模块我们我们就要往相应的模块中添加代码,但是如果模块过多的时候,缺点就出来了。代码重用性低,开发效率低,因而这个时候我们就可以用到AOP。
三.如何使用AOP?
功能:我现在要通过AOP来拦截所有的已save方法开头的方法
- (1) 新建一个SpringBoot项目
可以参考我的另外一篇博客。
- (2)引入jar包
<!--引入AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- (3)整个项目路径如下:
- (4) 编写切入类,定义切入点,在切入类中编写公共代码。
package com.example.demo.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 功能描述: 保存用户操作日志
*
* @Author: tanghh18
* @Date: 2020/2/2 12:34
*/
@Aspect
@Component
public class LogAspect {
/**
* 1.添加业务逻辑方法切入点
*/
@Pointcut("execution(* com.example.demo.service.*.save*(..))")
public void insertServiceCall() {
}
/**
* 2.方法调用前触发
*/
@Before(value = "insertServiceCall()")
public void doBefore(JoinPoint joinPoint) {
System.out.println("222----" + "方法之前调用-----" + "@Before");
}
/**
* 3.方法执行后调用
*
* @param joinPoint
* @param rtv
* @throws Throwable
*/
@AfterReturning(value = "insertServiceCall()", argNames = "joinPoint,rtv", returning = "rtv")
public void insertServiceCallCalls(JoinPoint joinPoint, Object rtv) throws Throwable {
System.out.println("333-----方法返回结果后调用" + "------@AfterReturning");
}
/**
* 4.方法执行前后调用
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around(value = "insertServiceCall()")
public Object doBeforeAdvice(ProceedingJoinPoint joinPoint) throws Throwable {// 通过JoinPoint 获取通知的签名信息,如目标方法名,目标方法参数信息等
//获取目标方法的参数信息
Object[] args = joinPoint.getArgs();
System.out.println("444-----方法执行前后调用" + "------@Around");
Object result = joinPoint.proceed(args);
return result;
}
/**
* 5.方法执行后调用(不区分成功或异常)
**/
@After(value = "insertServiceCall()")
public void after() {
System.out.println("555-----方法执行后调用" + "------@After");
}
/**
* 6. 方法抛出异常后执行 记录异常
**/
@AfterThrowing(value = "insertServiceCall()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("666-----方法抛出异常后执行" + "------@AfterThrowing");
}
}
- (5)编写一个简单的业务逻辑类
package com.example.demo.service;
/**
* @Author tanghh
* @Date 2020/2/2 12:36
*/
public interface DemoService {
void saveUserInfo();
}
package com.example.demo.service.impl;
import com.example.demo.service.DemoService;
import org.springframework.stereotype.Service;
/**
* @Author tanghh
* @Date 2020/2/2 12:36
*/
@Service
public class DemoServiceImpl implements DemoService {
@Override
public void saveUserInfo() {
}
}
- (6) 编写一个简单的控制层。
package com.example.demo.controller;
import com.example.demo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author tanghh
* @Date 2020/2/2 12:54
*/
@RestController
public class DemoController {
@Autowired
private DemoService demoService;
@GetMapping(value = "/testApi")
public void testApi() {
demoService.saveUserInfo();
}
}
- (7) 在浏览器上访问如下:
- (8) 访问这个接口,返回结果如下,以上对一个方法的简单拦截。