springboot自动配置原理

一、java注解的使用

定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的 一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方 法参数等的前面,用来对这些元素进行说明,注释。

作用:本来可能需要很多配置文件,需要很多逻辑才能实现的内容,就可以使用一个或者多个注解 来替代,这样就使得编程更加简洁,代码更加清晰。

原理:本质上就是一个继承自java.lang.annotation.Annotation的接口。

**接口设置属性:**简单来说,就是java通过动态代理的方式生成了一个实现了注解的实例(对于当前的实体来说,例如类、方法、属性域等,这个代理对象是单例的),然后对该代理实例的属 性赋值,这样就可以在程序运行时(如果将注解设置为运行时可见的话)通过反射获取到注解的配 置信息

1.1注解分类

1)按照运行机制划分

【源码注解→编译时注解→运行时注解】

源码注解:只在源码中存在,编译成. c lass文件就不存在了。

编译时注解:在源码和. class文件中都存在。像前面的@Override、@Deprecated、 @SuppressWarnings,他们都属于编译时注解。

运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解。像@Autowired自动注入的这样 一种注解就属于运行时注解,它会在程序运行的时候把你的成员变量自动的注入进来。

2)按照来源划分:

【来自JDK的注解——来自第三方的注解——自定义注解】

3)元注解: 元注解是给注解进行注解,可以理解为注解的注解就是元注解

1.2注解详解

在这里插入图片描述
首先我们要明确这不是一个接口,它是使用@intterface关键字定义的一个注解。

然后我们看下面的几个方法,String desc();虽然它很类似于接口里面的方法,其实它在注解里面只是一个成员变量 (成员以无参无异常的方式声明),int age() default 18 ;(成员变量可以用deault指定一个默认值的)。

最后我们要知道:

①.成员类型是受限制的,合法的类型包括基本的数据类型以及String,Class,Annotation ,Enumeration等。

②.如果注解只有一个成员,则成员名必须取名为v a l u e (),在使用时可以忽略成员名和赋值号(=)。

③.注解类可以没有成员,没有成员的注解称为标识注解

1.3元注解(负责注解其它注解)

1) @Target是这个注解的作用域,ElementType.METHOD是这个注解的作用域的列表, METHOD是方法声明,除此之外,还有:CONSTRUCTOR(构造方法声明),FIELD(字段声 明),LOCAL VARIABLE(局部变量声明),METHOD(方法声明),PACKAGE(包声 明),PARAMETER(参数声明),TYPE(类接口)

2)@Retention是它的生命周期,前面不是说注解按照运行机制有一个分类嘛,RUNTIME就是在 运行时存在,可以通过反射读取。除此之外,还有:SOURCE(只在源码显示,编译时丢 弃),CLASS(编译时记录到class中,运行时忽略),RUNTIME(运行时存在,可以通过反射读取)

3) @Inherited是一个标识性的元注解,它允许子注解继承它。

4) @Documented,生成javadoc时会包含注解

二、Spring注解

2.1Spring常用注入注解

注解注入就是通过注解来实现注入。Spring和注入相关的常见注解有@Autowired、@Resource、 @Qualifier、@Service、@Control ler、@Repositor y、@Component。

1)@Autowired是自动注入,默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的 Bean时,Spring将其注入@Autowired标注的变量中

2)@Resour ce用来指定名称注入

3)@Qualifier和Autowired配合使用,指定bean的名称。如果容器中有一个以上匹配的Bean,则可以通过 @Qualifier注解限定Bean的名称

4)@Service,@Control ler,@Repositor y分别标记类是Service层类,Control ler层类,数据访问层的类, spring扫描注解配置时,会标记这些类要生成bean

5)@Component是一种泛指,标记类是组件,spring扫描注解配置时,会标记这些类要生成bean。

注:上面的@Autowired和@Resource是用来修饰字段,构造函数,或者设置方法,并做注入的。 而@Service,@Control ler,@Repositor y,@Component则是所有受Spring 管理组件的通用形式,在组 件不明确具体类别时使用

2.2spring常用配置注解

从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一 个或多个被@Bean注解的方法

  1. @EnableXXX注解: 配合@Configuration使用,包括 @EnableAsync, @EnableScheduling, @EnableTransac tionManagement, @EnableAspectJAutoProxy, @EnableWebMvc。

2)@PropertySources注解,配合@Configuration加载外部配置文件,使用@Value加载配置项。

3)@Import注解,可以组合多个配置类。

4)@Lazy懒加载注解,与@Configuration和@Bean配合使用。Spring默认不是懒加载。

5)@Profile逻辑组配置,用于指定配置类加载的逻辑组(开发/生产环境),只有在组内的配置类才会被加载

2.3spring重要注解

Spring Boot的强大之处在于使用了Spring 4框架的新特性:@Conditional注释,此注释使得只 有在特定条件满足时才启用一些配置。

除此之外,拓展注解还有:

@ConditionalOnBean(仅仅在当前一个Bean上下文中存在某个对象时,才会实例化)

@ConditionalOnClass(某个c las s位于类路径上,才会实例化一个Bean)

@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean

@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)

@ConditionalOnMissingClass(某个c lass类路径上不存在的时候,才会实例化一个Bean)

@ConditionalOnWebAppl ication(是web应用时)

三、springboot

3.1Starter启动器

SpringBoot的一大优势就是Starter,由于SpringBoot有很多开箱即用的Starter依赖,使得我们 开发变得简单,我们不需要过多的关注框架的配置。

可以认为starter是一种服务——使得使用某个功能的开发者不需要关注各种依赖库的处理,不需要 具体的配置信息,由Spring Boot自动通过classpath路径下的类发现需要的Bean,并织入bean

3.2SpringBootApplication

实际是@SpringBootConfiguration,@EnableAutoConfiguration以及@ComponentScan的 组合。声明是配置类、自动注入配置、自动扫描包

  1. @SpringBootConfiguration和@Configuration有相同的作用,配置该注解的类能够以 JavaConfig的方式完成配置,不再使用XML配置
  2. @EnableAutoConfiguration注释告诉由@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)组成功能是基于已添加的依赖项,猜测你要如何使用Spring的相关配置。
  3. @ComponentScan注解的功能是自动扫描,默认扫描所在类的同级类和同级目录下的所有类。 相当于Spring XML配置文件中的 @Target、@Retention、@Documented、@Inherited是java元注解(注解其他注解) 表明注解的使用位置、保留时间(编译策略)、文档化使用和允许子类继承父类等

3.3启动过程

构造一个SpringApplication的实例,然后运行它的run方法,而run方法会返回一个 ConfigurableAppl icationContext 创建SpringAppl ication对象的过程(initialize方法)

  1. 保存主配置类、确定web应用类型
  2. 从类路径下找到META-INF/ spring.factories配置的所有ApplicationContextInitializer和 Appl icationListener
  3. 从多个配置类中找到有main方法的主配置类

run方法的执行过程

​ 1. 封装命令行参数、准备环境,创建ApplicationContext

​ 2. ioc容器初始化,扫描创建组件,回调后返回容器

执行过程逻辑

//创建了SpringApplication实例   执行run方法
	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}
	

	//创建实例的方法
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		//加载主配置  确定web应用类型等等
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		//从SpringFactoriesLoader类中的loadSpringFactories方法
		//加载文件META-INF/spring.factories
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		//查找主程序的main函数
		this.mainApplicationClass = deduceMainApplicationClass();
	}

//run方法的执行过程  
//加载一系列的配置  准备一系列的环境 打印一系列的日志  开始创建容器启动监听
public ConfigurableApplicationContext run(String... args) {
    //计时器
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
    //创建应用上下文/容器
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	//加载配置
	configureHeadlessProperty();
    //获取监听器
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
        //创建默认参数
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //准备环境
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		configureIgnoreBeanInfo(environment);
        //打印banner 
		Banner printedBanner = printBanner(environment);
        //创建容器
		context = createApplicationContext();
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
        //计时结束  启动完成
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
		callRunners(context, applicationArguments);
	}
    ...
    try {
        //启动监听器
		listeners.running(context);
	}
    ...
    //返回容器
	return context;
}

3.4自动配置原理

@EnableAutoConfiguration
//注入自动配置

//有两大注解组成
@AutoConfigurationPackage 查找并注册自动配置包
@Import(AutoConfigurationImportSelector.class) 引入资源,自动配置的包选择器

相关的逻辑都在spring-boot-antoconfigure.jar里

a)@AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class)
//引入资源 自动配置的包注册器

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		register(registry, new PackageImport(metadata).getPackageName());
	}

//注册bean定义器 包名是主程序入口的所在目录(默认只扫描入口类的当前目录和子目录)

b) @Import(AutoConfigurationImportSelector.class)
-1- AutoConfigurationImportSelector类有一个selectImports方法
-2- selectImports方法执行的逻辑: 加载元数据和具体的配置条目

AutoConfigurationMetadata autoConfigurationMetadata = 
    AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);

-3- getAutoConfigurationEntry调用了getCandidateConfigurations方法,去加载条件配置
-4- getCandidateConfigurations方法的逻辑是通过spring工厂加载器 获取名称

 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());

-5- loadFactoryNames方法的逻辑
加载文件 META-INF/spring.factories 里面的内容
XXXAutoConfiguration命名的一系列配置类 如ThymeleafAutoConfiguration

c) 以ThymeleafAutoConfiguration为例 查看选择注解的机制

四大注解
@Configuration   
//声明这是配置类 xml 
@EnableConfigurationProperties(ThymeleafProperties.class)  
//加载ThymeleafProperties这个配置文件类
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
//控制条件注解  当存在TemplateMode和SpringTemplateEngine的时候   才会执行类的逻辑
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
//控制执行顺序  在WebMvcAutoConfiguration和WebFluxAutoConfiguration执行之后再执行

ThymeleafAutoConfiguration注解的执行逻辑:

1 先执行AutoConfigureAfter里面的属性类
2 判断是否存在ConditionalOnClass里面的class文件 (取决于pom是否引入依赖)

    <!--首先引入starter (封装的启动器)-->
	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
  
     <!-- 启动器引入具体的依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.1.8.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.thymeleaf</groupId>
      <artifactId>thymeleaf-spring5</artifactId>
      <version>3.0.11.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.thymeleaf.extras</groupId>
      <artifactId>thymeleaf-extras-java8time</artifactId>
      <version>3.0.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>

   <!--具体的依赖就存在需要的class -->
   <!--thymeleaf-spring5 jar包中存在SpringTemplateEngine.class -->

3 判断需要执行后 ,开始加载ThymeleafProperties配置文件类

@ConfigurationProperties(prefix = "spring.thymeleaf")
//这是一个配置文件类  并且寻找前缀为spring.thymeleaf开头的配置

spring.thymeleaf.prefix 默认值classpath:/templates/
spring.thymeleaf.suffix 默认值html
spring.thymeleaf.mode   默认值HTML
spring.thymeleaf.encoding 默认值UTF-8

4 执行类里的逻辑

          //注入bean  是spring资源模板解析器
        @Bean
		public SpringResourceTemplateResolver defaultTemplateResolver() {
            //是通过spring-thymeleaf这个粘合器,调用thymeleaf里面的核心逻辑
			SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
            //赋值配置文件中的重要属性   
			resolver.setApplicationContext(this.applicationContext);
			resolver.setPrefix(this.properties.getPrefix());
			resolver.setSuffix(this.properties.getSuffix());
			resolver.setTemplateMode(this.properties.getMode());
			if (this.properties.getEncoding() != null) {
				resolver.setCharacterEncoding(this.properties.getEncoding().name());
			}
			resolver.setCacheable(this.properties.isCache());
			Integer order = this.properties.getTemplateResolverOrder();
			if (order != null) {
				resolver.setOrder(order);
			}
			resolver.setCheckExistence(this.properties.isCheckTemplate());
			return resolver;
		}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SpringBoot自动配置原理简述 SpringBoot自动配置SpringBoot的一大特色,它提供了一种简洁的方式来配置应用程序。在SpringBoot中,我们可以通过添加特定的依赖来自动配置应用程序的各种组件,例如数据源、日志、Web服务等。使用自动配置可以大大简化应用程序的配置过程,减少开发人员的工作量,提高开发效率。 自动配置的实现原理是基于Spring的条件化配置机制。SpringBoot会根据应用程序的依赖和当前环境自动配置各种组件。条件化配置可以通过在类上添加@Conditional注解来实现,该注解可以指定一个条件类,只有满足该条件类的条件时,才会进行配置。例如,以下代码示例中,只有当classpath中存在H2数据库驱动程序时,才会自动配置H2的数据源。 @Configuration @ConditionalOnClass(org.h2.Driver.class) public class H2DataSourceAutoConfiguration { // 配置H2数据源 } 除了依赖条件外,SpringBoot还支持很多其他条件,例如环境条件、属性条件、Bean条件等。通过组合这些条件,可以实现更加灵活的自动配置。例如,以下代码示例中,只有当当前环境为开发环境时,才会自动配置开发环境的日志。 @Configuration @Profile("dev") public class DevLoggerAutoConfiguration { // 配置开发环境的日志 } 总之,SpringBoot自动配置机制是基于Spring的条件化配置机制实现的,它可以根据应用程序的依赖和当前环境自动配置各种组件,大大简化了应用程序的配置过程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值