自定义注解类
package com.wing.my.cloud.system.modular.system.util.annotation;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckNullParams {
String[] params();
}
AOP
package com.wing.my.cloud.system.modular.system.util.aspect;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.wing.my.cloud.kernel.model.exception.ServiceException;
import com.wing.my.cloud.system.modular.system.util.annotation.CheckNullParams;
import lombok.extern.slf4j.Slf4j;
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.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
/**
* 校验参数不能为空
* ProceedingJoinPoint只能用在环绕通知上。
*/
@Slf4j
@Aspect
@Component
public class CheckNullParamsAspect {
private static String dateFormat = "yyyy-MM-dd HH:mm:ss";
@Pointcut("@annotation(com.wing.my.cloud.system.modular.system.util.annotation.CheckNullParams)")
public void paramNotNull(){}
@Around("paramNotNull()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
log.info("进入到环绕通知中");
// 1、记录方法开始执行的时间
long start = System.currentTimeMillis();
// 2、打印请求参数
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String target = joinPoint.getSignature().getDeclaringTypeName(); // 全路径类名
String classNm = target.substring(target.lastIndexOf(".") + 1, target.length()); // 类名截取
String method = joinPoint.getSignature().getName(); // 获取方法名
Map params = getAllRequestParam(request); // 获取请求参数
log.info("{}.{} 接收参数: {}", classNm, method, JSON.toJSONString(params));
CheckNullParams check = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(CheckNullParams.class); // 获取注解
String[] requiredFields = check.params(); // 获取注解参数
// 3、必填参数非空校验
Map result = validParams(params, requiredFields);
if ((Boolean) result.get("boolean")) {
Object object = joinPoint.proceed(); // 必填参数非空校验通过,执行方法,获取执行结果
// 4、打印应答数据和方法耗时
long time = System.currentTimeMillis() - start;
log.info("{}.{} 应答数据: {}; 耗时 {} ms", classNm, method, JSONObject.toJSONStringWithDateFormat(object,
dateFormat, SerializerFeature.WriteMapNullValue), time);
return object;
} else {
// 必填参数非空校验未通过,抛出异常,由GlobalExceptionHandler捕获全局异常,返回调用方“参数缺失”
throw new ServiceException(500, "参数【" + result.get("param") + "】缺失或格式错误");
}
}
/**
* 校验传入参数params(非null)中是否必含requiredFields(非null)中的各个属性,且属性值非空
*
* @param params 传入参数
* @param requiredFields 设置的非空属性数组
* @return 校验通过返回true,否则返回false
*/
private Map validParams(Map params, String[] requiredFields) {
Map map = new HashMap<>(2);
if (requiredFields.length == 0) {
// 无必送参数,直接返回true
map.put("boolean", true);
return map;
} else {
for (String field : requiredFields) {
if (StringUtils.isEmpty(params.get(field))) {
map.put("boolean", false);
map.put("param", field);
return map;
}
}
map.put("boolean", true);
return map;
}
}
/**
* 获取请求参数
*/
public static Map getAllRequestParam(HttpServletRequest request) {
Map res = new HashMap<>();
Enumeration> temp = request.getParameterNames();
if (null != temp) {
while (temp.hasMoreElements()) {
String en = (String) temp.nextElement();
String value = request.getParameter(en);
res.put(en, value);
// 在报文上送时,如果字段的值为空,则不上送<下面的处理为在获取所有参数数据时,判断若值为空,则删除这个字段>
if (StringUtils.isEmpty(res.get(en))) {
res.remove(en);
}
}
}
return res;
}
}
扩展
上面是AOP实现的业务逻辑,有的时候需要通过反射去处理 ,比如通过方法来获取方法上的注解信息以及注解里面的值等
java 获取类,属性变量,方法,方法参数上注解的值等
获取类上注解的值
定义注解@Target(ElementType.TYPE)用于类,接口等
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Orange {
String getName();
String getValue();
}
获取
@Orange(getName = "3333",getValue = "4444")
public class ParameterNameTest {
。。。
@Test
public void main() throws Exception {
Class clazz = ParameterNameTest.class;
if(clazz.isAnnotationPresent(Orange.class)){
// 获取 "类" 上的注解
Orange getAnnotation = clazz.getAnnotation(Orange.class);
System.out.println("\"类\"上的注解值获取到第一个 :"
+ getAnnotation.getName()+ ",第二个:"+ getAnnotation.getValue());
}
}
返回
"类"上的注解值获取到第一个 :3333,第二个:4444
获取属性变量上注解的值
定义注解@Target(ElementType.FIELD)用于属性变量
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Banana {
String length();
String price();
}
获取
public class ParameterNameTest {
@Banana(length = "6666", price = "$888")
String something = "other information";
@Test
public void main() throws Exception {
Class clazz = ParameterNameTest.class;
// 获取 "属性变量" 上的注解的值
Field[] fields = clazz.getDeclaredFields();
for(Field field: fields){
if(field.isAnnotationPresent(Banana.class)){
Banana bananaAnnotation = field.getAnnotation(Banana.class);
System.out.println("\"属性变量\"上的注解值获取到第一个 :"
+ bananaAnnotation.length()+ ",第二个:"+ bananaAnnotation.price());
}
}
}
}
返回
"属性变量"上的注解值获取到第一个 :6666,第二个:$888
获取方法上注解的值
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Apple {
String color();
String number();
}
获取
public class ParameterNameTest {
@Apple(color = "红色", number = "5555")
public void method1(){
// ...
}
@Test
public void main() throws Exception {
Class clazz = ParameterNameTest.class;
// 获取 "方法"上的注解的值
Method[] methods = clazz.getDeclaredMethods();
for (Method method: methods){
if(method.isAnnotationPresent(Apple.class)){
Apple appleAnnotation = method.getAnnotation(Apple.class);
System.out.println("\"方法\"上的注解值获取到第一个 :"
+ appleAnnotation.color()+ ",第二个:"+ appleAnnotation.number());
}
}
}
}
返回
"方法"上的注解值获取到第一个 :红色,第二个:5555
获取" 方法参数 " 上注解的值
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Cherry {
String value();
}
获取
public class ParameterNameTest {
public void method2(@Cherry("1111") String param1, @Cherry("2222") String param2) {
System.out.println(param1 + param2);
}
@Test
public void main() throws Exception {
Class clazz = ParameterNameTest.class;
// 获取 "方法参数" 上的注解的值
Method method = clazz.getDeclaredMethod("method2", String.class, String.class);
String[] parameterNames = getMethodParameterNamesByAnnotation(method);
System.out.println("\"方法参数\"上的注解值获取到"+Arrays.toString(parameterNames));
}
/**
* 获取给 "方法参数" 进行注解的值
*
* @param method 要获取参数名的方法
* @return 按参数顺序排列的参数名列表
*/
public static String[] getMethodParameterNamesByAnnotation(Method method) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
if (parameterAnnotations == null || parameterAnnotations.length == 0) {
return null;
}
String[] parameterNames = new String[parameterAnnotations.length];
int i = 0;
for (Annotation[] parameterAnnotation : parameterAnnotations) {
for (Annotation annotation : parameterAnnotation) {
if (annotation instanceof Cherry) {
Cherry param = (Cherry) annotation;
parameterNames[i++] = param.value();
}
}
}
return parameterNames;
}
}
返回
"方法参数"上的注解值获取到[1111, 2222]
小结:主要使用的API是Class类中的实现接口AnnotatedElement的方法
isAnnotationPresent — 检测该元素是否被对应注解修饰
default boolean isAnnotationPresent(Class extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
getAnnotation --- 获取注解对象
T getAnnotation(Class annotationClass);
Aop JoinPoint方法详解
重要API
/*获取参数的值数组*/
Object[] args = point.getArgs(); // [1] 参数的值
/*获取目标对象(被加强的对象)*/
Object target = point.getTarget();
/*获取signature 该注解作用在方法上,强转为 MethodSignature*/
MethodSignature signature = (MethodSignature) point.getSignature();
/*方法名*/
String signatureName = signature.getName(); // findById
/*参数名称数组(与args参数值意义对应)*/
String[] parameterNames = signature.getParameterNames(); // [i] 参数名称
/*获取执行的方法对应Method对象*/
Method method = signature.getMethod(); // public void com.draymond.aop2.service.UserService.findById(int)
/*获取返回值类型*/
Class returnType = signature.getReturnType(); // void
/*获取方法上的注解*/
WebAnnotation webAnnotation = method.getDeclaredAnnotation(WebAnnotation.class);
// 获取request/response(ThreadLocal模式)
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest request = servletRequestAttributes.getRequest();
HttpServletResponse response = servletRequestAttributes.getResponse();
@Before("customerJoinPointerExpression()")
public void beforeMethod(JoinPoint joinPoint){
joinPoint.getSignature().getName(); // 获取目标方法名
joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属类的简单类名
joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名
joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public、private、protected)
Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组
joinPoint.getTarget(); // 获取被代理的对象
joinPoint.getThis(); // 获取代理对象自己
// 获取目标方法上的注解
private T getMethodAnnotation(ProceedingJoinPoint joinPoint, Class clazz) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
return method.getAnnotation(clazz);
}
}
}
实现
@ApiImplicitParams({
@ApiImplicitParam(name = "channelParam", value = "闲钱保渠道维护表实体类", dataType = "ChannelParam", paramType = "query", example = "xxx"),
})
@ResponseBody
@ApiResource(name = "测试接口", path = "/test1")
@CheckNullParams(params = {"custId","inpName"}) //参数不能为空
public ResponseData test1( InsurancePolicyParam insurancePolicyParam) {
testUserService.testExceptionAop(insurancePolicyParam);
return ResponseData.success();
}
参数 custId,inpName为InsurancePolicyParam实体类的属性。
AOP各种通知
前置通知
方法执行前开始执行
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("这是切面开始打印出来的--->The method " + methodName + " begins with " + Arrays.asList(args));
}
//后置通知
//方法执行后开始执行
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("这是切面结束打印出来的--->The method " + methodName + " ends");
}
带返回值的后置通知
方法正常执行后执行
1 @AfterReturning(value = "declareJointPointExpression()",
2 returning = "result")
3 public void afterReturningMethod(JoinPoint joinPoint,Object result) {
4 String methodName = joinPoint.getSignature().getName();
5 System.out.println("The method " + methodName + " ends with " + result);
6 }
异常通知
代码执行中出现异常后执行
@AfterThrowing(value = "exceptionLog()",throwing = "e")
public void afterThrowing(JoinPoint point, Exception e) throws Throwable {
String target = point.getSignature().getDeclaringTypeName(); // 全路径类名
String classNm = target.substring(target.lastIndexOf(".") + 1, target.length()); // 类名截取
String method = point.getSignature().getName(); // 获取方法名
log.error("{}.{} 【异常信息】: {}", classNm, method, e.getMessage());
}
先执行前置通知,然后代码,然后异常通知,然后后置通知。