在實際的業務系統中,咱們一般都但願程序自動的打印方法的入參和返回值,某些特定的方法可能不想打印返回值(返回數據過大,打印日誌影響效率),特有了下面的實現。java
一、忽略返回值的java註解類web
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 不須要打印返回值的log * @author yangzhilong * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NotPrintResponseLog { public boolean value() default false; }
二、日誌記錄切面類spring
1 import java.io.Serializable; 2 import java.lang.reflect.Method; 3 import java.util.Arrays; 4 import java.util.List; 5 import java.util.concurrent.atomic.AtomicInteger; 6 7 import org.aspectj.lang.ProceedingJoinPoint; 8 import org.aspectj.lang.annotation.Around; 9 import org.aspectj.lang.annotation.Aspect; 10 import org.aspectj.lang.annotation.Pointcut; 11 import org.aspectj.lang.reflect.MethodSignature; 12 import org.springframework.stereotype.Component; 13 import org.springframework.web.multipart.MultipartFile; 14 15 import com.alibaba.fastjson.JSONObject; 16 17 18 import lombok.extern.slf4j.Slf4j; 19 20 21 /** 22 * 記錄Rest和service的方法的入參和返回值
* @author yangzhilong 23 */ 24 @Aspect 25 @Component 26 @Slf4j 27 public class CommonLogAspect { 28 @Pointcut(value = "execution(public * com.tomato..*Impl.*(..))") 29 private void pointcutService() { 30 31 } 32 @Pointcut(value = "execution(public * com.tomato..*Rest*.*(..))") 33 private void pointcutRest() { 34 35 } 36 /* 37 //攔截restmapping註解 38 @Pointcut(value = "execution(@org.springframework.web.bind.annotation.RequestMapping public * com.tomato..*(..))") 39 private void pointCut() { 40 41 } 42 43 //攔截post註解 44 @Pointcut(value = "execution(@org.springframework.web.bind.annotation.PostMapping public * com.tomato..*(..))") 45 private void pointCutPost() { 46 47 } 48 49 //攔截get註解 50 @Pointcut(value = "execution(@org.springframework.web.bind.annotation.GetMapping public * *com.tomato..*(..))") 51 private void pointCutGet() { 52 53 } 54 55 //攔截Delete註解 56 @Pointcut(value = "execution(@org.springframework.web.bind.annotation.DeleteMapping public * com.tomato..*(..))") 57 private void pointCutDelete() { 58 59 } 60 61 //攔截put註解 62 @Pointcut(value = "execution(@org.springframework.web.bind.annotation.PutMapping public * com.tomato..*(..))") 63 private void pointCutPut() { 64 65 }*/ 66 67 @Pointcut("pointcutService()|| pointcutRest()") 68 private void pointcut() { 69 } 70 71 @Around(value = "pointcut()") 72 public Object Around(ProceedingJoinPoint pjp) throws Throwable { 73 String classAndMethodName = null; 74 Method currentMethod = null; 75 76 try { 77 currentMethod = this.getCurrentMethod(pjp); 78 classAndMethodName = this.getCurrentCompleteMethodName(pjp); 79 } catch (Throwable e) { 80 log.error("初始化日誌記錄信息時出錯", e); 81 return pjp.proceed(); 82 } 83 84 // 處理入參 85 this.processBefore(pjp, classAndMethodName); 86 87 Object result = null; 88 try { 89 // 調用目標方法 90 result = pjp.proceed(); 91 } catch (Throwable e) { 92 // 目標方法異常了 93 log.info("end執行方法:{}發生異常,異常簡述:{}", classAndMethodName, e.getMessage()); 94 throw e; 95 } 96 97 // 處理返回值 98 processReturnValue(result, currentMethod, classAndMethodName); 99 100 return result; 101 } 102 103 /** 104 * 獲得當前方法的對象引用 105 * @param pjp 106 * @return 107 * @throws NoSuchMethodException 108 * @throws SecurityException 109 */ 110 private Method getCurrentMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException { 111 MethodSignature mig = (MethodSignature) pjp.getSignature(); 112 return pjp.getTarget().getClass().getMethod(mig.getName(), mig.getParameterTypes()); 113 } 114 115 /** 116 * 獲得完整的方法名 117 * @param pjp 118 * @return 119 */ 120 private String getCurrentCompleteMethodName(ProceedingJoinPoint pjp) { 121 return pjp.getTarget().getClass() + "的" + pjp.getSignature().getName() + "方法"; 122 } 123 124 /** 125 * 處理入參打印 126 * @param pjp 127 * @param classAndMethodName 128 */ 129 private void processBefore(ProceedingJoinPoint pjp, String classAndMethodName) { 130 try { 131 if(null==pjp.getArgs() || pjp.getArgs().length==0) { 132 log.info("begin執行方法:{},方法無入參", classAndMethodName); 133 } else { 134 if(pjp.getArgs().length == 1) { 135 if (pjp.getArgs()[0] instanceof Serializable) { 136 if(isFile(pjp.getArgs()[0])) { 137 log.info("begin執行方法:{},入參爲文件類型,文件名爲:{}", classAndMethodName, getFileName(pjp.getArgs()[0])); 138 } else { 139 log.info("begin執行方法:{},入參爲:{}", classAndMethodName, JSONObject.toJSONString(pjp.getArgs()[0])); 140 } 141 } 142 } else { 143 log.info("begin執行方法:{},有多個入參", classAndMethodName); 144 List<Object> list = Arrays.asList(pjp.getArgs()); 145 final AtomicInteger index = new AtomicInteger(1); 146 list.stream().filter(x -> x instanceof Serializable).forEach(x -> { 147 if(isFile(x)) { 148 log.info("入參{}:{}", index.get(), getFileName(x)); 149 } else { 150 log.info("入參{}:{}", index.get(), JSONObject.toJSONString(x)); 151 } 152 index.incrementAndGet(); 153 }); 154 } 155 } 156 } catch (Throwable e) { 157 log.error("記錄入參日誌的時候出錯:", e); 158 } 159 } 160 161 /** 162 * 處理返回值 163 * @param result 164 * @param currentMethod 165 * @param classAndMethodName 166 */ 167 private void processReturnValue(Object result, Method currentMethod, String classAndMethodName) { 168 if(null == result) { 169 return; 170 } 171 try { 172 if(!currentMethod.isAnnotationPresent(NotPrintResponseLog.class) && result instanceof Serializable) { 173 log.info("end執行方法:{},返回結果:{}", classAndMethodName, JSONObject.toJSONString(result)); 174 } 175 } catch (Throwable e) { 176 log.error("記錄返回日誌的時候出錯:", e); 177 } 178 179 } 180 181 /** 182 * 獲取文件上傳的文件名 183 * @param file 184 * @return 185 */ 186 private String getFileName(Object file) { 187 return null==file ? "空文件" : ((MultipartFile)file).getName(); 188 } 189 190 /** 191 * 判斷是不是文件類型 192 * @param obj 193 * @return 194 */ 195 private boolean isFile(Object obj) { 196 return obj instanceof MultipartFile; 197 } 198 }
20180530補充:json
在aop的邏輯內,先走@Around註解的方法。而後是@Before註解的方法,而後這兩個都經過了,走核心代碼,核心代碼走完,不管核心有沒有返回值,都會走@After方法。而後若是程序無異常,正常返回就走@AfterReturn,有異常就走@AfterThrowing。app