如果要实现一个自定义的starter,首先需要引入两个依赖spring-boot的jar包:spring-boot-autoconfigure和spring-boot-configuration-processor:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency>
在resource中新建META-INF文件夹,创建spring.factories,比如:
#定义自动装配的类=>RedissonCofigurationorg.springframework.boot.autoconfigure.EnableAutoConfiguration=com.study.configuration.RedissonConfiguration
同时你的starter的名称:
<groupId>com.study</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>1.0.0</version>
同时需要基于存在某种条件才进行装配时,可以使用@ContionOnClass.
ElementType.TYPE, ElementType.METHOD }) ({ RetentionPolicy.RUNTIME) (OnClassCondition.class) (public@interfaceConditionalOnClass { Class<?>[] value() default {}; String[] name() default {}; }
从源码上看,只有在classpath下能找到你需要的conditionOnClass类才会构建这个bean。比如你想写一个redisson的自动装配:
/*** redisson配置:配置、自动配置、配置条件*/value=RedissonProperties.class) (RedissonProperties.class) (publicclassRedissonConfiguration { }
使用@ConditionOnMissingBean:
,它是修饰bean的一个注解,主要实现的是,当你的bean被注册之后,如果而注册相同类型的bean,就不会成功,它会保证你的bean只有一个,即你的实例只有一个,当你注册多个相同的bean时,会出现异常,以此来告诉开发人员。
通常的相关注解:
// 当给定的bean存在时,则实例化当前Bean// 当给定的bean不存在时,则实例化当前Bean// 当给定的类名在类路径上存在,则实例化当前Bean// 当给定的类名在类路径上不存在,则实例化当前Bean
创建RessionClient的方式:基于对应的方式创建客户端,因为Ression有很多模式,哨兵、主从、单例、集群、云托管模式,拿到对应的模式的配置后,创建对应的客户端bean:
RedissonClient.class) (publicRedissonClientredissonClient() { // 创建Redisson客户端对象对象returnRedisson.create(config); }
同时还需要一些特定的信息:
相关bean:lockAop分布式锁、MQAop发送AOP、RedissonBinary操作对象二进制、RedissonObject操作对象、RedissonCollection操作集合、RedissonClient重要,此时就可以基于的方式进行创建,从而实现自定装配。此时可以基于分布式锁Aop切面来做拦截,对分布式锁进行增强操作,也即对当前拿到的锁信息进行判断。对锁的模式进行判断,如果当前的锁模式为自动的,则此时根据你所的key进行判断,如果keys的长度>1,则使用红锁,否者使用可重入式锁。如果锁模式不是联锁&&红锁&&长度大于1,此时会抛异常
如果是公平锁,则直接处理,如果是红锁,则需要变量keys,对锁进行添加到RLock中,对锁进行遍历,添加到数组中,然后将其重新赋值给红锁,否者放入到可重入式锁,或者读锁或者写锁中。执行aop.
那可自动装配又是怎样实现的呢?其关键在于@SpringBootApplication这个注解上,这个组件是一个组合注解:
ElementType.TYPE) (RetentionPolicy.RUNTIME) (//springboot配置注解//可以自动注入配置excludeFilters= { (type=FilterType.CUSTOM, classes=TypeExcludeFilter.class), (type=FilterType.CUSTOM, classes=AutoConfigurationExcludeFilter.class) }) //进行组件扫描,同时排掉过滤信息 (public@interfaceSpringBootApplication { // 省略代码,主要包含的方法:排掉特定自动注入的配置,通过名称或者类方式,进行基包扫描、或者classes、代理bean方法等}
同时我们可以看到SpringBootApplication里面的所有方法,都使用了一个注解@AliasFor。那这个组件有什么用呢?这个注解用于桥接到其它注解,该注解的属性中指定的所桥接的注解类,减少用户使用多注解带来的麻烦。
其关键就在@EnableAutoConfiguration这个注解中,这个注解里面有一个@Import注解,里面这个类自动配置导入选择器类:
ElementType.TYPE) (RetentionPolicy.RUNTIME) (AutoConfigurationImportSelector.class) (public@interfaceEnableAutoConfiguration { StringENABLED_OVERRIDE_PROPERTY="spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
如果我们想使用xml进行配置的话,此时在springboot启动的时候可以使用@Import将配置进行导入,实现配置注入的目的。而AutoConfigurationImportSelector实现了DeferredImportSelector延迟导入选择器,也即ImportSelector的子类。那ImportSelector里面有什么方法呢?里面有两个方法一个是选择导入的方法、一个是排掉过滤的方法,下面可以看到选择导入方法的入参是导入类元数据。
publicinterfaceImportSelector { String[] selectImports(AnnotationMetadataimportingClassMetadata); }
那这个选择导入的过程又是怎样的呢?
SpringFactoriesLoader加载器加载指定ClassLoader下面的所有的META-INF/spring.factories文件,并将文件解析内容存在Map中。然后通过loadFactoryNames传递过来的class的名称从map中获取该类的配置列表。通过Set集合进行去重操作。执行过滤组件操作,而这些操作都是在AutoConfigurationImportFilter接口下的组件实现的,也即FilterSpringBootCondition实现抽象类的。下面有OnBeanCondition、OnClassCondition、OnWebApplicationCondition的getOutcomes方法。
执行fire操作,fireAutoConfigurationImportEvents,此时会执行事件注册。
而其重要的方法就是getAutoConfigurationEntry就是自动装配的重点。