SpringBoot 切面通知
- maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- yaml配置不需要开启aop
################################# --- spring - aop --- #################################
spring:
aop:
auto: true
proxy-target-class: true
- 也不需要在启动类开启注解
- 因为SpringBootApplication会自动装配
- @Order(2) 权重 , 数字越小, 权重越高
- @Aspect 切面注解,表示这是一个aop切面类
- @Component Spring组件,被Spring所管理
- @Pointcut 切入点,这里配置监听规则
- @Before 前置通知,执行切入点之前执行
- @After 后置通知,执行切入点之后通知
- @Around 环绕通知 , 切入点执行前后都通知
- @AfterReturning @BeforeThrowing 都属于异常通知, 这里只写了两个
- 切面类;
- 这里只监听了两个方法登录和登出,只是记录用户登录有效时间;
- joinPoint.getSignature().getName(); 获取方法名;
- joinPoint.getSignature();获取签名;
- joinPoint.getArgs();
- 获取所有参数;
- RequestAttributes requestAttributes= RequestContextHolder.getRequestAttributes();获取RequestAttributes ;
- HttpServletRequest request=(HttpServletRequest)requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);运用requestAttributes对象,获取HttpServletRequest;
import com.richfit.user.domain.AuthUser;
import com.richfit.user.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
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.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* Login&Logout log print By CHENYB date 2019-09-03
*/
@Order(2)
@Aspect
@Component
public class LoginAdvice{
private final Logger logger = LoggerFactory.getLogger(LoginAdvice.class);
@Autowired
private UserService userService;
/**
* 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码.
* 使用 @Pointcut 来声明切入点表达式.
* 后面的其他通知直接使用方法名来引用当前的切入点表达式.
* (..)表示任意参数
* 这里路径尽量精准,减少资源浪费,或是错过写什么.
*/
@Pointcut("execution(public String com.richfit.user.controller.UserInfoController.*(..))")
public void declareJointPointExpression() {
}
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint) {
String loginInfoText = "";
//获取被调用的方法名
String methodName = joinPoint.getSignature().getName();
// 获取签名
//Signature signature = joinPoint.getSignature();
// 获取RequestAttributes
RequestAttributes requestAttributes= RequestContextHolder.getRequestAttributes();
// 从requestAttributes中获取HttpServletRequest信息 ,如果参数中有, 在参数中提取也可以
HttpServletRequest request=(HttpServletRequest)requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
//获取参数
Object[] args = joinPoint.getArgs();
if ("login".equals( methodName )){
String loninId = (String) args[1];
String clientIp = getClientIp( request );
loginInfoText = "系统提示 ######### " + new Date( ) + ", 帐号:"+loninId+",正在使用:"+clientIp+" -> 登录服务器 #########";
}
if ("logOut".equals( methodName )){
String userId = (String) args[1];
AuthUser authInfo = userService.getAuthInfo( userId );
String clientIp = getClientIp( request );
loginInfoText = "系统提示 ######### " + new Date( ) + ", 帐号:"+authInfo.getLoginId()+",正在使用:"+clientIp+" -> 已退出登录 #########";
}
logger.info(loginInfoText);
}
//获取用户ip
private String getClientIp(HttpServletRequest request) {
//X-Forwarded-For,不区分大小写
String possibleIpStr = request.getHeader("X-Forwarded-For");
String remoteIp = request.getRemoteAddr();
String clientIp;
if (StringUtils.isNotBlank(possibleIpStr) && !"unknown".equalsIgnoreCase(possibleIpStr)) {
//可能经过好几个转发流程,第一个是用户的真实ip,后面的是转发服务器的ip
clientIp = possibleIpStr.split(",")[0].trim();
} else {
//如果转发头ip为空,说明是直接访问的,没有经过转发
clientIp = remoteIp;
}
return clientIp;
}
}
- 注:想要开启多个通知配置,通知方法的返回值、方法名、参数
- 注:只有环绕通知(@Around)可以使用这个参数“ProceedingJoinPoint”,前、后通知不可以
2021-07-04 20:50:15 ERROR SpringApplication:858 - Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'objectMapperConfigurer' defined in class path resource [springfox/documentation/spring/web/SpringfoxWebMvcConfiguration.class]: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration': BeanPostProcessor before instantiation of bean failed; nested exception is java.lang.IllegalArgumentException: ProceedingJoinPoint is only supported for around advice
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:510)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)
at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:240)
at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:721)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:534)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
chenyb 随笔记录,只为方便自己学习
2019-09-04