• Spring的注解驱动历程以及springboot核心原理


    一、Spring的注解驱动历程

    Spring 1.0

    具备ioc

    <bean> ... 

    Spring 2.5

    有了 @Controller @service @Repository @Component 然后配置Component-scan 的路径就可以了,减少了bean的配置

    Spring 3.0      ---- 无配置化实现bean的装配(去xml化)

    用@Configuration 取代application.xml   <bean>...

    @Configuration  要配置一个bean可以 @bean来做

    @import

    spring4.X

    @conditionl:条件满足才装配

    spring5.X

    @indexed  它可以为Spring的模式注解添加索引

    二、springboot的自动装配

      1.疑惑点:导入对应的jar包(这个是官方的jar) 例如 

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    我们就直接可以使用

    @Autowired
    RedisTemplate redisTemplate;
    

      

    为什么可以呢?我们还没有手动加载到ioc?

      2.前言知识: 我们知道要引入bean有很多种方式,例如静态:xml/@Configuration、 动态的   @import(value = {Cat.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})可以实现ImportSelector接口(MyImportSelector是该接口的实现类)动态导入bean或configuration

      

    public class MyImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{"com.fjj.project.entity.Company",
                                "com.fjj.project.entity.Member"}; --- 这里可返回的classname都会被装进ioc
        }
    }

    3.原理:我们只要把的bean放到以上selectImports方法就可以装到ioc了,那么我们怎么知道将要被引入的jar的bean放在哪里呢?

    springboot约定去所有要引入的jar的MATE-INF/spring.factories 都写上

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=要加载的classname的路径
    

     然后springboot启动的时候会去读取,接下来我们去查看源码->

    步骤1:
    在启动类中
    @SpringBootApplication public class QuartzApplication{ public static void main(String[] args) { SpringApplication.run(QuartzApplication.class, args); } } 其中@SpringBootApplication里面包含@EnableAutoConfiguration,在这个配置文件里面有一个 @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { ... 步骤2: 所以我们进入AutoConfigurationImportSelector这个类,果不其然也有你实现DeferredImportSelector 也就是间接实现了ImportSelector
    public class AutoConfigurationImportSelector implements DeferredImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
    return NO_IMPORTS;
    }
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); --加载配置spring.factories,详细可以查看以下getCandidateConfigurations方法

    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
    .....

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
    getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
    }

    }
    扩展点:
    以上很类似于spi的机制:
    
    在第三方jar中
    
    1).首先在MATE-INF/services创建名称为interface的文件
    
    2).里面写着实现类的class路径
    

      

    按照这种思路,我们刚刚导入的包应该有spring.factories文件,并且里面有对应的org.springframework.boot.autoconfigure.EnableAutoConfiguration=XXX才对吧,实际上没有

    what? 刚刚分析的源码没卵用吗?

    其实springboot把jar分为两种:

    1)官方包 spring-boot-starter-xxx  (这种springboot直接给你配置好了,如下图)

    2)第三方包 xxx-spring-boot-starter

    第三方还是老老实实的写,例如我导入

    <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version>
    </dependency>
    这个想直接使用bean,肯定会有一个文件



    按照这种思路,官方的jar自己准备了那么多,那假如对应的jar没有引进来怎么办?加载岂不是报错了?

    其实不会,因为spring4.x引入了@conditional,也就是说满足条件才会加载,以RedisAutoConfiguration为例子

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package org.springframework.boot.autoconfigure.data.redis;
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisOperations;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.StringRedisTemplate;
    
    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({RedisOperations.class})
    @EnableConfigurationProperties({RedisProperties.class})
    @Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
    public class RedisAutoConfiguration {
        public RedisAutoConfiguration() {
        }
    
        @Bean
        @ConditionalOnMissingBean(
            name = {"redisTemplate"}
        )
        @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
        public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<Object, Object> template = new RedisTemplate();
            template.setConnectionFactory(redisConnectionFactory);
            return template;
        }
    
        @Bean
        @ConditionalOnMissingBean
        @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
        public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
            StringRedisTemplate template = new StringRedisTemplate();
            template.setConnectionFactory(redisConnectionFactory);
            return template;
        }
    }
    

      

    扩展点
    条件装配两种方式:
    
    1.@conditional
    
    2.添加spring-autoconfigure-metadata-properties文件 格式:被加载的classname路径 +.ConditionalOnClass= 需要存在才生效的classname的路径
    

      

      

  • 相关阅读:
    Eclipse 读取config目录下文件
    cakephp 中Console / Shell 有什么优点?
    cakephp中使用 find('count')方法
    [转]using components in Cakephp 2+ Shell
    [转]Git for windows 下vim解决中文乱码的有关问题
    在Foxmail中添加阿里云企业邮箱账号
    Cakephp在Controller中显示sql语句
    java线程的基本概念
    mysql varchar到底能存多少字符。
    mysql 联合索引匹配原则
  • 原文地址:https://www.cnblogs.com/imfjj/p/15127769.html
Copyright © 2020-2023  润新知