目的:
統一日志輸出格式,統計訪問網站的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不需要配置任何東西
啟動項目:
當然可以配置文件輸出的級別,制定輸出的文件夾