spring 自定義註解實現日誌統一處理
阿新 • • 發佈:2019-01-11
需求:
通過註解的方式 統一處理controller和service的日誌(實現上可能不太嚴謹,主要是實現流程)
原理:
先自定義註解。用aop切面攔截方法的使用,看是否有對應的自定義的註解,如果有,在切面中進行日誌的統一列印,可以獲取到加了註解方法的類名、方法名、引數。
如果想每個方法傳進來不同資訊,可以在自定義註解裡寫上引數,這樣在使用時就可以帶進來不同資訊。例如,spring自帶的註解@Resource(name=“”)。
1.controller、service自定義註解
package com.qunar.fresh.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * ControllerLog * * @author chenliclchen * @date 17-10-12 上午11:35 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ControllerLog { String description() default ""; }
可以通過需求加多個如同description的註解。package com.qunar.fresh.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * ServiceLog * * @author chenliclchen * @date 17-10-12 上午11:34 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ServiceLog { String description() default ""; }
2.寫aop進行切面
只寫了before,如果有需求還可以寫上after等。package com.qunar.fresh.aop; import com.qunar.fresh.annotation.ControllerLog; import com.qunar.fresh.annotation.ServiceLog; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.text.StrBuilder; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.Arrays; /** * LogAop * * @author chenliclchen * @date 17-10-12 上午11:40 */ @Slf4j @Component @Aspect public class LogAop { @Pointcut("@annotation(com.qunar.fresh.annotation.ServiceLog)") public void serviceAspect(){ log.info("service 日誌"); } @Pointcut("@annotation(com.qunar.fresh.annotation.ControllerLog)") // public void controllerAspect(){ log.info("controller 日誌"); } @Before("controllerAspect() || serviceAspect()") public void doBefore(JoinPoint joinPoint){ String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); String description = getDescription(joinPoint); Object[] args = joinPoint.getArgs(); String logResult = new StringBuilder().append(description).append("操作: ") .append(className).append("類的").append(methodName).append("()方法被請求,引數為: ") .append(Arrays.toString(args)).toString(); log.info(logResult); } // @AfterThrowing(pointcut = "controllerAspect() || serviceAspect()", throwing = "e") // public void doAfterThrowing(JoinPoint joinPoint, Throwable e){ // log.error("統一處理異常日誌 {}", e); // } private String getDescription(JoinPoint joinPoint){ Class<?> className = joinPoint.getTarget().getClass(); //得到類的所有方法 Method[] methods = className.getMethods(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); //在類所有方法中找到對應的被攔截到的方法,通過方法的名字和引數的個數。 for(Method method: methods){ if(method.getName().equals(methodName)){ Class<?>[] parameterTypes = method.getParameterTypes(); //引數個數是否相等。 if(parameterTypes.length == args.length){ ControllerLog controllerLog = method.getAnnotation(ControllerLog.class); if(controllerLog != null){ return controllerLog.description(); }else{ ServiceLog serviceLog = method.getAnnotation(ServiceLog.class); return serviceLog != null? serviceLog.description(): ""; } } } } return ""; } }
3.xml配置
<aop:aspectj-autoproxy proxy-target-class="true"/>
需要注意的是,如果專案的spring-mvc.xml和applicationContext.xml是兩個檔案,而你的切面是同時面向controller和service的,
兩個配置檔案裡都需要加上這行配置。
4.使用
package com.qunar.fresh.controller;
import com.qunar.fresh.annotation.ControllerLog;
import com.qunar.fresh.annotation.LoginRequired;
import com.qunar.fresh.service.TestService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
/**
* TestController
*
* @author chenliclchen
* @date 17-10-11 下午9:05
*/
//@LoginRequired
@Controller
@RequestMapping("/test")
public class TestController {
@Resource
TestService testService;
@RequestMapping("/one/{name}")
@ControllerLog(description="測試用aop統一處理日誌")
public String test(@PathVariable String name){
testService.printHello(name);
return "test";
}
}
package com.qunar.fresh.service;
/**
* TestService
*
* @author chenliclchen
* @date 17-10-12 下午6:32
*/
public interface TestService {
void printHello( String name);
}
package com.qunar.fresh.service.impl;
import com.qunar.fresh.annotation.ServiceLog;
import com.qunar.fresh.service.TestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* TestServiceImpl
*
* @author chenliclchen
* @date 17-10-12 下午6:33
*/
@Slf4j
@Service
public class TestServiceImpl implements TestService {
@Override
@ServiceLog(description = "測試aop統一處理service日誌輸出")
public void printHello(String name) {
log.info("hello {}", name);
}
}
這樣,當瀏覽器輸入:http://localhost:8080/test/one/who 時,會有如下輸出日誌:INFO com.qunar.fresh.aop.LogAop - 測試用aop統一處理日誌操作: com.qunar.fresh.controller.TestController類的test()方法被請求,引數為: [who]
INFO com.qunar.fresh.aop.LogAop - 測試aop統一處理service日誌輸出操作: com.qunar.fresh.service.impl.TestServiceImpl類的printHello()方法被請求,引數為: [who]