一、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