AOP(Aspect Oriented Programming):面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP是Spring框架面向切面的编程思想,AOP采用一种称为“横切”的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中。
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<context:component-scan base-package="com.test.spring.aop">
</context:component-scan>
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.SourceLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Optional;
@Aspect
@Component
public class LogAspect {
private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
// java针对处理对象与json之间关系转换用的
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
// 定义切点
private final String POINT_CUT = "execution( * com.lzq..controller.*.*(..))";
@Pointcut(value = POINT_CUT)
public void pointCut() {
}
/**
* 前置增强方法
*
* @param joinPoint
*/
@Before(value = "pointCut()")
public void before(JoinPoint joinPoint) {
logger.info("\n\n----------------- before start -------------");
// 获取目标方法参数信息
Object[] args = joinPoint.getArgs();
if (args.length > 0) {
logger.info("目标方法参数信息:");
Arrays.stream(args).forEach(arg -> {
Optional.ofNullable(arg).ifPresent(a -> logger.info(a.toString()));
});
}
// 获取 AOP 代理对象:CGLib代理和JDK动态代理
logger.info("AOP 代理对象:");
Object thisObj = joinPoint.getThis();
logger.info("-" + thisObj.getClass().getName() + ":" + thisObj.toString());
// 获取被代理的目标对象
logger.info("被代理的目标对象:");
Object object = joinPoint.getTarget();
logger.info("-" + object.getClass().getName() + ":" + object.toString());
// 获取连接点Joint Point类型
logger.info("连接点Joint Point类型:");
String kind = joinPoint.getKind();
logger.info("-" + kind);// -method-execution
// 获取连接点的方法签名对象
logger.info("连接点的方法签名对象:");
Signature signature = joinPoint.getSignature();
logger.info("-" + signature.toLongString());
logger.info("-" + signature.toShortString());
logger.info("-" + signature.toString());
logger.info("-方法名:" + signature.getName());
logger.info("-获取声明类型 方法所在类的class对象" + signature.getDeclaringType().toString());
logger.info("-获取声明类型名" + signature.getDeclaringTypeName());
// 获取连接点方法所在类文件中的位置 打印报异常
logger.info("连接点方法所在类文件中的位置:");
SourceLocation sourceLocation = joinPoint.getSourceLocation();
logger.info("-" + sourceLocation.toString());
// logger.info("-" + sourceLocation.getFileName());
// logger.info("-" + sourceLocation.getLine() + "");
// logger.info("-" + sourceLocation.getWithinType().toString());
// 返回连接点静态部分
logger.info("连接点静态部分:");
JoinPoint.StaticPart staticPart = joinPoint.getStaticPart();
logger.info("-" + staticPart.toLongString());// execution(public com.lzq.selfdiscipline.business.bean.MessageBean com.lzq.selfdiscipline.business.controller.UserFileManagerController.queryFiles(java.lang.String,java.lang.Integer,java.lang.Integer))
// attributes可以获取request信息 session信息等
logger.info("request信息:");
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
logger.info("-" + request.getRequestURL().toString());
logger.info("-" + request.getRemoteAddr());
logger.info("-" + request.getMethod());
logger.info("-" + request.getParameterMap().toString());
logger.info("----------------- before end -------------");
}
/**
* 后置增强,方法正常退出时执行
* 如果第一个参数为JoinPoint,则第二个参数为返回值的信息
* 如果第一个参数不为JoinPoint,则第一个参数为returning中对应的参数
* returning:限定了只有目标方法返回值与通知方法参数类型匹配时才能执行后置返回通知,否则不执行,
* 参数为Object类型将匹配任何目标返回值
*
* @param joinPoint
* @param result
*/
@AfterReturning(value = "pointCut()", returning = "result", argNames = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("----------------- afterReturning -------------" + result);
}
/**
* 异常抛出增强,方法执行抛出异常后执行。
* 定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;
* throwing:限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,
* 对于throwing对应的通知方法参数为 Throwable 类型将匹配任何异常。
*
* @param joinPoint
* @param exception
*/
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Throwable exception) {
System.out.println("----------------- afterThrowing -------------");
if (exception instanceof NullPointerException) {
logger.info("空指针异常");
}
}
/**
* final增强,不管是抛出异常或者正常退出都会执行。
*/
@After(value = "pointCut()")
public void after(JoinPoint joinPoint) {
System.out.println("----------------- after -------------\n\n");
}
/**
* 环绕通知:
* 注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用
* 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
* 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
*
* @param proceedingJoinPoint
* @return
*/
@Around(value = "pointCut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) {
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
// 获取当前方法对象
Method method = methodSignature.getMethod();
Object obj = null;
try {
// 调用下一个advice或者执行目标方法,返回值为目标方法返回值,因此可以通过更改返回值,修改方法的返回值
// 如:修改obj,然后将obj返回
obj = proceedingJoinPoint.proceed();
/*Object[] args = new Object[] {};
// //参数为目标方法的参数 因此可以通过修改参数改变方法入参
obj = proceedingJoinPoint.proceed(args);*/
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return obj;
}
}