一、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 整合一个mybatis

其实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的路径

  目标:画一下springboot自动装配的思维导图

  

posted on 2021-08-11 22:44  我是坏男孩  阅读(233)  评论(0编辑  收藏  举报