首页 > 其他分享 >Autoconfiguration详解——自动注入配置参数

Autoconfiguration详解——自动注入配置参数

时间:2023-04-03 21:25:43浏览次数:42  
标签:Metrics name Autoconfiguration class metrics 详解 参数 public String

目录

Autoconfiguration详解——自动注入配置参数

一、自动注入配置基础

  1. @EnableConfigurationProperties(CommonRedisProperties.class) 注解configuration类;
  2. @ConfigurationProperties(prefix = "myserver")注解配置文件类,prefix标明配置文件的前缀;
  3. public RedisTemplate<String, Object> getRedisTemplate(CommonRedisProperties properties, RedisConnectionFactory redisConnectionFactory) ,加到需要使用的参数中即可;
  4. META-INF目录下添加additional-spring-configuration-metadata.json文件,格式如下
{
  "groups": [
    {
      "name": "server",
      "type": "com.huawei.workbenchcommon.redis.CommonRedisProperties",
      "sourceType": "com.huawei.workbenchcommon.redis.CommonRedisProperties"
    }
  ],
  "properties": [
    {
      "name": "myserver.database",
      "type": "java.lang.String",
      "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
    }
  ]
}

二、注释切面 @Metrics

1. 注解@Metrics

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Metrics {
    /**
     * 在方法成功执行后打点,记录方法的执行时间发送到指标系统,默认开启
     */
    boolean recordSuccessMetrics() default true;

    /**
     * 在方法成功失败后打点,记录方法的执行时间发送到指标系统,默认开启
     */
    boolean recordFailMetrics() default true;

    /**
     * 通过日志记录请求参数,默认开启
     */
    boolean logParameters() default true;

    /**
     * 通过日志记录方法返回值,默认开启
     */
    boolean logReturn() default true;

    /**
     * 出现异常后通过日志记录异常信息,默认开启
     */
    boolean logException() default true;

    /**
     * 出现异常后忽略异常返回默认值,默认关闭
     */
    boolean ignoreException() default false;

2. 切面MetricsAspect

@Aspect
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MetricsAspect {
    /**
     * 让Spring帮我们注入ObjectMapper,以方便通过JSON序列化来记录方法入参和出参
     */
    @Resource
    private ObjectMapper objectMapper;

    /**
     * 实现一个返回Java基本类型默认值的工具。其实,你也可以逐一写很多if-else判断类型,然后手动设置其默认值。
     * 这里为了减少代码量用了一个小技巧,即通过初始化一个具有1个元素的数组,然后通过获取这个数组的值来获取基本类型默认值
     */
    private static final Map<Class<?>, Object> DEFAULT_VALUES = Stream
            .of(boolean.class, byte.class, char.class, double.class, float.class, int.class, long.class, short.class)
            .collect(toMap(clazz -> clazz, clazz -> Array.get(Array.newInstance(clazz, 1), 0)));

    public static <T> T getDefaultValue(Class<T> clazz) {
        //noinspection unchecked
        return (T) DEFAULT_VALUES.get(clazz);
    }

    /**
     * 标记了Metrics注解的方法进行匹配
     */
    @Pointcut("@annotation(com.common.config.metrics.annotation.Metrics)")
    public void withMetricsAnnotationMethod() {
    }

    /**
     * within指示器实现了匹配那些类型上标记了@RestController注解的方法
     * 注意这里使用了@,标识了对注解标注的目标进行切入
     */
    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
    public void controllerBean() {
    }

    @Pointcut("@within(com.common.config.metrics.annotation.Metrics)")
    public void withMetricsAnnotationClass() {
    }

    @Around("controllerBean() || withMetricsAnnotationMethod() || withMetricsAnnotationClass()")
    public Object metrics(ProceedingJoinPoint pjp) throws Throwable {
        // 通过连接点获取方法签名和方法上Metrics注解,并根据方法签名生成日志中要输出的方法定义描述
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Metrics metrics = signature.getMethod().getAnnotation(Metrics.class);

        String name = String.format("【%s】【%s】", signature.getDeclaringType().toString(), signature.toLongString());

        if (metrics == null) {
            @Metrics
            final class InnerClass {
            }
            metrics = InnerClass.class.getAnnotation(Metrics.class);
        }
        // 尝试从请求上下文获得请求URL
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
            name += String.format("【%s】", request.getRequestURL().toString());
        }
        // 入参的日志输出
        if (metrics.logParameters()) {
            log.info(String.format("【入参日志】调用 %s 的参数是:【%s】", name, objectMapper.writeValueAsString(pjp.getArgs())));
        }
        // 连接点方法的执行,以及成功失败的打点,出现异常的时候记录日志
        Object returnValue;
        Instant start = Instant.now();
        try {
            returnValue = pjp.proceed();
            if (metrics.recordSuccessMetrics()) {
                // 在生产级代码中,应考虑使用类似Micrometer的指标框架,把打点信息记录到时间序列数据库中,实现通过图表来查看方法的调用次数和执行时间,
                log.info(String.format("【成功打点】调用 %s 成功,耗时:%d ms", name, Duration.between(start, Instant.now()).toMillis()));
            }
        } catch (Exception ex) {
            if (metrics.recordFailMetrics()) {
                log.info(String.format("【失败打点】调用 %s 失败,耗时:%d ms", name, Duration.between(start, Instant.now()).toMillis()));
            }
            if (metrics.logException()) {
                log.error(String.format("【异常日志】调用 %s 出现异常!", name), ex);
            }
            if (metrics.ignoreException()) {
                returnValue = getDefaultValue(signature.getReturnType());
            } else {
                throw ex;
            }
        }
        // 返回值输出
        if (metrics.logReturn()) {
            log.info(String.format("【出参日志】调用 %s 的返回是:【%s】", name, returnValue));
        }
        return returnValue;
    }

3. 自动注入AutoConfiguration

@AutoConfiguration
@Slf4j
@EnableConfigurationProperties(MetricsProperties.class)
@ConditionalOnProperty(prefix = "common.metrics", name = {"keep-alive"}, havingValue = "true", matchIfMissing = true)
public class AspectAutoConfiguration {

    public AspectAutoConfiguration() {
        log.info("AspectAutoConfiguration initialize.");
    }

    @Bean
    public MetricsAspect metricsAspect() {
        return new MetricsAspect();
    }
}

4. 配置文件MetricsProperties

@ConfigurationProperties(prefix = "common.metrics")
public class MetricsProperties {
    public Boolean getKeepAlive() {
        return keepAlive;
    }

    public void setKeepAlive(Boolean keepAlive) {
        this.keepAlive = keepAlive;
    }

    private Boolean keepAlive = true;

}

5. 其它配置

配置自动注入

配置resource.META-INF.spring.org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,增加AspectAutoConfiguration类路径。

配置文件提示

{
  "groups": [],
  "properties": [
    {
      "name": "common.metrics.keepAlive",
      "type": "java.lang.Boolean",
      "sourceType": "com.common.config.metrics.properties.MetricsProperties"
    }
  ]
}

三、自定义spring的profile限定注解

1. 注解@RunOnProfiles

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface RunOnProfiles {
    /**
     * Profile name array,eg,dev,pro.
     */
    String[] value() default {};

    /**
     * Skip the code of  the method of the class or method itself.
     */
    boolean skip() default true;

}

2. 切面RunOnProfilesAspect

@Aspect
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class RunOnProfilesAspect {
    @Autowired
    private ApplicationContext applicationContext;

    @Pointcut("@annotation(com.common.config.profiles.annotation.RunOnProfiles)")
    public void withAnnotationMethod() {
    }

    @Pointcut("@within(com.common.config.profiles.annotation.RunOnProfiles)")
    public void withAnnotationClass() {
    }

    @Around("withAnnotationMethod() || withAnnotationClass()")
    public Object runsOnAspect(ProceedingJoinPoint pjp) throws Throwable {
        var activeArray = applicationContext.getEnvironment().getActiveProfiles();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        RunOnProfiles runOnProfiles = signature.getMethod().getAnnotation(RunOnProfiles.class);
        if (runOnProfiles == null) {
            return null;
        }
        var profilesArray = runOnProfiles.value();
        if (profilesArray == null || profilesArray.length == 0) {
            return pjp.proceed();
        }
        for (var profile : profilesArray) {
            for (var p : activeArray) {
                if (p.equals(profile)) {
                    return pjp.proceed();
                }
            }
        }
        return null;
    }
}

3. 自动注入AutoConfiguration

@AutoConfiguration
@Slf4j
public class RunsOnProfilesAutoConfiguration {

    public RunsOnProfilesAutoConfiguration() {
        log.info("RunsOnProfilesAutoConfiguration initialize.");
    }

    @Bean
    public RunOnProfilesAspect runsOnProfilesAspect() {
        return new RunOnProfilesAspect();
    }
}

4. 其它配置

配置自动注入

配置resource.META-INF.spring.org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,增加RunsOnProfilesAutoConfiguration类路径。

参考

[1] springboot doc configuration metadata

标签:Metrics,name,Autoconfiguration,class,metrics,详解,参数,public,String
From: https://www.cnblogs.com/bloodcolding/p/17284491.html

相关文章

  • 【UNCTF逆向】ezmaze详解
    题目ezmaze解法题目下载下来是一个ezmaze.exe文件,用exeinfo打开看一下好像还可以,用ida打开看看刚开始我甚至找不到这个界面,问了一名比较厉害的同学,他告诉我就一个个函数找找看,可能会找到可疑的内容,我就一个个找,最后锁定了这个140001490。打开是这样的反编译一下有点......
  • python参数,既有单引号又有双引号的解决办法
       python参数,既有单引号又有双引号的解决办法:使用双引号引起来,中间的双引号使用\转义,中间的单引号不转义,如下python3./pluginTool.pyout/228out/tr069Transform_ass.xmltr069Transform_assmodify/tr069Transform_ass:Device/tr069Transform_ass:Services/tr069Tran......
  • C语言再学习 -- 详解C++/C 面试题 2
    (经典)C语言测试:想成为嵌入式程序员应知道的0x10个基本问题。参看:嵌入式程序员面试问题集锦1、用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #defineSENCONDS_PER_YEAR(60*60*24*365)UL解答:#define声明一个常量,使用计算常量表达式的值来表明一年中有多少......
  • Spring事件详解,Spring-Event源码详解,一文搞透Spring事件管理
    文章目录一、Java中事件/监听器编程模型1、Java中Observable/Observer事件监听(1)代码实例(2)Java9新的事件监听2、面向接口的事件/监听器设计模式3、面向注解的事件/监听器设计模式二、Spring事件1、Spring标准事件-ApplicationEvent2、基于接口的Spring事件监听器代码实例3、基于注......
  • Spring 类型转换详解,SpringBean创建时属性类型转换源码详解
    文章目录一、概述1、Spring类型转换的实现2、使用场景3、源码分析二、基于JavaBeans接口的类型转换1、代码实例2、Spring內建PropertyEditor扩展ByteArrayPropertyEditor3、自定义PropertyEditor扩展整合到springframework代码实例SpringPropertyEditor的设计缺陷三、Spr......
  • Go mod包依赖管理工具使用详解
    我个人觉得,一个包管理工具应该有以下功能:基本功能依赖管理依赖包版本控制对应的包管理平台可以私有化部署加分:代码包是否可以复用构建,测试,打包发布上线对比上面几点:目前做的最好的也就maven了,gradle没有使用过,不知道。今天主角是gomod,先来谈谈没有使用gomod之前的问题。使......
  • echart js给相关参数赋值的问题
    需要在初始化的时候加上相关的定义,后面用js进行动态赋值的时候才能找到,否则报Undefined,定义:varoption={title:{text:'',textStyle:{color:'#5AC8FA'}},//color:'#00ff00',legend:{show:true,data:[],x:'right&......
  • S5PV210开发 -- UART 详解
    上一篇文章系统的讲了一下通信的分类,包括并行通信,串行通信。串行通信的分类,包括同步通信,异步通信。这篇文章我们主要讲一下UART 串口编程,我们并不陌生。之前讲过RS485通信,参看:UNIX再学习--RS485串口编程再者,参看:日常生活小技巧--UART回环测试一、基本概念 参看:UART--维......
  • mybatis调用存储过程,并返回out参数
        ......
  • PHPExcel 中文使用手册详解
     1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848......