springboot整合aop实现网站访问日志记录
目的:
统一日志输出格式,统计访问网站的ip.
思路:
1、针对不同的调用场景定义不同的注解,目前想的是接口层和服务层。
2、我设想的接口层和服务层的区别在于:
(1)接口层可以打印客户端IP,而服务层不需要
(2)接口层的异常需要统一处理并返回,而服务层的异常只需要向上抛出即可
3、就像Spring中的@Controller、@Service、@Repository注解那样,虽然作用是一样的,但是不同的注解用在不同的地方显得很清晰,层次感一下就出来了
4、AOP去拦截特定注解的方法调用
5、为了简化使用者的操作,采用Spring Boot自动配置
如果要直接用@Aspect注解的话,要在spring的配置文件中加入
<aop:aspectj-autoproxy />
那么我们这里要不要在程序的主类中增加@EnableAspectJAutoProxy来启用呢?实际并不需要
好的也就是说,只要引入SpringAOP相关的jar包依赖,我们就可以开始相关的Aspet的编程了。有时候拦截器也是可以实现的,但是如果我们采用的是post请求,如果使用拦截器就需要从报文中读取数据,
其实就是io流,只能获取一次,所以在controller中无法获取数据,所以拦截器的方法是不可行的.
首先需要引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
controller中:
package com.cxy.shibernate.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; /*** * @ClassName: HelloController * @Description: * @Auther: cxy * @Date: 2019/5/19:18:06 * @version : V1.0 */ @Controller public class HelloController { @RequestMapping(value = "/hello", method = RequestMethod.GET) @ResponseBody public String hello(@RequestParam String name) { return "Hello " + name; } }
工具类:
package com.cxy.shibernate.controller; /*** * @ClassName: HttpContextUtils * @Description: * @Auther: cxy * @Date: 2019/5/19:18:17 * @version : V1.0 */ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; public class HttpContextUtils { public static HttpServletRequest getHttpServletRequest() { ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); return servletRequestAttributes.getRequest(); } //获取ip public static String getIpAddress() { HttpServletRequest request = getHttpServletRequest(); String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } }else if (ip != null && ip.length() > 15) { String[] ips = ip.split(","); for (int index = 0; index < ips.length; index++) { String strIp = (String) ips[index]; if (!("unknown".equalsIgnoreCase(strIp))) { ip = strIp; break; } } } return ip; } }
切面:
package com.cxy.shibernate.controller; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; /*** * @ClassName: WebLogAspect * @Description: * @Auther: cxy * @Date: 2019/5/19:18:08 * @version : V1.0 */ @Aspect @Order(5) @Component public class WebLogAspect { private final Logger logger = LoggerFactory.getLogger(WebLogAspect.class); ThreadLocal<Long> startTime = new ThreadLocal<>(); /** 第一个*表示返回任何类型,com.cxy.shibernate.controller下任何类,任何方法,任何参数 也可以加入参数限定例如com.cxy.shibernate.controller.*.*(..)&&args(name,..) 下面那中表示方法也是对的,表示com.cxy.shibernate.下面任何子包下任何方法,任何参数 **/ @Pointcut("execution(public * com.cxy.shibernate..*.*(..))") public void webLog(){} @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { startTime.set(System.currentTimeMillis()); // 接收到请求,记录请求内容 HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); String ipAddress = HttpContextUtils.getIpAddress(); // 记录下请求内容 logger.info("URL : " + request.getRequestURL().toString()); logger.info("HTTP_METHOD : " + request.getMethod()); logger.info("IP : " + request.getRemoteAddr()); logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs())); logger.info("ip:"+ipAddress); } @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) throws Throwable { // 处理完请求,返回内容 logger.info("RESPONSE : " + ret); logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get())); } }
启动类:
package com.cxy.shibernate; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ShibernateApplication { public static void main(String[] args) { SpringApplication.run(ShibernateApplication.class, args); } }
在application.yml不需要配置任何东西
启动项目:
当然可以配置文件输出的级别,制定输出的文件夹