Spring Boot自动装配的原理

spring / 251人浏览 / 0人评论
在Spring Boot中有一个点叫自动装配是Starter的基础,也是整个Spring Boot的核心,那什么是自动装配呢?简单来说,就是自动将Bean装配到IOC容器中这么一个操作。

一、Spring Boot中的自动装配

添加Starter依赖

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

配置Redis数据源

spring.redis.host=localhost
spring.redis.port=6379

创建一个Controller

@RestController
public class TestController {

    @Autowired
    RedisTemplate<String,String> redisTemplate;

    @GetMapping("/test")
    public String test(){
        redisTemplate.opsForValue().set("key","Spring Boot");
        return "success";
    }
}

这就是一个Spring Boot的装配机制,我们没有通过XML形式或者注解的形式把RedisTemplate注入到IOC容器,但是在Controller中可以直接通过@Autowired来直接注入使用。

二、自动装配是如何实现的?

@SpringBootApplication
public class TestDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(TestDemoApplication.class, args);
    }
}

Spring Boot的中自动装配,是通过注解@SpringBootApplication中的@EnableAutoConfiguration注解来开启的,点进去@SpringBootApplication

@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
@java.lang.annotation.Inherited
@org.springframework.boot.SpringBootConfiguration
@org.springframework.boot.autoconfigure.EnableAutoConfiguration
@org.springframework.context.annotation.ComponentScan(excludeFilters = {@org.springframework.context.annotation.ComponentScan.Filter(type = org.springframework.context.annotation.FilterType.CUSTOM, classes = {org.springframework.boot.context.TypeExcludeFilter.class}), @org.springframework.context.annotation.ComponentScan.Filter(type = org.springframework.context.annotation.FilterType.CUSTOM, classes = {org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter.class})})
public @interface SpringBootApplication {
    @org.springframework.core.annotation.AliasFor(annotation = org.springframework.boot.autoconfigure.EnableAutoConfiguration.class)
    java.lang.Class<?>[] exclude() default {};

    @org.springframework.core.annotation.AliasFor(annotation = org.springframework.boot.autoconfigure.EnableAutoConfiguration.class)
    java.lang.String[] excludeName() default {};

    @org.springframework.core.annotation.AliasFor(annotation = org.springframework.context.annotation.ComponentScan.class, attribute = "basePackages")
    java.lang.String[] scanBasePackages() default {};

    @org.springframework.core.annotation.AliasFor(annotation = org.springframework.context.annotation.ComponentScan.class, attribute = "basePackageClasses")
    java.lang.Class<?>[] scanBasePackageClasses() default {};
}

再继续点进去@EnableAutoConfiguration注解

@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
@java.lang.annotation.Inherited
@org.springframework.boot.autoconfigure.AutoConfigurationPackage
@org.springframework.context.annotation.Import({org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    java.lang.String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    java.lang.Class<?>[] exclude() default {};

    java.lang.String[] excludeName() default {};
}

在@EnableAutoConfiguration注解中除了@Import注解之外还有一个@AutoConfigurationPackage注解,@AutoConfigurationPackage注解的作用是把使用了该注解的类所在的包及子包下所有组件扫描到Spring IOC容器中,并且在这里@Import注解中导入的不是一个Configuration的配置类,而是一个AutoConfigurationImportSelector.class

public class AutoConfigurationImportSelector implements org.springframework.context.annotation.DeferredImportSelector
***
***
***
public interface DeferredImportSelector extends org.springframework.context.annotation.ImportSelector

AutoConfigurationImportSelector实现了接口DeferredImportSelector,DeferredImportSelector又继承了ImportSelector,ImportSelector中通过制定需要装配到IOC容器中的类,当在@Import中导入ImportSelector的实现之后,会把该实现类中返回的Class名称都装载到IOC容器中去。
如何利用ImportSelector装配类到IOC容器中去
首先创建一个类TestClass

public class TestClass {
}

创建一个ImportSelector的实现类,在实现类中把定义的Bean加入String数据中(因为ImportSelector可以实现批量装配,这里用一个来举例,多个逗号分隔就好),该Bean将会装配到IOC容器中

public class TestImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{TestClass.class.getName()};
    }
}

为了模拟EnableAutoConfiguration,我们自定义一个注解,通过@Import导入

@Target(ElementType.TYPE) //接口、类、枚举、注解
@Retention(RetentionPolicy.RUNTIME) //这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
@Documented // 说明该注解将被包含在javadoc中
@Inherited // 说明子类可以继承父类中的该注解
@AutoConfigurationPackage //添加该注解的类所在的package 作为 自动配置package 进行管理
@Import(TestImportSelector.class)
public @interface EnableAutoImport {
}

然后创建一个启动类,在启动类上使用@EnableAutoImport注解,即可通过getBean从IOC容器中得到TestClass实例

@SpringBootApplication
@EnableAutoImport
public class ImportSelectorMain {

    public static void main(String[] args) {
        ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(ImportSelectorMain.class,args);
        TestClass testClass = configurableApplicationContext.getBean(TestClass.class);
    }
}

三、总结

至此我们会发现自动装配是通过@Import(AutoConfigurationImportSelector.class)实现配置类的导入,AutoConfigurationImportSelector实现了接口DeferredImportSelector,DeferredImportSelector又继承了ImportSelector,实际演示实例中直接实现了接口ImportSelector重写了selectImports()方法实现配置类的批量装配。

0 条评论

还没有人发表评论

发表评论 取消回复

记住我的信息,方便下次评论
有人回复时邮件通知我