目录
前言
上一节我们学习了Spring IOC容器与Bean管理,通过学习我们可以使用Spring进行简单的开发,接下来我们继续学习Spring AOP面向切面编程。
学习内容
什么是AOP?
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP百度百科
AOP关键概念
注解 | 说明 |
---|---|
Aspect | 切面,具体的可插拔组件功能类,通常一个切面只实现一个通用功能 |
Target Class/Method | 目标类、目标方法,指真正要执行与业务相关的方法 |
PointCut | 切入点,使用execution表达式说明切面要作用在系统的哪些类上 |
JoinPoint | 连接点,切面运行过程中是包含了目标类/方法元数据的对象 |
Advice | 通知,说明具体的切面执行时机,Spring包含了五种不同类型的通知 |
AOP配置过程
- 依赖AspectJ
- 实现切面类/方法
- 配置asoect Bean
- 定义PointCut
- 配置Advice
JoinPoint对象
注解 | 说明 |
---|---|
Object getTarget() | 获取IOC容器类目标对象 |
Signature getSignature() | 获取目标方法 |
Object[] getArgs() | 获取目标方法参数 |
PointCut切点表达式
五种通知类型
前置通知
目标方法运行前执行。
<aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
返回后通知
目标方法返回数据后执行。
<aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
异常通知
目标方法抛出异常后执行。
<aop:after-throwing method="doAfterThrowing" throwing="th" pointcut-ref="pointcut"/>
后置通知、
目标方法运行后执行。
<aop:after method="doAfter" pointcut-ref="pointcut"></aop:after>
环绕通知
最强大通知,自定义通知执行时间,可决定目标方法是否运行。
<aop:around method="check" pointcut-ref="pointcut"/>
利用注解配置AOP
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--初始化IoC容器-->
<context:component-scan base-package="com.imooc"/>
<!--启用Spring AOP注解模式-->
<aop:aspectj-autoproxy/>
</beans>
package com.imooc.spring.aop.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component //标记当前类为组件
@Aspect //说明当前类是切面类
public class MethodChecker {
//环绕通知,参数为PointCut切点表达式
@Around("execution(* com.imooc..*Service.*(..))")
//ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
public Object check(ProceedingJoinPoint pjp) throws Throwable {
try {
long startTime = new Date().getTime();
Object ret = pjp.proceed();//执行目标方法
long endTime = new Date().getTime();
long duration = endTime - startTime; //执行时长
if(duration >= 1000){
String className = pjp.getTarget().getClass().getName();
String methodName = pjp.getSignature().getName();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String now = sdf.format(new Date());
System.out.println("=======" + now + ":" + className + "." + methodName + "(" + duration + "ms)======");
}
return ret;
} catch (Throwable throwable) {
System.out.println("Exception message:" + throwable.getMessage());
throw throwable;
}
}
}
AOP实现原理
Spring基于代理模式实现功能动态扩展,包含两种形式:
- 目标类拥有接口,通过JDK动态代理实现功能扩展;
- 目标类没有接口,通过CGLib组件实现功能扩展;
代理模式通过代理对象对原对象的实现功能扩展;
目标类拥有接口
静态代理
JDK动态代理
基于反射机制实现。
package com.imooc.spring.aop.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* InvocationHandler是JDK提供的反射类,用于在JDK动态代理中对目标方法进行增强
* InvocationHandler实现类与切面类的环绕通知类似
*/
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;//目标对象
private ProxyInvocationHandler(Object target){
this.target = target;
}
/**
* 在invoke()方法对目标方法进行增强
* @param proxy 代理类对象
* @param method 目标方法对象
* @param args 目标方法实参
* @return 目标方法运行后返回值
* @throws Throwable 目标方法抛出的异常
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("=====" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) +"=========");
Object ret = method.invoke(target, args);//调用目标方法,ProceedingJoinPoint.proceed()
return ret;
}
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler(userService);
//动态创建代理类
UserService userServiceProxy = (UserService)Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
invocationHandler);
userServiceProxy.createUser();
//动态代理,必须实现接口才可以运行
EmployeeService employeeService = new EmployeeServiceImpl();
EmployeeService employeeServiceProxy = (EmployeeService)Proxy.newProxyInstance(employeeService.getClass().getClassLoader(),
employeeService.getClass().getInterfaces(),
new ProxyInvocationHandler(employeeService));
employeeServiceProxy.createEmployee();
}
}
目标类没有接口
总结
本次主要学习了Spring的AOP,相信大家看完以后基本明白了AOP是个什么,怎么使用,如果你发现文章存在问题,欢迎评论区交流哦~