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

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


}
}

如果使用@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();
}
}

  • 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);
}

  • 完整示例

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

自动配置类

@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 ;
}
}

自定义注解

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {

/**
* <p>操作说明</p>
* @return
*/
String value() default "" ;
}

属性key配置

/**
* 日志功能属性配置
* @author xg
*/
@ConfigurationProperties(prefix = "logs")
public class LogsProperties {
/**
* 切入点定义<br/>
* 示例:execution(public * com.pack.controller.*.*(..))
*/
private String pointcut ;
/**
* 是否开启日志功能
*/
private boolean enabled = true ;
}

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();
}
}

配置META-INF\spring.factories

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

以上就实现自定义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

2018-01-29 15:23:14

网络知识点软件测试

2015-08-12 15:12:56

黑客攻击云安全云服务

2021-08-13 08:36:15

SpringMVC自定义

2021-07-06 14:56:20

深度学习编程人工智能

2011-04-01 15:28:40

Zabbix配置安装

2021-02-03 13:22:53

区块链数据隐私

2019-09-18 17:35:52

2009-06-24 10:45:42

Linux

2012-06-27 09:11:47

2012-06-26 10:13:55

2011-04-01 11:16:06

hessian

2019-03-31 23:16:44

网络前端协议
点赞
收藏

51CTO技术栈公众号