前面我们简单介绍了如何使用消息中间件 Apache Pulsar ,但是在项目中那样使用,显然是不太好的,不管从易用性和扩展性来看,都是远远不够, 为了和springboot项目集成,写一个pulsar-spring-boot-starter是非常有必要的,在此之前,我们先看看一个starter需要些什么。
spring-boot的强大之处在于其提供的大量starter组件,基本涵盖了我们开发中的各个技术领域,比如数据库访问有jdbc、jpa,缓存有redis,全文检索有elasticsearch,消息队列有amqp、kafka等等。
在项目中你只需要按需引入相应的依赖 spring-boot-starter-xxx ,然后只需要替换对应的配置参数即可,就能快速使用对应的功能,不得不说简直是为开发者插上了翅膀。
对于starter模块如何命名,spring官方是这样建议:
如果你之前有看过spring官方starter组件,你会发现主要是基于AutoConfigure及@Enable来实现的。
当启动Spring Boot项目时这些配置都会被加载(这么多的配置全部加载并处理,难怪启动那么慢)。
在starter中依赖的具体实现包中,一般都会提供一个@Enable注解作为部分扩展功能的开关,我们可以在系统中通过该注解引入按需引入配置
AutoConfigure配置的一定会被加载,而@Enable有开发者选择使用使用,当然有些组件是没有AutoConfigure,必须通过@Enable来启用
下面我们先对这块内容做个简单的认识,方便后续在写具体starter时知道怎么写以及为什么那样写。
在目录中创建src/main/resources/MATE-INF中创建文件spring.factories,定义SpringBoot应用启动时的需要注册的配置,这个主要是基于SPI机制来实现, 下面是当前spring-boot-autoconfigure中spring.factories文件的部分内容
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,
...
配置在这里的带有@Configuration的类(如果没有被Conditional条件过滤掉)都会作为配置将相关Bean注册到Spring容器.
主要实现基于@SpringBootApplication注解上的注解@EnableAutoConfiguration
以Spring Aop相关的注解@EnableAspectJAutoProxy为例,我们看下 Spring官方是怎么使用@Enable注解来实现配置加载的:
@EnableAspectJAutoProxy
改注解除了一般注解的基础(@Target、@Retention)元素外,还包含了两个配置属性proxyTargetClass、exposeProxy以及一个@Import
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
@Import
在@Import中我们可以配置需要导入的配置类,有以下几个选择:
@Import(AspectJAutoProxyRegistrar.class)
ImportBeanDefinitionRegistrar
在上面@EnableAspectJAutoProxy注解上,通过@Import,引入了AspectJAutoProxyRegistrar,而该类又实现了接口ImportBeanDefinitionRegistrar, 该接口能够通过BeanDefinitionRegistry向Spring容器注册我们期望的BeanDefinition,看代码:
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
这里我们可以拿到@EnableAspectJAutoProxy的元数据以及对应的属性配置,这样就可以基于开发者的配置实现不同逻辑
ImportSelector
上面说到了,@Import还可以配置实现了ImportSelector接口的类,进而控制具体需要使用的Configuration,下面是@EnableAsync中@Import配置的类
public class AsyncConfigurationSelector extends AdviceModeImportSelector< EnableAsync > {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
@Override
@Nullable
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {ProxyAsyncConfiguration.class.getName()};
case ASPECTJ:
return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
ImportAware
同样和@Import配合使用,针对基于ImportSelector选择的Configuration,只要实现了ImportAware接口,就可以拿到@Import对应@Enable注解的元数据
@Configuration
public abstract class AbstractAsyncConfiguration implements ImportAware {
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableAsync = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
if (this.enableAsync == null) {
throw new IllegalArgumentException(
"@EnableAsync is not present on importing class " + importMetadata.getClassName());
}
}
}
上面主要根据Spring源代码中的例子,了解@Enable、@Import、ImportBeanDefinitionRegistrar、ImportSelector、ImportAware如何搭配使用, 从而实现Spring的动态配置,用一张关系图表示:
relation
spring-boot-configuration-processor
我们知道SpringBoot的配置我们都会写在application.yml(.properties)文件中,为了简化配置工作,如果能有智能提示就好了。这不,别人也想到了。只用这样做:
< dependency >
< groupId >org.springframework.boot< /groupId >
< artifactId >spring-boot-configuration-processor< /artifactId >
< optional >true< /optional >
< /dependency >
@Data
@ConfigurationProperties(prefix = "myProp")
public class MyProperties {
private Boolean enable;
private String name;
}
@Configuration
@EnableConfigurationProperties({MyProperties.class})
public class WebApiAutoConfiguration {
}
mvn clean install
@Conditional
实现spring bean的可插拔,我们可以基于属性、配置、类或者Bean来控制配置(@Configuration)是否生效,常见的有下面的这些:
AutoConfigure和@Enable
AutoConfigure是在spring.factories中配置了就会加载,但是可以通过@Conditional让配置中的Bean不生效;@Enable需要显示地使用才能有效,且先于AutoConfigure生效,从而可以配合@Conditional来阻断AutoConfigure的配置
关于Spring框架的学习后面会慢慢增多,我们会从原理到实践来介绍其功能,如果你有感兴趣的技术点或者开发中的问题,可以通过留言进行交流分享。
全部0条评论
快来发表一下你的评论吧 !