SpringBoot自定义自动配置这些知识点,你需要了解

开发 前端
如果使用@ConditionalOnClass或@ConditionalOnMissingClass作为元注释的一部分来组合自己的组合注释,则必须使用name,因为在这种情况下引用类不会被处理。

理解自动配置bean

在底层,自动配置是用标准的@Configuration类实现的。附加的@Conditional注释用于约束何时应用自动配置。通常,自动配置类使用@ConditionalOnClass和@ConditionalOnMissingBean注释。这确保了自动配置仅在找到相关类且尚未声明自己的@configuration时适用。

你可以浏览spring-boot-autoconfigure的源代码,以查看Spring提供的@Configuration类(参见META-INF/spring.factories 文件)。

定位候选自动配置

Spring Boot检查是否存在META-INF/spring.factories文件在你发布的jar中。该文件应该在EnableAutoConfiguration为key下列出你的配置类,如下例所示:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pack.bus.autoconfigure.BusAutoConfiguration,\
com.pack.bus.autoconfigure.BusWebAutoConfiguration
  • 1.
  • 2.
  • 3.

自动配置只能以这种方式加载。确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描的目标。此外,自动配置类不应该允许组件扫描来查找其他组件。应该使用特定的@Imports。

如果你的配置需要按特定顺序应用,你可以使用@AutoConfigureAfter或@AutoConfigureBefore注释。例如,如果你提供了特定于web的配置,你的类可能需要应用在WebMvcAutoConfiguration之后。

可以使用@AutoConfigureOrder。该注释具有与常规@Order注释相同的语义,但为自动配置类提供了专用的顺序。

与标准的@Configuration类一样,自动配置类的应用顺序只影响其bean定义的顺序。随后创建这些bean的顺序不受影响,由每个bean的依赖关系和@DependsOn关系决定。

条件注释

你几乎总是希望在自动配置类中包含一个或多个@Conditional注解。@ConditionalOnMissingBean注解是一个常见的例子,它允许开发人员在对默认值不满足时覆盖自动配置。

Spring Boot包含很多@Conditional注解,你可以在自己的代码中重用这些注解,方法是注解@Configuration类或单独的@Bean方法。这些注释包括:

  • Class Conditions

@ConditionalOnClass和@ConditionalOnMissingClass注解让@Configuration类根据特定类的存在与否被包含。由于注释元数据是通过ASM解析的,因此你可以使用value属性来引用真正的类,即使这个类可能实际上没有出现在正在运行的应用程序类路径中。如果想用字符串指定类名,也可以使用name属性。

这种机制不适用于@Bean方法,因为@Bean方法的返回类型通常是条件的目标:在方法的条件应用之前,JVM将加载类并可能处理方法引用,如果类不存在,则这些引用将失败。

为了处理这种情况,可以使用一个单独的@Configuration类来隔离这种情况,如下面的例子所示:

@Configuration(proxyBeanMethods = false)
// Some conditions ...
public class MyAutoConfiguration {
  // Auto-configured beans ...
  @Configuration(proxyBeanMethods = false)
  @ConditionalOnClass(SomeService.class)
  public static class SomeServiceConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public SomeService someService() {
      return new SomeService();
    }


  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

如果使用@ConditionalOnClass或@ConditionalOnMissingClass作为元注释的一部分来组合自己的组合注释,则必须使用name,因为在这种情况下引用类不会被处理。

  • Bean Conditions

@ConditionalOnBean和@ConditionalOnMissingBean注解让一个bean根据特定bean的存在与否被包含进来。可以使用value属性按类型指定bean,也可以使用name指定bean。search属性允许您限制在搜索bean时应该考虑的ApplicationContext层次结构。

当放在@Bean方法上时,目标类型默认为方法的返回类型,如下面的例子所示:

@Configuration(proxyBeanMethods = false)
public class MyAutoConfiguration {


  @Bean
  @ConditionalOnMissingBean
  public SomeService someService() {
    return new SomeService();
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

  • Property Conditions

@ConditionalOnProperty注解让配置基于Spring环境属性包含。使用prefix和name属性指定要检查的属性。默认情况下,匹配任何存在且不等于false的属性。你还可以使用havingValue和matchIfMissing属性来创建更高级的检查。

  • Resource Conditions

@ConditionalOnResource注解让配置只在特定资源存在时才包含。可以使用常用的Spring约定来指定资源,如下面的例子所示。

  • Web Application Conditions

@ConditionalOnWebApplication和@ConditionalOnNotWebApplication注解让应用程序根据是否是“web应用程序”来包含配置。基于servlet的web应用程序是任何使用Spring WebApplicationContext、定义会话范围或具有ConfigurableWebEnvironment的应用程序。任何使用ReactiveWebApplicationContext或者ConfigurableReactiveWebEnvironment的应用都可以被称为响应式web应用。

@ConditionalOnWarDeployment注解根据应用程序是否是部署到容器中的传统WAR应用程序来包含配置。此条件不适用于与嵌入式服务器一起运行的应用程序。

  • SpEL Expression Conditions

@ConditionalOnExpression注解让配置基于SpEL表达式的结果包含。

创建自己的Starter

  • 命名

你应该确保为你的starter程序提供适当的命名空间。即使你用了不同的Maven groupId,也不要用spring-boot来启动模块名。我们可能会在未来为你的自动配置提供官方支持。

根据经验,你应该在starter之后命名一个组合模块。例如,假设你正在为“acme”创建一个starter程序,并且你将自动配置模块命名为acme-spring-boot,而starter程序命名为acme-spring-boot-starter。如果只有一个模块组合了这两个模块,请将其命名为acme-spring-boot-starter。

  • 配置key

如果starter提供了配置key,它们使用唯一的命名空间。不要把key放在Spring Boot使用的命名空间中(比如server、management、Spring等)。

为每个属性添加javadoc,确保配置项有文档记录,如下面的例子所示。

@ConfigurationProperties("acme")
public class AcmeProperties {


  /**
   * Whether to check the location of acme resources.
   */
  private boolean checkLocation = true;
  /**
   * Timeout for establishing a connection to the acme server.
   */
  private Duration loginTimeout = Duration.ofSeconds(3);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

  • 完整示例

本示例主要功能是实现日志记录功能

自动配置类

@Configuration
@EnableConfigurationProperties(LogsProperties.class)
@ConditionalOnProperty(prefix = "logs", name = "enabled", havingValue = "true")
@EnableAspectJAutoProxy
public class LogsAutoConfiguration {
  
  private static final Logger logger = LoggerFactory.getLogger(LogsAutoConfiguration.class) ;
  
  @Resource
  private LogsProperties logsProperties ;
  
  @Bean
  public AspectJExpressionPointcutAdvisor logAdvisor() {
    AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor() ;
    logger.info("执行表达式:{}", logsProperties.getPointcut()) ;
    advisor.setExpression(logsProperties.getPointcut()) ;
    advisor.setAdvice(new SystemAroundOperator()) ;
    return advisor ;
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

自定义注解

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {
  
  /**
   * <p>操作说明</p>
   * @return
  */
  String value() default "" ;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

属性key配置

/**
 *   日志功能属性配置
 *   @author xg
 */
@ConfigurationProperties(prefix = "logs")
public class LogsProperties {
  /**
   *   切入点定义<br/>
   *    示例:execution(public * com.pack.controller.*.*(..))
   */
  private String pointcut ;
  /**
   *   是否开启日志功能
   */
  private boolean enabled = true ;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

Advice定义

public class SystemAroundOperator implements MethodInterceptor {


  private static final Logger logger = LoggerFactory.getLogger(SystemAroundOperator.class);


  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
    // 开始执行时间
    long start = System.currentTimeMillis();
    Method method = invocation.getMethod() ;
    SystemLog annoLog = null ;
    if (method.isAnnotationPresent(SystemLog.class)) {
      annoLog = method.getAnnotation(SystemLog.class) ;
      String value = annoLog.value() ;
      try {
        Object result = invocation.proceed() ;
        // 方法执行时间
        Long execTime = System.currentTimeMillis() - start ;
        logger.info("{}, 业务执行时间:{} ms", value, execTime) ;
        return result ;
      } catch (Throwable t) {
        Long execTime = System.currentTimeMillis() - start ;
        logger.info("{}, 业务执行时间:{} ms,发生异常信息:{}", value, execTime, t.getMessage()) ;
        throw t ;
      }
    }
    return invocation.proceed();
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

配置META-INF\spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pack.config.LogsAutoConfiguration
  • 1.
  • 2.

以上就实现自定义starter的流程。

责任编辑:武晓燕 来源: 实战案例锦集
相关推荐

2023-12-15 08:07:37

响应式布局官网

2022-10-26 07:21:15

网络视频开发

2021-04-27 22:27:19

手机安卓苹果

2019-11-25 21:46:12

数据湖云计算数据仓库

2010-03-18 13:48:14

Linux新手

2024-02-26 08:19:00

WebSpring容器

2021-05-05 11:32:36

MySQL数据库索引

2024-01-24 11:59:44

Django自定义字段Python

2025-02-08 10:29:03

2018-01-29 15:23:14

网络知识点软件测试

2021-07-06 14:56:20

深度学习编程人工智能

2015-08-12 15:12:56

黑客攻击云安全云服务

2021-08-13 08:36:15

SpringMVC自定义

2021-02-03 13:22:53

区块链数据隐私

2019-09-18 17:35:52

2011-04-01 15:28:40

Zabbix配置安装

2009-06-24 10:45:42

Linux

2012-06-26 10:13:55

2011-04-01 11:16:06

hessian

2012-06-27 09:11:47

点赞
收藏

51CTO技术栈公众号