SpringBoot 扩展机制 ——Spring.factories文件 如何将外部项目加入Spring容器
▶ 简介
简单说用于模块之间解耦,外部项目下各种配置、类加入到Spring容器中进行管理,类似JAVA中SPI机制。机制自行百度。
▶ 方式
将外部类引入IOC容器两种方式
1.@import
2.加入Spring.factories文件 — 主讲这个
▶ Spring.factories文件
1.在resource目录下 META-INF/ Spring.factories 文件
2.格式:自定义需要扫描到类(接口),多个用 ”,\“ 隔开
3.SpringFactoriesLoader类会去扫描加载文件下定义类,加入容器
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mostsnails.common.base.utils.config.WebLogAutoConfiguration,\
com.mostsnails.common.base.utils.config.RedisAutoConfiguration,\
com.mostsnails.common.base.utils.test.TestClass
注意这里使用EnableAutoConfiguration ,SpringBoot启动类@SpringBootApplication注解包下有@EnableAutoConfiguration和@Component 具体作用开启自动装备,将满足条件@Configuration配置加入到SpringBoot中。
Spring.factories也可以包含其他类型
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
....
可以去看项目下spring-boot-***-start jar包
外部项目下Redis配置类
/**
* Redis配置
*
* @Author MostSnails
* @Version 1.0
*/
@Configurable
public class RedisAutoConfiguration {
private static org.slf4j.Logger log = LoggerFactory.getLogger(RedisAutoConfiguration.class);
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
// 创建RedisTemplate<String, Object>对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置连接工厂
template.setConnectionFactory(factory);
// 定义Jackson2JsonRedisSerializer序列化对象
Jackson2JsonRedisSerializer<Object> jacksonSeial = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会报异常
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jacksonSeial.setObjectMapper(om);
StringRedisSerializer stringSerial = new StringRedisSerializer();
// redis key 序列化方式使用stringSerial
template.setKeySerializer(stringSerial);
// redis value 序列化方式使用jackson
template.setValueSerializer(jacksonSeial);
// redis hash key 序列化方式使用stringSerial
template.setHashKeySerializer(stringSerial);
// redis hash value 序列化方式使用jackson
template.setHashValueSerializer(jacksonSeial);
template.afterPropertiesSet();
return template;
}
/**
* 配置redis默认缓存管理器
* 可以设置多个 缓存管理器 @Primary 指定默认
*/
@Bean
public CacheManager cacheManager(RedisTemplate<String, Object> template) {
RedisCacheConfiguration defaultCacheConfiguration =
RedisCacheConfiguration
.defaultCacheConfig()
// 设置key为String
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getStringSerializer()))
// 设置value 为自动转Json的Object
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getValueSerializer()))
// 不缓存null
.disableCachingNullValues()
// 缓存数据保存1小时
.entryTtl(Duration.ofHours(1));
RedisCacheManager redisCacheManager =
RedisCacheManager.RedisCacheManagerBuilder
// Redis 连接工厂
.fromConnectionFactory(template.getConnectionFactory())
// 缓存配置
.cacheDefaults(defaultCacheConfiguration)
// 配置同步修改或删除 put/evict
.transactionAware()
.build();
return redisCacheManager;
}
@Bean
public RedisUtils redisUtils(StringRedisTemplate stringRedisTemplate) {
log.info("Redis工具类.....Start");
return new RedisUtils(stringRedisTemplate);
}
}



▶ 延伸扩展 — 选择性加载@Conditional 和 @ConditionalOn***
在一些自动装配配置类中 出现过 @ConditionalOn***,@EnableAutoConfiguration会在满足条件情况下加入IOC容器
/**
* Web日志加入Spring容器
*
* @Author MostSnails
* @Version 1.0
*/
public class WebLogAutoConfiguration {
private static org.slf4j.Logger log = LoggerFactory.getLogger(WebLogAutoConfiguration.class);
public WebLogAutoConfiguration() {
log.info("Web日志记录配置类.....Start");
}
@Bean
@ConditionalOnWebApplication//当项目是一个Web项目时进行实例化
@ConditionalOnMissingBean(WebLogAspect.class)//当容器里没有指定Bean的条件下进行实例化。
public WebLogAspect webLogAspect() {
log.info("注入webLogAspect类.....");
return new WebLogAspect();
}
}
▶ 更多@Conditional 注解
@ConditionalOnBean: 仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。
@ConditionalOnClass: 某个class位于类路径上,才会实例化一个Bean。
@ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean。
@ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean。
@ConditionalOnMissingClass:某个class类路径上不存在的时候,才会实例化一个Bean。
@ConditionalOnNotWebApplication:不是web应用,才会实例化一个Bean。
@ConditionalOnBean: 当容器中有指定Bean的条件下进行实例化。
@ConditionalOnMissingBean:当容器里没有指定Bean的条件下进行实例化。
@ConditionalOnClass: 当classpath类路径下有指定类的条件下进行实例化。
@ConditionalOnMissingClass:当类路径下没有指定类的条件下进行实例化。
@ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。
@ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。
@ConditionalOnProperty: 当指定的属性有指定的值时进行实例化。
@ConditionalOnExpression: 基于SpEL表达式的条件判断。
@ConditionalOnJava: 当JVM版本为指定的版本范围时触发实例化。
@ConditionalOnResource: 当类路径下有指定的资源时触发实例化。
@ConditionalOnJndi: 在JNDI存在的条件下触发实例化。
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化。

▶ 如何使用IOC容器对象
1.@Autowired 2.ApplicationContext
@RunWith(SpringRunner.class)
@SpringBootTest(classes = WebDemoApplication.class)
public class JunitTest {
// @Autowired
// private TestClass testClass;
@Test
public void customYAMLTest(){
TestClass bean = BeanFactoryUtil.getBean(TestClass.class);
System.out.println(JSON.toJSONString(bean));
}
}
BeanFactoryUtil
/**
* Spring 上下文 获取
*
* @Author MostSnails
* @Version 1.0
*/
public class BeanFactoryUtil {
private static ApplicationContext context;
public static void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (context == null){
context = applicationContext;
}
}
// 获取applicationContext
private static ApplicationContext getApplicationContext() {
return context;
}
// 通过name获取Bean
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
// 通过class获取Bean
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
// 通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
