细说SpringBoot的自动装配原理

org.springframework.boot

spring-boot-starter-web

在当前项目下运行mvn spring-boot:run 或者直接运行main方法就可以启动一个使用了嵌入式tomcat服务请求的web应用,但是我们没有提供任何服务web请求的controller,所以访问任何路径都会返回一个springboot默认的错误页面(whitelabel error page)

所以,我们可以创建一个Controller来实现请求

@RestController

public class HelloController {

@GetMapping(“/say”)

public String sayHello(){

return “hello Mic”;

}

}

访问http://localhost:8080/say 就可以获得一个请求结果。 这样就完成了一个非常简单的web应用。 springboot是一个约定优于配置的产物,所以在快速构建web应用的背后,其实有很多的约定。

  1. 项目结构层面,静态文件和页面模版的存放位置变成了src/main/resources对应的子目录下

  2. 自动嵌入tomcat作为web容器对外提供http服务,默认使用8080端口监听

  3. 自动装配springmvc必要的组件

3.Spring Boot四大核心

================================================================================

EnableAutoConfiguration 自动装配

Starter组件, 开箱即用

Actuator 监控

Spring Boot Cli 为Spring Cloud 提供了Spring Boot 命令行功能

那今天主要给大家讲讲Enable*这个注解

4.Enable* 注解的作用

===============================================================================

Enable是启用的意思,相当于开启某一个功能

  • EnableScheduling

  • EnableHystrix

  • EnableAsync

  • EnableAutoConfiguration

  • EnableWebMvc

仍然是在spring3.1版本中,提供了一系列的@Enable开头的注解,Enable主机应该是在JavaConfig框架上更进一步的完善,是的用户在使用spring相关的框架是,避免配置大量的代码从而降低使用的难度

比如常见的一些Enable注解:EnableWebMvc,(这个注解引入了MVC框架在Spring 应用中需要用到的所有bean);

比如说@EnableScheduling,开启计划任务的支持;

找到EnableAutoConfiguration,我们可以看到每一个涉及到Enable开头的注解,都会带有一个@Import的注解。

5.深入分析Spring Boot中的自动装配

======================================================================================

在Spring Boot中,不得不说的一个点是自动装配,它是starter的基础,也是Spring Boot的核心, 那什么叫自动装配?或者什么叫装配呢?

回顾一下Spring Framework,它最核心的功能是IOC和AOP, IoC容器的主要功能是可以管理对象的生命周期。也就是bean的管理。我们把Bean对象托管到Spring Ioc容器的这个过程称为装配,那什么是自动装配呢?我们慢慢来介绍

首先大家看下这张图,我们先不解释。等把今天的内容讲完,我们再回头来通过这张图来总结~

在这里插入图片描述

自动装配在SpringBoot是基于EnableAutoConfiguration来实现的。那么在深入分析EnableAutoConfiguration之前,我们来看一下传统意义上的装配方式。

5.1简单分析@Configuration


Configuration这个注解大家应该有用过,它是JavaConfig形式的基于Spring IOC容器的配置类使用的一种注解。因为SpringBoot本质上就是一个spring应用,所以通过这个注解来加载IOC容器的配置是很正常的。所以在启动类里面标注了@Configuration,意味着它其实也是一个IoC容器的配置类。

public class DemoClass {

public void say(){

System.out.println("sya hello … ");

}

}

@Configuration

@Import(UserClass.class)

public class DemoConfiguration {

@Bean

public DemoClass getDemoClass(){

return new DemoClass();

}

}

public class DemoConfigurationMain {

public static void main(String[] args) {

ApplicationContext ac = new AnnotationConfigApplicationContext(DemoConfiguration.class);

DemoClass bean = ac.getBean(DemoClass.class);

bean.say();

}

}

这种形式就是通过注解的方式来实现IoC容器,传统意义上的spring应用都是基于xml形式来配置bean的依赖关系。然后通过spring容器在启动的时候,把bean进行初始化并且,如果bean之间存在依赖关系,则分析这些已经在IoC容器中的bean根据依赖关系进行组装。

直到Java5中,引入了Annotations这个特性,Spring框架也紧随大流并且推出了基于Java代码和Annotation元信息的依赖关系绑定描述的方式。也就是JavaConfig。

从spring3开始,spring就支持了两种bean的配置方式,一种是基于xml文件方式、另一种就是JavaConfig

Configuration的本质

如果大家细心一点就会发现Configuration注解的本质是一个Component注解,这个注解会被AnnotationConfigApplicationContext加载,而AnnotationConfigApplicationContext是ApplicationContext的一个具体实现,表示根据配置注解启动应用上下文。

因此我们在Main方法中通过AnnotationConfigApplicationContext去加载JavaConfig后,可以得到Ioc容器中的bean的实例

JavaConfig的方式在前面的代码中已经演示过了,任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类。而在这个配置类中,任何标注了@Bean的方法,它的返回值都会作为Bean定义注册到Spring的IOC容器,方法名默认成为这个bean的id

5.2简单分析@ComponentScan


ComponentScan这个注解是大家接触得最多的了,相当于xml配置文件中的context:component-scan。 它的主要作用就是扫描指定路径下的标识了需要装配的类,自动装配到spring的Ioc容器中。

标识需要装配的类的形式主要是:@Component、@Repository、@Service、@Controller这类的注解标识的类。

public static void main(String[] args) {

ApplicationContext ac = new AnnotationConfigApplicationContext(DemoConfiguration.class);

String[] beanDefinitionNames = ac.getBeanDefinitionNames();

System.out.println(“********************************”);

for (String beanName:beanDefinitionNames

) {

System.out.println(beanName);

}

}

5.3 Import注解


import 注解是什么意思呢? 联想到xml形式下有一个<import resource/> 形式的注解,就明白它的作用了。import就是把多个分来的容器配置合并在一个配置中。在JavaConfig中所表达的意义是一样的。为了能更好的理解后面讲的EnableAutoConfiguration,我们详细的和大家来介绍下import注解的使用

方式一:直接填class数组

我们先在不同的两个package下创建对应的bean。

比如:

在这里插入图片描述

那么DemoConfigurationMain中执行的代码应该是加载不到demo2中的UserClass的

在这里插入图片描述

这时我们可以通过@import来直接加载

在这里插入图片描述

或者

在这里插入图片描述

方式二:ImportSelector方式【重点】

第一种方式如果导入的是一个配置类,那么该配置类中的所有的都会加载,如果想要更加的灵活,动态的去加载的话可以通过Import接口的第二种使用方式,也就是ImportSelector这种方式,我们来看看怎么使用。

LogService

public class LogService {

}

CacheService

public class CacheService {

}

GpDefineImportSelector

public class GpDefineImportSelector implements ImportSelector {

/**

  • AnnotationMetadata 注解元数据

  • @param annotationMetadata

  • @return

  •  要被IOC容器加载的bean信息
    

*/

@Override

public String[] selectImports(AnnotationMetadata annotationMetadata) {

// 我们可以基于注解元数据信息来动态的返回要加载的bean信息

annotationMetadata

.getAllAnnotationAttributes(EnableDefineService.class.getName(),true)

.forEach((k,v)->{

System.out.println(annotationMetadata.getClassName());

System.out.println(“—> " + k+”:" + String.valueOf(v));

});

return new String[]{CacheService.class.getName()};

}

}

EnableDefineService

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@Import(GpDefineImportSelector.class)

public @interface EnableDefineService {

String[] packages() default “”;

}

EnableDemoTest

@EnableDefineService()

@SpringBootApplication

public class EnableDemoTest {

public static void main(String[] args) {

ApplicationContext ac = new AnnotationConfigApplicationContext(EnableDemoTest.class);//SpringApplication.run(EnableDemoTest.class,args);

System.out.println(ac.getBean(CacheService.class));

System.out.println(ac.getBean(LogService.class));

}

}

方式三:ImportBeanDefinitionRegistrar方式

这种方式和第二种方式很相似,同样的要实现 ImportBeanDefinitionRegistrar 接口

public class GpImportDefinitionRegister implements ImportBeanDefinitionRegistrar {

@Override

public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

// 指定bean的定义信息

RootBeanDefinition beanDefinition = new RootBeanDefinition(CacheService.class);

RootBeanDefinition beanDefinition1 = new RootBeanDefinition(LogService.class);

// 注册一个bean

beanDefinitionRegistry.registerBeanDefinition(“cacheService1111”,beanDefinition);

beanDefinitionRegistry.registerBeanDefinition(“cacheService2222”,beanDefinition1);

}

}

在这里插入图片描述

在这里插入图片描述

5.4 深入分析EnableAutoConfiguration原理


了解了ImportSelectorImportBeanDefinitionRegistrar后,对于EnableAutoConfiguration的理解就容易一些了

它会通过import导入第三方提供的bean的配置类:AutoConfigurationImportSelector

在这里插入图片描述

AutoConfigurationImportSelector

public String[] selectImports(AnnotationMetadata annotationMetadata) {

if (!this.isEnabled(annotationMetadata)) {

return NO_IMPORTS;

} else {

try {

// 加载META-INF/spring-autoconfigure-metadata.properties 下的元数据信息

AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);

AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

// 获取候选加载的配置信息 META-INF/spring.factories

List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

// 去掉重复的配置信息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值