浏览器运行环境异常,请检查是否开启本站的JavaScript权限或下载最新版浏览器
springboot自定义注解这么用,轻松捕获系统操作日志(下)

springboot自定义注解这么用,轻松捕获系统操作日志(下)

在上一篇中我们讲到了将日志保存到数据库,有点遗憾的地方是方法的中文名字得在Log2DBApiOperation中写两次,有点不人性化。今天我们就来解决这个问题。

如果只能写一次,那我们只能改自己的代码读取ApiOperation中的value属性(因为swagger是不会改代码适配你d的),那如果要读取别人的属性,有什么方法呢?

  1. 继承
  2. 扫描ApiOperation

如果我们的Log2DB继承ApiOperation呢?注解能否继承?答案是肯定的。

我们看下RestController的注解,其实是继承了Controller注解的

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller//就在这里继承的
@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

但是注解的继承依赖如下一个因素:

  1. 首先要想Annotation能被继承,需要在注解定义的时候加上@Inherited,并且如果要被反射应用的话,还需要@Retention(RetentionPolicy.RUNTIME)标识
  2. JDK文档中说明的是:只有在类上应用Annotation才能被继承,而实际应用结果是:除了类上应用的Annotation能被继承外,没有被重写的方法的Annotation也能被继承;
  3. 当方法被重写后,Annotation不会被继承
  4. Annotation的继承不能应用在接口

遗憾的是ApiOperation不符合条件,那我们就只剩下第二个方法了。
第二个我们要解决几个问题
1、怎么扫描读取ApiOperation所有的配置
2、用什么key存储关系

先解决第一个,我们可以在工程启动的时候获取所有的ApiOperation,如下

@Slf4j
@Component
public class ApisScaner {

    @Resource
    private ApplicationContextProvider applicationContextProvider;

    //key是全类名.方法名,value是ApiOperation的name属性
    public static Map<String,String> APIS = new HashMap<String,String>();

    @PostConstruct
    public void handler() throws ClassNotFoundException {
        log.info("SwaggerScaner.handler");
        ApplicationContext applicationContext = applicationContextProvider.getApplicationContext();
        //获取自定义注解的配置
        final Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(Api.class);

        for (String key : beansWithAnnotation.keySet()) {
            Method[] methods = Class.forName(beansWithAnnotation.get(key).getClass().getName()).getMethods();
            for (Method method : methods) {
                ApiOperation annotation = AnnotationUtils.findAnnotation(method, ApiOperation.class);
                if(annotation!=null){
                    String value = annotation.value();
                    String className = beansWithAnnotation.get(key).toString();
                    String methodName = method.getName();
                    className = className.substring(0,className.indexOf("@"));

                    APIS.put(className+"."+methodName,value);
                }
            }
        }

        for (String item : APIS.keySet()) {
            log.info("item:{}-->value:{}",item, APIS.get(item));
        }
    }
}

ApplicationContextProvider (动态获取spring的bean对象)的源码也贴下:

@Component
public class ApplicationContextProvider implements ApplicationContextAware {

     /**
      * 上下文对象实例
      */
     private static ApplicationContext applicationContext;

     @Override
     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         ApplicationContextProvider.applicationContext = applicationContext;
     }

     /**
      * 获取Spring上下文
      *
      * @return
      */
     public static ApplicationContext getApplicationContext() {
         return applicationContext;
     }

     /**
      * 通过name获取Bean
      *
      * @param name
      * @return
      */
     public static Object getBean(String name) {
         return getApplicationContext().getBean(name);
     }

     /**
      * 通过class获取Bean
      *
      * @param clazz
      * @param <T>
      * @return
      */
     public static <T> T getBean(Class<T> clazz) {
         return getApplicationContext().getBean(clazz);
     }

     /**
      * 通过name,以及Clazz返回指定的Bean
      *
      * @param name
      * @param clazz
      * @param <T>
      * @return
      */
     public static <T> T getBean(String name, Class<T> clazz) {
         return getApplicationContext().getBean(name, clazz);
     }

 }

好了,看了代码大伙基本也明白了,用什么存储,我暂时是用map存储的,大家可以写到数据库中,也可以用redis,mongodb,随意。启动一下试试:

SwaggerScaner.handler
item:com.example.lesson20.controller.TestController.getUser-->value:获取用户1
item:com.example.lesson20.controller.TestController.postUser-->value:保存用户1

数据我们都拿到了,改下上一节的一个小点即可:

//name = logAnnotation.name();
name = ApisScaner.APIS.get(clazz_method);

这样我们就搞定了,so easy!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得UP主同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理: DMCA投诉/Report
赞 4
踩 1
1
收藏
1条评论

兄弟对BeanPostProcessor和BeanFactory有没有研究🥹

1月前
我是有底线的
内容滑到底啦,去看看别的风景吧