美文网首页
springBoot自动装配原理

springBoot自动装配原理

作者: ajajaj | 来源:发表于2020-08-05 12:48 被阅读0次

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

    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

    相关文章

      网友评论

          本文标题:springBoot自动装配原理

          本文链接:https://www.haomeiwen.com/subject/bvedrktx.html