一、AOP簡單介紹前端
二、AOP的概念java
橫切關注點:web
對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之爲橫切關注點(概念)三、AOP開發步驟spring
因此進行AOP編程的關鍵就是定義切入點和定義加強處理,一旦定義了合適的切入點和加強處理,AOP框架將自動生成AOP代理,即:代理對象的方法=加強處理+被代理對象的方法。apache
<!--aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>複製代碼
package com.wxx.demo.aop;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.wxx.demo.model.HelloModel;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
@Component
@Aspect//切面類
public class HelloAspect {
private static final Logger logger = LoggerFactory.getLogger(HelloAspect.class);
//凡是註解了RequestMapping的方法都被攔截
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
private void webPointcut() {
}
//指定切入點
@Pointcut("execution(* com.wxx.demo.controller.HelloController.*(..)))")
private void validate() {
}
//通知advice
@Before("validate()")
public void doBefore(JoinPoint joinPoint) {//經過joinpoint獲取通知的簽名信息如目標名,參數信息
System.out.println("========================前置通知========================");
Object[] args = joinPoint.getArgs();
joinPoint.getThis();//aop代理信息
System.out.println("========================aop代理信息:" + joinPoint.getThis() + "========================");
joinPoint.getTarget();//代理對象
System.out.println("========================aop代理對象:" + joinPoint.getTarget() + "========================");
Signature signature = joinPoint.getSignature();
System.out.println("========================aop通知簽名:" + signature + "========================");
String methodName = signature.getName();//代理方法名
System.out.println("========================aop代理方法名:" + methodName + "========================");
// AOP 代理的名字
System.out.println("========================aop代理的名字:" + signature.getDeclaringTypeName() + "========================");
signature.getDeclaringType();// AOP代理類的類(class)信息
/**
* 經過RequestContextHolder獲取請求信息,如session 信息 ;
* 注:
關於調用 JoinPoint 和 RequestContextHolder。
經過JoinPoint能夠得到通知的簽名信息,如目標方法名、目標方法參數信息等。
經過RequestContextHolder來獲取請求信息,Session信息。
*/
// 獲取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 從requestAttributes中獲取HttpServletRequest信息
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
// 獲取session信息
HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
System.out.println("請求 : " + request + " , HttpSession : " + session);
Enumeration<String> enumerations = request.getParameterNames();
// Map<String,String> parameterMaps=new HashMap<>();
Map<String, String> parameterMaps = Maps.newHashMap();
while (enumerations.hasMoreElements()) {
String parameter = enumerations.nextElement();
parameterMaps.put(parameter, request.getParameter(parameter));
}
// String str=JSON.toJSONString(parameterMaps);
String str = JSON.toJSONString(parameterMaps);// alibaba.fastjson
if (args.length > 0) {
System.out.println("請求參數信息爲 : " + str);
}
}
/**
* 後置返回通知
* 須要注意:
* 若是第一個參數是JoinPoint,則第二個參數是返回值的信息
* 若是參數中的第一個不是JoinPoint,則第一個參數是returning中對應的參數,
* returning 限定了只有目標方法返回值與通知方法相應參數類型時才能
* 執行後置返回通知,不然不執行;
* 對於returning對應的通知方法參數爲Object類型將匹配任何目標返回值
* @param joinPoint
* @param keys
* value = "execution(* com.wxx.demo.controller..*.*(..))"
*/
@AfterReturning(pointcut = "validate()",returning = "keys")
public void doAfterReturn(JoinPoint joinPoint,Object keys){
System.out.println("========================後置返回通知執行========================");
if (keys instanceof HelloModel){
HelloModel hello = (HelloModel) keys;
hello.setHello("hello aop i am @AfterReturning!");
System.out.println("========================後置返回通知修改後的參數:" + keys.toString() + "========================");
}
}
/**
* 後置異常通知
* @param e
*/
@AfterThrowing(pointcut = "validate()",throwing = "e")
public void doAfterThrowing(Exception e){
//if (e instanceof FieldError)
Map<String,Object> map = new HashMap<>();
map.put("resCode",500);
map.put("resMsg","Illegal parameters");
writeContent(JSONObject.toJSONString(map));
}
/**
* 後置最終通知
*
*/
@After(value = "validate()")
public void doAfter(){
System.out.println("========================後置通知最終執行了========================");
}
/**
* 攔截web層異常,
* 記錄異常日誌,並返回友好信息到前端
* 目前只攔截Exception,是否要攔截Error需再作考慮
*
* @param e 異常對象
*/
@AfterThrowing(pointcut = "webPointcut()", throwing = "e")
public void handleThrowing(Exception e) {
e.printStackTrace();
logger.error("發現異常!" + e.getMessage());
logger.error(JSON.toJSONString(e.getStackTrace()));
//這裏輸入友好性信息
Map<String, Object> map = new HashMap<>();
map.put("resCode", "500");
map.put("resMsg", "laoma is dead");
writeContent(JSONObject.toJSONString(map));
}
/**
* 將內容輸入瀏覽器
*
* @param content
*/
private void writeContent(String content) {
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
response.reset();
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "text/plain;charset=UTF-8");
response.setHeader("icop-content-type", "exception");
PrintWriter writer = null;
try {
writer = response.getWriter();
} catch (IOException e) {
e.printStackTrace();
}
writer.print(content);
writer.flush();
writer.close();
}
}
複製代碼
/**
* 參數校驗
* @param helloModel
* @param bindingResult
*/
@GetMapping("/validate")
public HelloModel validate(@Valid HelloModel helloModel, BindingResult bindingResult) {
HelloValidate.validate(bindingResult);
return helloModel;
}複製代碼
========================前置通知========================
========================aop代理信息:com.wxx.demo.controller.HelloController@6cdae95d========================
========================aop代理對象:com.wxx.demo.controller.HelloController@6cdae95d========================
========================aop通知簽名:HelloModel com.wxx.demo.controller.HelloController.validate(HelloModel,BindingResult)========================
========================aop代理方法名:validate========================
========================aop代理的名字:com.wxx.demo.controller.HelloController========================
請求 : org.apache.catalina.connector.RequestFacade@5288ea44 , HttpSession : org.apache.catalina.session.StandardSessionFacade@89f7377
請求參數信息爲 : {"phone":"「123456789」","hello":" "}
========================後置通知最終執行了========================
========================後置返回通知執行========================
========================後置返回通知修改後的參數:HelloModel{hello='hello aop i am @AfterReturning!', phone='「123456789」', email='null'}========================複製代碼
========================前置通知========================
========================aop代理信息:com.wxx.demo.controller.HelloController@6cdae95d========================
========================aop代理對象:com.wxx.demo.controller.HelloController@6cdae95d========================
========================aop通知簽名:HelloModel com.wxx.demo.controller.HelloController.validate(HelloModel,BindingResult)========================
========================aop代理方法名:validate========================
========================aop代理的名字:com.wxx.demo.controller.HelloController========================
請求 : org.apache.catalina.connector.RequestFacade@5288ea44 , HttpSession : org.apache.catalina.session.StandardSessionFacade@89f7377
請求參數信息爲 : {"phone":"","hello":" "}
========================後置通知最終執行了========================
2018-12-28 17:53:19.570 ERROR 10632 --- [nio-8086-exec-2] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/api] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: 手機號不能爲空!] with root cause
java.lang.IllegalArgumentException: 手機號不能爲空!
at org.springframework.util.Assert.isTrue(Assert.java:92) ~[spring-core-4.3.21.RELEASE.jar:4.3.21.RELEASE]
複製代碼
採坑總結:該實例爲springmvc的參數校驗和異常處理用aop統一處理,學習中遇到的坑編程
啓動報錯json
Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut
複製代碼
緣由是配置不一樣通知的時候參數是否配置好比:api