spring AOP-切面编程
首先学习aop切面的顺序:
- 前置通知:(Before advice)表明在连接点执行之前执行的动作。
@Before("controllerLog()")
- 环绕通知:(Around Advice) 环绕可以看作是包含前置通知和后置通知的一个通知,先了解,后面具体理解。
@Around("controllerLog()")
- 后置通知:(After returning advice)在某个连接点完成后通知,比如一个方法没有抛出任何异常,正常返回【第一个参数还是指定切点,第二个参数指定的是返回值】
@AfterReturning(value = "controllerLog()",returning = "obj")
- 异常通知:(After throwing advice) 在方法异常推出时候执行的通知。【这里和上面的后置通知差不多,不过第二个参数是一个异常类型对象】
@AfterThrowing(value = "controllerLog()", throwing = "ex")
- 最终通知:(After advice) 在连接点退出时候执行的通知。不论是正常退出还是异常退出。
@After("controllerLog()")
正常返回:
异常返回:
学习切面编程的时候学到两种方式。下面阐述一下
公共部分
引入aop包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
1.直接配置类路径进行切面编程
package com.whalecloud.uip.client.loggeraspect; import com.alibaba.fastjson.JSONObject; import com.whalecloud.common.cfg.Constants; import com.whalecloud.common.component.ExteParamObtain; import com.whalecloud.common.domain.CfgReqInfo; import com.whalecloud.common.domain.ReqServiceBean; import com.whalecloud.common.result.IomResult; import com.whalecloud.common.util.BeetlTemplateUtil; import com.whalecloud.common.util.MapToXmlUtils; import com.whalecloud.uip.client.feign.DataSourceFeignClient; import org.apache.commons.collections.MapUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; @Aspect @Component public class WebLogAspect { private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class); /** * Service层切点 */ @Pointcut("execution(* com.whalecloud.uip.client.service.ToRestService.invokeRestReq(com.whalecloud.common.domain.ReqServiceBean)) || execution(* com.whalecloud.uip.client.service.ToWebserviceService.invokeWebServiceReq(com.whalecloud.common.domain.ReqServiceBean))") public void controllerAspect() { } /** * 配置Service环绕通知,使用在方法aspect()上注册的切入点 * * @param point 切点 * @return * @throws Throwable */ @Around("controllerAspect()") public Object doAround(ProceedingJoinPoint point) throws Throwable {/*获取方法的参数值*/ Map<String, Object> paramMap = getParam(point); ReqServiceBean reqServiceBean = (ReqServiceBean) MapUtils.getObject(paramMap, "reqServiceBean");
/*获取结果*/
result = (IomResult)point.proceed();return result; } /** * 获取参数的列表 * * @param joinPoint * @return */ private Map<String, Object> getParam(ProceedingJoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); String[] parameterNames = pnd.getParameterNames(method); Map<String, Object> paramMap = new HashMap<>(); assert parameterNames != null; for (int i = 0; i < parameterNames.length; i++) { paramMap.put(parameterNames[i], args[i]); } return paramMap; } }
proceedingJoinPoint 可以拿到入参值和返回值。进行切面操作。
2.根据注解进行切面编程
1.先写个注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface OperationLog { String type(); }
2.AOP配置
@Aspect @Component public class AOPConfig { @Around(value = "@annotation(OperationLog)") public Object around(ProceedingJoinPoint proceedingJoinPoint){ System.out.println("方法环绕begin...参数:"+Arrays.toString(proceedingJoinPoint.getArgs())); try { Object ret= proceedingJoinPoint.proceed(); System.out.println("方法环绕end...结果:"+ret); return ret; } catch (Throwable throwable) { throwable.printStackTrace(); } return null; } @Before(value = "@annotation(OperationLog)") public void doBefore(JoinPoint joinPoint){ ServletRequestAttributes requestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest req= requestAttributes.getRequest(); System.out.println("uri:"+req.getRequestURI()); System.out.println("执行方法前 : " + Arrays.toString(joinPoint.getArgs())); } @After(value = "@annotation(OperationLog)") public void after(JoinPoint joinPoint){ System.out.println("执行方法后:"+ Arrays.toString(joinPoint.getArgs())); } @AfterReturning(pointcut = "@annotation(OperationLog)",returning = "ret") public void doAfterReturning(Object ret){ System.out.println("方法的返回值 : " + ret); } @AfterThrowing(pointcut = "@annotation(OperationLog)",throwing = "ex") public void AfterThrowing(JoinPoint joinPoint,Throwable ex){ System.out.println("方法执行异常 : " + ex); } }
3.服务类
@RequestMapping("/aop") @RestController public class AOPCtrl { @Autowired private MessageService messageService; @RequestMapping(value = "/test",method = RequestMethod.GET) public String test(){ return messageService.sendMessage("xiaoming",29); } } @Service public class MessageService { @OperationLog(type = "sendMessage") public String sendMessage(String name,int age){ return "this person: "+name+" age: "+age; } }
4.返回结果
运行结果: 方法环绕begin...参数:[xiaoming, 29] uri:/aop/test 执行方法前 : [xiaoming, 29] 方法环绕end...结果:this person: xiaoming age: 29 执行方法后:[xiaoming, 29] 方法的返回值 : this person: xiaoming age: 29