springBoot自动装配原理

2020-08-05  本文已影响0人  ajajaj

提到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组件

@Configuration 
@Import(value  = {CbSelector.class})
public class TulingConfig {
}
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

image

AutoConfigurationImportSelector比较关键

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

上一篇 下一篇

猜你喜欢

热点阅读