提到springBoot就必须说下它的自动装配,springBoot遵循的原则就是约定大于配置,使用注解对常规的配置项做默认配置,结束xml配置,简化项目的搭建、配置过程。本文以整合redis为例简单介绍下springBoot的自动装配原理
ssm整合redis
传统ssm整合redis的时候 需要在xml的配置文件中 进行大量的配置Bean
1、加入配置
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
2、配置xml的bean的配置
//配置连接池
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="minIdle" value="10"></property>
<property name="maxTotal" value="20"></property>
</bean>
//配置连接工厂
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="47.104.128.12"></property>
<property name="password" value="123456"></property>
<property name="database" value="0"></property>
<property name="poolConfig" ref="poolConfig"></property>
</bean>
//配置 redisTemplate 模版类
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!! -->
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
</bean>
3、导入配置
@SpringBootApplication
@ImportResource(locations = "classpath:beans.xml")
@RestController
public class AutoconfigPrincipleApplication {@Autowired
private RedisTemplate redisTemplate;
public static void main(String[] args) {
SpringApplication.run(AutoconfigPrincipleApplication.class, args);
}
@RequestMapping("/testRedis")
public String testRedis() {
redisTemplate.opsForValue().set("smlz","smlz");
return "OK";
}
}
springBoot整合redis
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
修改yml配置文件
spring.redis.host=49.112.128.12
spring.redis.port=6379
spring.redis.password=12345
直接使用(下述代码可以不要配置,为了解决保存使用jdk的序列方式才配置的)
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setDefaultSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
template.setConnectionFactory(redisConnectionFactory);
return template;
}
通过对比可以发现,springBoot整合redis快捷了很多,因为springBoot通过自动装配的方式将很多配置,都自己做掉了。在介绍自动装配之前,首先看下几个注解
注解介绍
@Import
1、通过@Import注解来导入ImportSelector组件
- 写一个配置类在配置类上标注一个@Import的注解
@Configuration
@Import(value = {CbSelector.class})
public class TulingConfig {
}
- 在@Import注解的value值 写自己需要导入的组件;在selectImports方法中 就是你需要导入组件的全类名
public class CbSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.cb.service.CbServiceImpl"};
}
}
@RestController
public class CbController {
//自动注入 cbServiceImpl
@Resource
private CbServiceImpl cbServiceImpl;
@RequestMapping("testSelectInject")
public String testSelectInject() {
cbServiceImpl.testService();
return "hello";
}
}
这里是没有标注其他注解提供给spring包扫描的
public class CbServiceImpl {
public void testService() {
System.out.println("我是通过importSelector导入进来的service");
}
}
2、通过@Import导入ImportBeanDefinitionRegistrar 从而进来导入组件
public class CbImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//定义一个BeanDefinition
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TulingDao.class);
//把自定义的bean定义导入到容器中
beanDefinitionRegistry.registerBeanDefinition("cbDao",rootBeanDefinition);
}
}
通过ImportSelector功能导入进来的
public class CbServiceImpl {
@Autowired
private CbDao cbDao;
public void testService() {
cbDao.testCbDao();
System.out.println("我是通过importSelector导入进来的service");
}
}
通过ImportBeanDefinitionRegistar导入进来的
public class CbDao {
public void testCbDao() {
System.out.println("我是通过ImportBeanDefinitionRegistrar导入进来Dao组件");
}
}
自动装配原理
@SpringBootApplication
分析自动装配,要从启动类上的注解“@SpringBootApplication”入手
可以看到“@SpringBootApplication”是一个组合注解
image@EnableAutoConfiguration
imageAutoConfigurationImportSelector比较关键
AutoConfigurationImportSelector
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//去mata-info/spring.factories文件中 查询 EnableAutoConfiguration对于值
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
//去除重复的配置类,若我们自己写的starter 可能存主重复的
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//根据maven 导入的启动器过滤出 需要导入的配置类
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
}
去spring.factories中去查询EnableAutoConfirution类
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
然后我们分析RedisAutoConfiguration类
导入了三个组件 RedisTemplate 、StringRedisTemplate、JedisConnectionConfiguration
@Configuration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
整体流程图
image原文地址
http://cbaj.gitee.io/blog/2020/07/30/springBoot自动装配原理/#more
网友评论