1)什么是注解:
Java 注解就是代码中的一些特殊标记(元信息),用于在编译、类加载、运行时进行解析和使用,并执行相应的处理。它本质是继承了 Annotation 的特殊接口,其具体实现类是 JDK 动态代理生成的代理类,通过反射获取注解时,返回的也是 Java 运行时生成的动态代理对象 $Proxy1。通过代理对象调用自定义注解的方法,会最终调用 AnnotationInvocationHandler 的 invoke 方法,该方法会从 memberValues 这个Map中查询出对应的值,而 memberValues 的来源是Java常量池。
注解在实际开发中非常常见,比如 Java 原生的 @Overried、@Deprecated 等,Spring的 @Controller、@Service等,Lombok 工具类也有大量的注解,不过在原生 Java 中,还提供了元 Annotation(元注解),他主要是用来修饰注解的,比如 @Target、@Retention、@Document、@Inherited 等。
@Target:标识注解可以修饰哪些地方,比如方法、成员变量、包等,具体取值有以下几种:ElementType.TYPE/FIELD/METHOD/PARAMETER/CONSTRUCTOR/LOCAL_VARIABLE/ANNOTATION_TYPE/PACKAGE/TYPE_PARAMETER/TYPE_USE
@Retention:什么时候使用注解:SOURCE(编译阶段就丢弃) / CLASS(类加载时丢弃) / RUNTIME(始终不会丢弃),一般来说,我们自定义的注解都是 RUNTIME 级别的,因为大多数情况我们是根据运行时环境去做一些处理,一般需要配合反射来使用,因为反射是 Java 获取运行是的信息的重要手段
@Document:表示该注解可以被生成API文档
@Inherited:表示该注解可以被继承
(2)如何自定义注解?
① 创建一个自定义注解:与创建接口类似,但自定义注解需要使用 @interface
② 添加元注解信息,比如 @Target、@Retention、@Document、@Inherited 等
③ 创建注解方法,但注解方法不能带有参数
④ 注解方法返回值为基本类型、String、Enums、Annotation 或其数组
⑤ 注解可以有默认值;
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface CarName {
String value() default “”;
}
自定义注解的使用场景
通过AOP和自定义注解实现 >>操作日志 --> DB数据库
定义日志字段属性
@Data
@Entity
@Table(name = "operate_log")
public class OperateLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String recordId; // 操作数据id
private String module;// 模块名称
private String business;// 业务方法描述
private String opType;// 操作类型
private Long userId;// 操作人
private String userName;// 操作人姓名
private String params;// 操作数据
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
定义注解
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface SystemLog {
/**
* 操作描述 业务名称business
*
* @return
*/
String description() default "";
/**
* 操作模块
*
* @return
*/
OperateModule module();
/**
* 操作类型 create modify delete
*
* @return
*/
OperateType opType();
/**
* 主键入参参数名称,入参中的哪个参数为主键
*
* @return
*/
String primaryKeyName() default "";
/**
* 主键在参数中的顺序,从0开始,默认0
*/
int primaryKeySort() default 0;
/**
* 主键所属类
*
* @return
*/
Class<?> primaryKeyBelongClass();
}
使用注解(处理注解),AOP / Aspect + 反射(通过注解获取对象的字段,方法等信息)
@Aspect
@Component
@Slf4j
public class WebLogAspect {
@Autowired
private OperateLogRepository logRepository;
@Pointcut("execution(public * demo1.controller..*.*(..)) "
+ " && @annotation(demo1.log.SystemLog)")
public void webLog() {
}
@Around("webLog()")
public Object round(ProceedingJoinPoint joinPoint) throws Throwable {
// log.info("环绕通知开始........");
// String username = SecurityUtils.getCurrentUserName();
String username = "ss";
// 入参 value
Object[] args = joinPoint.getArgs();
// 入参名称
String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
Map<String, Object> params = new HashMap<>();
// 获取所有参数对象
for (int i = 0; i < args.length; i++) {
if (null != args[i]) {
if (args[i] instanceof BindingResult) {
params.put(paramNames[i], "bindingResult");
} else {
params.put(paramNames[i], args[i]);
}
} else {
params.put(paramNames[i], "无");
}
}
Map<String, Object> values = getControllerAnnotationValue(joinPoint);
String opType = values.get("opType").toString();
String module = values.get("module").toString();
String business = values.get("business").toString();
String primaryKeyName = values.get("primaryKeyName").toString();
int primaryKeySort = Integer.parseInt(values.get("primaryKeySort").toString());
Class<?> primaryKeyBelongClass = (Class<?>) values.get("primaryKeyBelongClass");
Object primaryKeyValue = null;
if (StringUtils.isNotBlank(primaryKeyName) && OperateType.valueOf(opType) == OperateType.delete) {
primaryKeyValue = args[primaryKeySort];
}
// 切面返回值
Object returnValue = joinPoint.proceed();
if (OperateType.valueOf(opType) != OperateType.delete) {
// 主要目的是为了获取方法保存成功的返回数据格式,以获取保存成功的数据id
// 此处要限制新增返回成功的格式,return ok("具体操作信息", new MapBean("此处为实体主键属性名称",
// primaryKeyValue));
// 不然自己也可定义格式,进行拆分获取主键
primaryKeyName = getPrimaryKeyName(primaryKeyBelongClass).toString();
primaryKeyValue = ReflectUtils.dynamicGet(returnValue, primaryKeyName);
if (primaryKeyValue == null || primaryKeyValue.toString().equals("")) {// 处理service层返回ResultBean
Object result = ReflectUtils.dynamicGet(returnValue, "result");
if (result != null) {
primaryKeyValue = ReflectUtils.dynamicGet(result, primaryKeyName);
} else {
primaryKeyValue = args[primaryKeySort];
}
}
}
OperateLog operateLog = new OperateLog();
//
if (JSONUtil.toJsonStr(params).length() <= 2000) {
operateLog.setData(JSONUtil.toJsonStr(params));
}
operateLog.setUserId(1L);
operateLog.setUserName(username == null ? "系统" : username);
operateLog.setModule(module);
operateLog.setOpType(opType);
operateLog.setBusiness(business);
String recordId = null;
if (primaryKeyValue instanceof Object[]) {
recordId = Arrays.toString((Object[]) primaryKeyValue);
} else {
recordId = primaryKeyValue.toString();
}
operateLog.setRecordId(recordId);
operateLog.setCreateTime(new Date());
logRepository.save(operateLog);
log.info(">>>操作日志:{}", operateLog);
return returnValue;
}
/**
* 获取class的主键字段名 主键值
*/
@SuppressWarnings({"rawtypes", "unchecked"})
private static Object getPrimaryKeyName(Class<?> clazz) throws Exception {
Object param = null;
// 递归获取父子类所有的field
Class tempClass = clazz;
// 当父类为null的时候说明到达了最上层的父类(Object类
while (tempClass != null && !StringUtils.equals(tempClass.getName().toLowerCase(), "java.lang.object")) {
Field[] fields = tempClass.getDeclaredFields();
for (Field field : fields) {
String fieldName = field.getName();
// boolean类型不必判断,因实体里包含boolean类型的属性,getter方法是以is开头,不是get开头
if (field.getType().equals(Boolean.class) || field.getType().getName().equals("boolean")) {
continue;
}
if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) {
continue;
}
String getterMethod = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Method method = tempClass.getDeclaredMethod(getterMethod);
// 字段上是否存在@Id注解
Object primaryAnnotation = field.getAnnotation(Id.class);// for hibernate
if (primaryAnnotation == null)
primaryAnnotation = field.getAnnotation(org.springframework.data.annotation.Id.class);// for spring
// data
// getter方法上是否存在@Id注解
if (primaryAnnotation == null)
primaryAnnotation = method.getAnnotation(Id.class);
if (primaryAnnotation == null)
primaryAnnotation = method.getAnnotation(org.springframework.data.annotation.Id.class);
// 存在@Id注解,则说明该字段为主键
if (primaryAnnotation != null) {
/* String primaryKeyName = field.getName(); */
param = field.getName();
break;
}
}
if (param != null && StringUtils.isNotBlank(param.toString())) {
break;
}
// 得到父类赋值给tempClass
tempClass = tempClass.getSuperclass();
}
if (param == null) {
throw new Exception(clazz.getName() + "实体,未设置主键");
}
return param;
}
/**
* 获取@SystemLog 注解上信息
*
* @param joinPoint
* @return map
* @throws Exception
*/
@SuppressWarnings("rawtypes")
public static Map<String, Object> getControllerAnnotationValue(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
Map<String, Object> map = new HashMap<>();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] classes = method.getParameterTypes();
if (classes.length == arguments.length) {
// 取入参数据
String description = method.getAnnotation(SystemLog.class).description();
String module = method.getAnnotation(SystemLog.class).module().name();
String opType = method.getAnnotation(SystemLog.class).opType().name();
String primaryKeyName = method.getAnnotation(SystemLog.class).primaryKeyName();
int primaryKeySort = method.getAnnotation(SystemLog.class).primaryKeySort();
Class<?> clazz = method.getAnnotation(SystemLog.class).primaryKeyBelongClass();
map.put("module", module);
map.put("opType", opType);
map.put("business", description);
map.put("primaryKeyName", primaryKeyName);
map.put("primaryKeySort", primaryKeySort);
map.put("primaryKeyBelongClass", clazz);
break;
}
}
}
return map;
}
@AfterReturning("webLog()")
public void doAfterReturning(JoinPoint joinPoint) {
// 处理完请求,返回内容
// log.info("WebLogAspect.doAfterReturning()");
}
}
原文链接:https://blog.csdn.net/a745233700/article/details/80959716,https://blog.csdn.net/qq_42105629/article/details/102474481