一、什么是aop?
Aspect Oriented Programming的缩写,面向切面编程,通过预编译和动态代理实现程序功能的
统一维护的一种技术
主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等。
二、SpringAOP的实现方式
下边这两种Spring都是支持的
2.1预编译
-AspectJ 完整的面向切面编程解决方案--》spring不是完整的解决方案,不过spring提供比较好的实现方式,当然spring是同时也是支持这种方式的,这也是一种常用的方式
2.2运行期间动态代理(JDK动态代理,CGLib动态代理)
-SpringAop,JbossAop
Spring的AOP使用纯java实现,无需特殊的编译过程,不需要控制类的加载器层次,目前只支持方法的执行的连接点(通知Spring Bean某个方法执行)
不是为了提供完整AOP实现;而是侧重于一种AOP于IOC容器之间的整合,SpringAOP不会AspectJ(完整的AOP解决方案)竞争
Spring没有使用AspectJ的时候,也可以通过如下方式实现AOP
Spring AOP 默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者集合)都可以被代理
Spring AOP 中也可以使用CGLIB代理(如果一个业务对象没有实现一个接口)
有接口的:使用JDK的动态里
无接口的:使用CGLIB代理
三、SpringBoot中AspectJ 实现
AspectJ是一个AOP框架,由于SpringAOP的配置过于繁琐,因此使用了AspectJ依赖注解开发
1、Aspecj依赖,此处省略了Spring相关依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
2.AspactJ框架常用注解
@PonitCut // 声明切入点的注解
@Before // 声明前置通知注解
@After // 声明后置通知注解(原始方法执行正常或者非正常执行都会进入)
@AfterReturing // 声明后置通知注解(原始方法必须正常执行)
@AfterThrowing // 声明异常通知
@Around // 环绕通知注解
3.实例
import com.pingan.haofang.standard.api.params.domain.ApiParams;
import com.pingan.haofang.standard.common.annotation.aspect.TableName;
import com.pingan.haofang.standard.common.context.ManageThreadContext;
import com.pingan.haofang.standard.common.context.ManageUser;
import com.pingan.haofang.standard.configmapping.domain.ConfigMapping;
import com.pingan.haofang.standard.configmapping.service.ConfigMappingService;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.List;
@Aspect
@Component
public class AddConfigMappingAspect {
private final Logger log = LoggerFactory.getLogger(AddConfigMappingAspect.class);
@Autowired
ConfigMappingService configMappingService;
@Pointcut("@annotation(com.pingan.haofang.standard.common.annotation.aspect.AddConfigMapping)")
public void addPoint() {
}
@AfterReturning(returning = "returnValue", pointcut = "addPoint()")
public void doAfterReturning(Object returnValue) throws Throwable {
Field field = null;
List<ApiParams> paramsList = null;
Long id = 0l;
String tableName = "";
if (returnValue instanceof List) {//1.0 返回值为集合
paramsList = (List) returnValue;
if (paramsList != null && paramsList.size() != 0) {
TableName annotation0 = paramsList.get(0).getClass().getAnnotation(TableName.class);
tableName = annotation0.name();
log.info("tableName:" + tableName);
}
} else {
//1.先利用反射获取拦截的返回值
String objectName = returnValue.getClass().getSimpleName();
field = returnValue.getClass().getDeclaredField("id");
field.setAccessible(true);
id = (Long) field.get(returnValue);
TableName annotation = returnValue.getClass().getAnnotation(TableName.class);
tableName = annotation.name();
log.info("tableName:" + tableName + " id:" + id);
}
//2.获取当前版本和用户信息
ManageUser currentUser = ManageThreadContext.getManageVisitor();
if (paramsList != null && paramsList.size() != 0) {//当返回值为集合
for (ApiParams apiParam : paramsList) {
ConfigMapping configMapping = new ConfigMapping();
configMapping.setObjectId(apiParam.getId());
configMapping.setTableName(tableName);
configMapping.setAppCode(currentUser.getCurrentApp());
configMapping.setVersionNo(currentUser.getCurrentAppVersion());
configMapping.setCreateTime(new Date());
configMapping.setUpdateTime(new Date());
configMappingService.insert(configMapping);
}
} else {
//3.设置到配置映射表中
if (!id.equals(0l)) {
ConfigMapping configMapping = new ConfigMapping();
configMapping.setObjectId(id);
configMapping.setTableName(tableName);
configMapping.setAppCode(currentUser.getCurrentApp());
configMapping.setVersionNo(currentUser.getCurrentAppVersion());
configMapping.setCreateTime(new Date());
configMapping.setUpdateTime(new Date());
configMappingService.insert(configMapping);
}
}
}
}
改段代码完成的是在添加了拦截注解的方法上,拦截到方法返回值,将返回值id和当前用户信息保存到关系映射表中。
注意:
切入点不仅支持注解,也支持表达式,具体语法参照https://blog.csdn.net/zhengchao1991/article/details/53391244;
要了解Spring配置文件方式实现,请参照https://blog.csdn.net/u014292162/article/details/52504633。