0


Autoconfiguration详解

文章目录

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. @ConditianalOnClass2. @ConditionalOnMissingClass3. @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。

示例:

# comments
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
org.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

的参数都会被匹配到,你也可以使用

havingValue

matchIfMissing

属性闯将更多的校验。

例子:

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

;
属性名类型解析nameString[] name() default {};配置项全称,如果有prefix,可以省略prefix中的前缀部分prefixString prefix() default “”;统一的配置项前缀havingValueString havingValue() default “”;配置项需要匹配的内容,如果没有指定,那么配置的值等于

false

时结果为false,否则结果都为truematchIfMissingboolean 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<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@interfaceMetrics{/**
     * 在方法成功执行后打点,记录方法的执行时间发送到指标系统,默认开启
     */booleanrecordSuccessMetrics()defaulttrue;/**
     * 在方法成功失败后打点,记录方法的执行时间发送到指标系统,默认开启
     */booleanrecordFailMetrics()defaulttrue;/**
     * 通过日志记录请求参数,默认开启
     */booleanlogParameters()defaulttrue;/**
     * 通过日志记录方法返回值,默认开启
     */booleanlogReturn()defaulttrue;/**
     * 出现异常后通过日志记录异常信息,默认开启
     */booleanlogException()defaulttrue;/**
     * 出现异常后忽略异常返回默认值,默认关闭
     */booleanignoreException()defaultfalse;

2. 切面MetricsAspect

@Aspect@Slf4j@Order(Ordered.HIGHEST_PRECEDENCE)publicclassMetricsAspect{/**
     * 让Spring帮我们注入ObjectMapper,以方便通过JSON序列化来记录方法入参和出参
     */@ResourceprivateObjectMapper objectMapper;/**
     * 实现一个返回Java基本类型默认值的工具。其实,你也可以逐一写很多if-else判断类型,然后手动设置其默认值。
     * 这里为了减少代码量用了一个小技巧,即通过初始化一个具有1个元素的数组,然后通过获取这个数组的值来获取基本类型默认值
     */privatestaticfinalMap<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)));publicstatic<T>TgetDefaultValue(Class<T> clazz){//noinspection uncheckedreturn(T) DEFAULT_VALUES.get(clazz);}/**
     * 标记了Metrics注解的方法进行匹配
     */@Pointcut("@annotation(com.common.config.metrics.annotation.Metrics)")publicvoidwithMetricsAnnotationMethod(){}/**
     * within指示器实现了匹配那些类型上标记了@RestController注解的方法
     * 注意这里使用了@,标识了对注解标注的目标进行切入
     */@Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")publicvoidcontrollerBean(){}@Pointcut("@within(com.common.config.metrics.annotation.Metrics)")publicvoidwithMetricsAnnotationClass(){}@Around("controllerBean() || withMetricsAnnotationMethod() || withMetricsAnnotationClass()")publicObjectmetrics(ProceedingJoinPoint pjp)throwsThrowable{// 通过连接点获取方法签名和方法上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){@MetricsfinalclassInnerClass{}
            metrics =InnerClass.class.getAnnotation(Metrics.class);}// 尝试从请求上下文获得请求URLRequestAttributes 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)publicclassAspectAutoConfiguration{publicAspectAutoConfiguration(){
        log.info("AspectAutoConfiguration initialize.");}@BeanpublicMetricsAspectmetricsAspect(){returnnewMetricsAspect();}}

4. 配置文件MetricsProperties

@ConfigurationProperties(prefix ="common.metrics")publicclassMetricsProperties{publicBooleangetKeepAlive(){return keepAlive;}publicvoidsetKeepAlive(Boolean keepAlive){this.keepAlive = keepAlive;}privateBoolean 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@interfaceRunOnProfiles{/**
     * Profile name array,eg,dev,pro.
     */String[]value()default{};/**
     * Skip the code of  the method of the class or method itself.
     */booleanskip()defaulttrue;}

2. 切面RunOnProfilesAspect

@Aspect@Slf4j@Order(Ordered.HIGHEST_PRECEDENCE)@ComponentpublicclassRunOnProfilesAspect{@AutowiredprivateApplicationContext applicationContext;@Pointcut("@annotation(com.common.config.profiles.annotation.RunOnProfiles)")publicvoidwithAnnotationMethod(){}@Pointcut("@within(com.common.config.profiles.annotation.RunOnProfiles)")publicvoidwithAnnotationClass(){}@Around("withAnnotationMethod() || withAnnotationClass()")publicObjectrunsOnAspect(ProceedingJoinPoint pjp)throwsThrowable{var activeArray = applicationContext.getEnvironment().getActiveProfiles();MethodSignature signature =(MethodSignature) pjp.getSignature();RunOnProfiles runOnProfiles = signature.getMethod().getAnnotation(RunOnProfiles.class);if(runOnProfiles ==null){returnnull;}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();}}}returnnull;}}

3. 自动注入AutoConfiguration

@AutoConfiguration@Slf4jpublicclassRunsOnProfilesAutoConfiguration{publicRunsOnProfilesAutoConfiguration(){
        log.info("RunsOnProfilesAutoConfiguration initialize.");}@BeanpublicRunOnProfilesAspectrunsOnProfilesAspect(){returnnewRunOnProfilesAspect();}}

4. 其它配置

配置自动注入

配置resource.META-INF.spring.org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,增加

RunsOnProfilesAutoConfiguration

类路径。

参考

[1] springboot doc configuration metadata

标签: java spring servlet

本文转载自: https://blog.csdn.net/weixin_56838459/article/details/129979200
版权归原作者 千秋不醉 所有, 如有侵权,请联系我们删除。

“Autoconfiguration详解”的评论:

还没有评论