springboot中定義切面
技術標籤:springbootspringaop
1.基礎概念
1、切面(Aspect)
首先要理解‘切’字,需要把物件想象成一個立方體,傳統的面向物件變成思維,類定義完成之後(封裝)。每次例項化一個物件,對類定義中的成員變數賦值,就相當於對這個立方體進行了一個定義,定義完成之後,那個物件就在那裡,不卑不亢,不悲不喜,等著被使用,等著被回收。
面向切面程式設計則是指,對於一個我們已經封裝好的類,我們可以在編譯期間或在執行期間,對其進行切割,把立方體切開,在原有的方法裡面新增(織入)一些新的程式碼,對原有的方法程式碼進行一次增強處理。而那些增強部分的程式碼,就被稱之為切面,如下面程式碼例項中的通用日誌處理程式碼,常見的還有事務處理、許可權認證等等。
2、切入點(PointCut)
要對哪些類中的哪些方法進行增強,進行切割,指的是被增強的方法。即要切哪些東西。
3、連線點(JoinPoint)
我們知道了要切哪些方法後,剩下的就是什麼時候切,在原方法的哪一個執行階段加入增加程式碼,這個就是連線點。如方法呼叫前,方法呼叫後,發生異常時等等。
4、通知(Advice)
通知被織入方法,改如何被增強。定義切面的具體實現。那麼這裡面就涉及到一個問題,空間(切哪裡)和時間(什麼時候切,在何時加入增加程式碼),空間我們已經知道了就是切入點中定義的方法,而什麼時候切,則是連線點的概念,如下面例項中,通用日誌處理(切面),@Pointcut規則中指明的方法即為切入點,@Before、@After是連線點,而下面的程式碼就是對應通知。
@Before(“cutMethod()”)
public void begin() {
System.out.println("@Before lingyejun blog logger : begin");
}
5、目標物件(Target Object)
被一個或多個切面所通知的物件,即為目標物件。
6、AOP代理物件(AOP Proxy Object)
AOP代理是AOP框架所生成的物件,該物件是目標物件的代理物件。代理物件能夠在目標物件的基礎上,在相應的連線點上呼叫通知。
7、織入(Weaving)
將切面切入到目標方法之中,使目標方法得到增強的過程被稱之為織入。
2.例項
(1)依賴包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
(2)自定義註解
@Target({ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
//模組
String title() default "";
//業務
BusinessType businessType() default BusinessType.OTHER;
//操作人類別
OperatorType operatorType() default OperatorType.MANAGE;
//是否儲存請求的引數
boolean isSaveRequestData() default true;
}
(3)切面
@Component
@Aspect
@Slf4j
public class LogAspect {
@Pointcut("@annotation(com.mysuper.demo.annotation.Log)")
public void logPointCut(){
}
//處理完請求後執行
@AfterReturning(pointcut = "logPointCut()",returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint,Object jsonResult){
log.info("注意,這個模組有所操作");
handleLog(joinPoint,null,jsonResult);
}
@Before("logPointCut()")
public void doBefore(){
log.info("before");
}
public void handleLog(final JoinPoint joinPoint,final Exception e, Object jsonResult){
Log controllerLog = getAnnotation(joinPoint);
if(controllerLog == null){
return;
}
//獲取當前登入使用者
LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
//資料庫日誌
SysOperLog operLog = new SysOperLog();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
//請求的地址
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
operLog.setOperIp(ip);
//返回引數
operLog.setJsonResult(JSON.toJSONString(jsonResult));
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
if(loginUser != null){
operLog.setOperName(loginUser.getUsername());
}
if(e != null){
operLog.setStatus(BusinessStatus.FAIL.ordinal());
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
}
//設定方法名稱
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
operLog.setMethod(className + "." + methodName);
//設定請求方式
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
//處理設定註解上的引數
getControllerMethodDescription(joinPoint, controllerLog, operLog);
// 儲存資料庫
AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
}
//獲取註解中對方法得描述資訊
public void getControllerMethodDescription(JoinPoint joinPoint,Log log,SysOperLog operLog){
//設定action動作
operLog.setBusinessType(log.businessType().ordinal());
//設定標題
operLog.setTitle(log.title());
//設定操作人類別
operLog.setOperatorType(log.operatorType().ordinal());
// 是否需要儲存request,引數和值
if (log.isSaveRequestData())
{
// 獲取引數的資訊,傳入到資料庫中。
setRequestValue(joinPoint, operLog);
}
}
//獲取註解
public Log getAnnotation(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if(method != null){
return method.getAnnotation(Log.class);
}
return null;
}
//獲取請求的引數,存放到log中
private void setRequestValue(JoinPoint joinPoint,SysOperLog operLog){
String requestMethod = operLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
{
String params = argsArrayToString(joinPoint.getArgs());
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
}
else
{
Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
}
}
/**
* 引數拼裝
*/
private String argsArrayToString(Object[] paramsArray)
{
String params = "";
if (paramsArray != null && paramsArray.length > 0)
{
for (int i = 0; i < paramsArray.length; i++)
{
if (!isFilterObject(paramsArray[i]))
{
Object jsonObj = JSON.toJSON(paramsArray[i]);
params += jsonObj.toString() + " ";
}
}
}
return params.trim();
}
/**
* 判斷是否需要過濾的物件。
*
* @param o 物件資訊。
* @return 如果是需要過濾的物件,則返回true;否則返回false。
*/
public boolean isFilterObject(final Object o)
{
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
}
}
(4)應用
@RestController
public class LoginController {
@Autowired
private ISysLoginService sysLoginService;
@Log(title = "登入模組",businessType = BusinessType.OTHER)
@RequestMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody){
AjaxResult ajaxResult = new AjaxResult();
String token = sysLoginService.Login(loginBody.getUsername(),loginBody.getPassword(),loginBody.getCode(),loginBody.getUuid());
ajaxResult.put(Constants.TOKEN,token);
return ajaxResult;
}
}