文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Autoconfiguration详解

2023-12-25 12:03

关注

Autoconfiguration详解

一、 理解自动装配bean

1. 常用注解

  1. @AutoConfiguration(每个配置类都要加上)
    1. Class[] after() default {};
    2. Class[] before() default {};
    3. 以上两个配置可以控制加载顺序;
    4. 不需要再增加@Configuration注解;
  2. @AutoConfigureBefore and @AutoConfigureAfter
  3. @Configuration
  4. @Conditional(后面会详细讲到)
    1. @ConditianalOnClass
    2. @ConditionalOnMissingClass
    3. @ConditionalOnWebApplication:只在web应用中加载;
  5. @EnableConfigurationProperties:配置文件参数内容,参照类RedisProperties;
    1. @ConfigurationProperties(prefix = "spring.redis"),该注解展示了配置文件前缀;
  6. @DependsOn:列举一些前置的注入bean,以备用,用在类上需要有 @Component自动扫描的时候才能生效;
    1. 实际上控制了bean加载的顺序,优先加载指定的bean,然后加载当前bean;
    2. 销毁的时候,注解的bean优先与于依赖的bean销毁;

2. 定位自动装配的候选类

springboot 框架会自动扫描 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 进行引入,所以需要自动注入的Configuration文件都写在这个文件中。每个class一行。

这里本质上是一个自动版的@Import。

示例:

# commentsorg.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfigurationorg.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfigurationorg.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration

如果需要引入特定的Component,使用@Import注解。

3. 条件注解

在所有自动装配类出现的地方,我们都因该时加上@Conditional注解,允许使用的开发人员覆盖自动装配的bean,当然他们也可以选择什么也不做,使用默认的配置。

Spring Boot 提供了一些条件注解,可以注解在@Configuration类或者@Bean方法上。

3.1 有关类的判断

对于@Configuration类来说,@ConditionalOnClass@ConditionalOnMissingClass代表了在指定类存在或者不存在的时候进行加载。因为实际上注解的元数据使用ASM技术进行解析,所以可以使用**value参数来指定特定的类的class对象(可以是多个),或者使用name参数**来指定特定的类名(可以是多个),两种方式所指向的类即使不存在也不影响正常执行。

@Bean方法返回值是条件注解的的目标之时,可能会因为JVM加载顺序的问题导致加载失败,上文提到的两个注解可以用在@Bean方法上。

3.2 有关bean的判断

@ConditionalOnBean@ConditionalOnMissingBean,代表在指定bean存在或者不存在时加载。value参数可以指定bean的class(多个),name可以指定bean的名称(多个)。search参数允许你限制ApplicationContext即应用上下文的搜索范围,可选当前上下文,继承上层,或者是全部(默认)。

@Bean方法上使用时,默认参数为当前方法返回类型。

在使用@Bean注解时,建议使用具体类型而不是父类型进行指代。

3.3 配置条件

@ConditionalOnProperty,指定配置项文件(例如dev,pro),prefix属性规定了配置前缀,name属性指定了应该被检查的参数。默认,所有存在且不等于false的参数都会被匹配到,你也可以使用havingValuematchIfMissing属性闯将更多的校验。

例子:@ConditionalOnProperty(name = "spring.redis.client-type", havingValue = "lettuce", matchIfMissing = true);

属性名类型解析
nameString[] name() default {};配置项全称,如果有prefix,可以省略prefix中的前缀部分
prefixString prefix() default “”;统一的配置项前缀
havingValueString havingValue() default “”;配置项需要匹配的内容,如果没有指定,那么配置的值等于false时结果为false,否则结果都为true
matchIfMissingboolean matchIfMissing() default false;配置项不存在时的配置,默认为false
3.4 源文件条件

@ConditionalOnResource,指定源文件存在时引入。

例如:@ConditionalOnResource(resources = {"classpath:test.log"});

3.5 web 应用条件

@ConditionalOnWebApplication@ConditionalOnNotWebApplication ,web应用或者不是web应用时启用,以下部分只要满足一个条件即为web 应用。

servlet-based web 应用特点:

  1. 使用 Spring WebApplicationContext;
  2. 定义了一个session作用域的bean;
  3. 有一个WebApplicationContext;

reactive web 应用特点:

  1. 使用了ReactiveWebApplicationContext;

  2. 有一个ConfigurableReactiveWebEnvironment;

ConditionalOnWarDeployment,仅限于使用war进行部署的场景,在嵌入式tomcat的场景里不会启用;

3.6 Spel表单式条件

ConditionalOnWarDeployment ,使用Spel表达式返回结果进行判断。

注意:在表达式中引用一个bean会导致这个bean非常早的被加载,此时还没有进行预加载(例如配置项的绑定),可能会导致不完成的加载。

二、自动注入配置基础

  1. @EnableConfigurationProperties(CommonRedisProperties.class) 注解configuration类;
  2. @ConfigurationProperties(prefix = "myserver")注解配置文件类,prefix标明配置文件的前缀;
  3. public RedisTemplate 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 {        @Resource    private ObjectMapper objectMapper;        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);    }        @Pointcut("@annotation(com.common.config.metrics.annotation.Metrics)")    public void withMetricsAnnotationMethod() {    }        @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@Indexedpublic @interface RunOnProfiles {        String[] value() default {};        boolean skip() default true;}

2. 切面RunOnProfilesAspect

@Aspect@Slf4j@Order(Ordered.HIGHEST_PRECEDENCE)@Componentpublic 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@Slf4jpublic 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

来源地址:https://blog.csdn.net/weixin_56838459/article/details/129979200

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯