Aop官方定义为面向切面编程,凡事先问下为什么要出现这项技术?
Aop的实际作用是为了对公共方法的剥离与对目标方法的织入,具体几个方法的定义为Aspect(切面)、PointCut(切点)、@Befor(切入时刻);
就组织成了一个完整的增强功能,如下将示例展示Log增强与Controller参数校验
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
第一步:定义切面
package com.bdcloud.aop;
import com.bdcloud.exception.ParmCheckException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
@Order(-99) // 控制多个Aspect的执行顺序,越小越先执行, 当然也可以不写这注解, 对于写和不写@order的两个切面, 有@order的优先于无@order的执行; 都有@order时, 越小越执先执行
@Aspect
public class LogAspect {
/**
* 定义Logger输出类
*/
private Logger logger = LoggerFactory.getLogger(LogAspect.class);
/****
* 定义日志增强切点
*/
@Pointcut("execution(public * com.bdcloud.controller.HelloController.*(..))")
public void addAdvice(){};
/***
* 定义参数校验切点,并引入自定义注解细粒度控制对Controller的参数校验
*/
@Pointcut("execution(public * com.bdcloud.controller.ParmController.*(..)) && @annotation(com.bdcloud.annotation.MyAnnotaion)")
public void parmCheck(){};
/***
* 定义在对目标方法增强前
* @param joinPoint
* @return
*/
@Before(value = "addAdvice()")
public Object intrecepter(JoinPoint joinPoint){
logger.info(joinPoint.getTarget().getClass().getName());
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
logger.info(signature.getMethod().getName());
System.out.println("测试Logger增强控制");
return "hello Aspect!";
}
/***
* 参数校验前置通知的处理
*/
@Before("parmCheck()")
public void sayParmCheck(){
logger.info("Start ParmCheck-进行非空验证");
}
/***
* 参数校验环绕通知的处理
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("parmCheck()")
public Object parmCheck(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取方法名
Method method = signature.getMethod();
//获取方法参数名
String[] parameterNames = signature.getParameterNames();
//获取参数值
Object[] args = joinPoint.getArgs();
//获取参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterNames.length; i++) {
parmIsNull(parameterNames[i],args[i],parameterTypes[i]==null?null:parameterTypes[i].getName());
}
return joinPoint.proceed();
}
/***
* 参数进行非空校验
* @param parmName
* @param value
* @param parmType
*/
private void parmIsNull(String parmName,Object value,String parmType){
if (null == value || "".equals(value.toString().trim())){
throw new ParmCheckException(parmName,value);
}
}
}
第二步通过@Pointcut()定义切点,如下代码
/****
* 定义日志增强切点
*/
@Pointcut("execution(public * com.bdcloud.controller.HelloController.*(..))")
public void addAdvice(){};
/***
* 定义参数校验切点,并引入自定义注解细粒度控制对Controller的参数校验
*/
@Pointcut("execution(public * com.bdcloud.controller.ParmController.*(..)) && @annotation(com.bdcloud.annotation.MyAnnotaion)")
public void parmCheck(){};
第三步定义不同时刻切入的执行方法与流程
/***
* 定义在对目标方法增强前
* @param joinPoint
* @return
*/
@Before(value = "addAdvice()")
public Object intrecepter(JoinPoint joinPoint){
logger.info(joinPoint.getTarget().getClass().getName());
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
logger.info(signature.getMethod().getName());
System.out.println("测试Logger增强控制");
return "hello Aspect!";
}
/***
* 参数校验前置通知的处理
*/
@Before("parmCheck()")
public void sayParmCheck(){
logger.info("Start ParmCheck-进行非空验证");
}
第四步HelloController的实现
package com.bdcloud.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String sayHello(){
return "hello world";
}
@GetMapping("/hi")
public String sayHi(){
return "Hi World!";
}
}
package com.bdcloud.exception;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ParmCheckException extends RuntimeException {
private String parameterName;
private Object ParameterValue;
@Override
public String getMessage() {
return "Throw Exception Parmeter is "+this.parameterName+" : "+this.ParameterValue;
}
}
package com.bdcloud.exception;
import org.aspectj.lang.annotation.After;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/***
* 全局异常处理类
*/
@ControllerAdvice
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/***
* 参数为该Handler处理的异常
* @param e
* @return
*/
@ExceptionHandler({ParmCheckException.class})
@ResponseBody
public Object getException(Exception e){
logger.error("request Exception:", e);
return e.getMessage();
}
}
package com.bdcloud.annotation;
import java.lang.annotation.*;
/***
* 自定义注解,用于细粒度控制Aspect
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotaion {
}
package com.bdcloud.controller;
import com.bdcloud.annotation.MyAnnotaion;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ParmController {
@GetMapping("/parm")
public String getParam(@RequestParam String temp){
return temp;
}
@MyAnnotaion //添加该自定义注解后才会走增强代码流程
@GetMapping("/parm2")
public String getParam2(@RequestParam String lxf){
return lxf;
}
}