使用SpringAOP切面实现对controller的拦截,并对url,参数和返回值记录
2017-11-14 11:40
381 查看
做这个功能之前 先讲一下AOP的环绕通知,因为这个功能我之前也想用springMVC的拦截器实现
对于环绕通知来说, 连接点的参数类型必须是 ProceedingJoinPoint . 它是 JoinPoint 的子接口, 允许控制何时执行, 是否执行连接点.
在环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法. 如果忘记这样做就会导致通知被执行了, 但目标方法没有被执行.
注意: 环绕通知的方法需要返回目标方法执行之后的结果, 即调用 joinPoint.proceed(); 的返回值, 否则会出现空指针异常
(1)环绕通知需要携带 ProceedingJoinPoint 类型的参数.
(2)环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.且环绕通知必须有返回值, 返回值即为目标方法的返回值
参考自:http://blog.csdn.net/snow_7/article/details/52077770
看到这一般都会想到几个问题:
request和response怎么和当前请求挂钩?
request和response等是什么时候设置进去的?
首先分析RequestContextHolder这个类,里面有两个ThreadLocal保存当前线程下的request,关于ThreadLocal可以参考这篇博文
http://www.jianshu.com/p/5675690b351e
后面详细的结果可以看http://blog.csdn.net/zzy7075/article/details/53559902
这样就实现AOP对SpringMVC controller的拦截,当然,我们的项目是统一管理Exception,所有没有对Exception进行拦截,如果有需要也可以配置
AOP的环绕通知、切面的优先级以及重用切入点定义
一、环绕通知
环绕通知是所有通知类型中功能最为强大的, 能够全面地控制连接点. 甚至可以控制是否执行连接点.对于环绕通知来说, 连接点的参数类型必须是 ProceedingJoinPoint . 它是 JoinPoint 的子接口, 允许控制何时执行, 是否执行连接点.
在环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法. 如果忘记这样做就会导致通知被执行了, 但目标方法没有被执行.
注意: 环绕通知的方法需要返回目标方法执行之后的结果, 即调用 joinPoint.proceed(); 的返回值, 否则会出现空指针异常
(1)环绕通知需要携带 ProceedingJoinPoint 类型的参数.
(2)环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.且环绕通知必须有返回值, 返回值即为目标方法的返回值
@Around("execution(* com..Spring4.AOP.*.*(..))") public Object aroundMethod(ProceedingJoinPoint pjd){ Object result = null; String methodName = pjd.getSignature().getName(); try { //前置通知 System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs())); //执行目标方法 result = pjd.proceed(); //返回通知 System.out.println("The method " + methodName + " ends with " + result); } catch (Throwable e) { //异常通知 System.out.println("The method " + methodName + " occurs exception:" + e); throw new RuntimeException(e); } //后置通知 System.out.println("The method " + methodName + " ends"); return result;
参考自:http://blog.csdn.net/snow_7/article/details/52077770
url获取必须通过request中获取,怎么获取request呢?
1.RequestContextHolder的使用
RequestContextHolder顾名思义,持有上下文的Request容器.使用是很简单的,具体使用如下://两个方法在没有使用JSF的项目中是没有区别的 RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); // RequestContextHolder.getRequestAttributes(); //从session里面获取对应的值 String str = (String) requestAttributes.getAttribute("name",RequestAttributes.SCOPE_SESSION); HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest(); HttpServletResponse response = ((ServletRequestAttributes)requestAttributes).getResponse();
看到这一般都会想到几个问题:
request和response怎么和当前请求挂钩?
request和response等是什么时候设置进去的?
2.解决疑问
2.1 request和response怎么和当前请求挂钩?首先分析RequestContextHolder这个类,里面有两个ThreadLocal保存当前线程下的request,关于ThreadLocal可以参考这篇博文
http://www.jianshu.com/p/5675690b351e
后面详细的结果可以看http://blog.csdn.net/zzy7075/article/details/53559902
有了上面两个基础知识我们就可以完成这个功能了
1.先配置切面
<aop:config> <aop:aspect id="OperationRecordAspect" ref="operationRecordAspect"> <aop:pointcut id="recordTask" expression="execution(* com.xx.xx.web.*.*.*(..))"/> <aop:around pointcut-ref="recordTask" method="aroundMethod"/> </aop:aspect> </aop:config>
2.写切面类
public class OperationRecordAspect { private static final Logger logger = LoggerFactory.getLogger(OperationRecordAspect.class); @Autowired private IOperationRecordService OperationRecordService; public Object aroundMethod(ProceedingJoinPoint pjd) { Object result = null; //String methodName = pjd.getSignature().getName(); //获取request HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); //请求url String url = request.getRequestURI(); if(StrUtils.isNotNullOrBlank(url)){ try { result = pjd.proceed(); if(url.contains("get")||url.contains("to")||url.contains("search")){ return result; } } catch (Throwable throwable) { logger.error(throwable.toString()); } } logger.debug("拦截了员工从操作,url是:"+url); logger.info("返回的结果是"+result); //logger.info("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs())); //返回通知 //logger.info("The method " + methodName + " ends with " + result); //参数列表 String param = null; Map<String, String[]> parameterMap = request.getParameterMap(); if(parameterMap!=null&& parameterMap.size()>0 ){ param = JSON.toJSONString(parameterMap); if(StrUtils.isNotNullOrBlank(param)){ logger.info("请求中的参数param"+param); } } //操作人 Emplo 4000 yee employee = (Employee)request.getSession().getAttribute("employee"); String employeeName = null; String loginName = null; if(employee!=null){ loginName = employee.getLoginName(); employeeName = employee.getEmployeeName(); logger.info("操作员工的登录名:"+loginName+"操作员工的姓名:"+employeeName); } OperationRecord operationRecord = new OperationRecord(); operationRecord.setLoginName(loginName); operationRecord.setEmployeeName(employeeName); operationRecord.setUrl(url); operationRecord.setOperationTime(new Date()); operationRecord.setParam(param); operationRecord.setResult(result.toString()); OperationRecordService.addOperationRecord(operationRecord); return result; } }
这样就实现AOP对SpringMVC controller的拦截,当然,我们的项目是统一管理Exception,所有没有对Exception进行拦截,如果有需要也可以配置
相关文章推荐
- Ajax的底层实现--使用Ajax实现无刷新登录实例--解决ajax中URl参数带中文的乱码问题
- 使用 aop拦截 springMVC的controller并获取请求参数及返回结果
- AngularJS使用Controller实现URL跳转
- 如果简单使用固定url实现类似友盟的在线参数
- 使用fgetc实现fgets的功能 参数与返回值的含义,同fgets
- 【亲测】SpringBoot04_controller类中注解对url及url参数的使用
- android使用HttpURLConnection实现带参数文件上传
- SpringMVC3,使用RequestMappint的Param参数,实现一个url绑定多个方法
- 页面跳转传参:参数过长(cookie,url , AngularJs controller 传参实现)
- android客户端通过Get方式提交参数给服务器,使用URL和HttpURLConnection实现,以及乱码问题解决
- spring boot中controller的使用及url参数的获取方法
- (不推荐使用)springMVC基本配置+继承MultiActionController来实现根据参数名指定要请求的方法
- SpringAOP实现拦截Controller请求参数并输出到日志
- SpringMVC3,使用RequestMappint的Param参数,实现一个url绑定多个方法
- shiro使用经验总结:【同时实现url和按钮的拦截,只能用配置文件。不需要用注解!!!已多次测试=@RequiresPermissions不能拦截url直接访问。只能拦截标签(鸡肋,不要用!!)
- android使用HttpURLConnection实现带参数文件上传
- 使用 ResponseBodyAdvice 拦截Controller方法默认返回参数,统一处理返回值/响应体
- SpringMVC3,使用RequestMappint的Param参数,实现一个url绑定多个方法
- 使用jquery获取url以及jquery获取url参数的实现方法
- SpringMVC3,使用RequestMappint的Param参数,实现一个url绑定多个方法