一文带你了解SpringBoot中常用注解的原理和使用

 更新时间:2022年11月01日 14:32:33   作者:twilight0402  
这篇文章主要介绍了一文带你了解SpringBoot中常用注解的原理和使用

@AutoConfiguration

读取所有jar包下的 /META-INF/spring.factories 并追加到一个 LinkedMultiValueMap 中。每一个url中记录的文件路径如下:

file:/C:/Users/wangchao/apache-maven-3.5.0/repo/com/baomidou/mybatis-plus-boot-starter/3.5.1/mybatis-plus-boot-starter-3.5.1.jar!/META-INF/spring.factories

按照如下路径查看

// 1. @EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    
}

// 2. AutoConfigurationImportSelector.class#selectImports()
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
}

// 3. AutoConfigurationImportSelector.class#getAutoConfigurationEntry()
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	configurations = removeDuplicates(configurations);
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	configurations = getConfigurationClassFilter().filter(configurations);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}


// 4. AutoConfigurationImportSelector.class#getCandidateConfigurations()
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;
}

// 5. org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames()
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	String factoryTypeName = factoryType.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

最终使用 loadSpringFactories(@Nullable ClassLoader classLoader) 方法读取所有配置文件。

// 6. org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories()
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
			    // 读取所有jar包下的 /META-INF/spring.factories
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

tomcat的自动配置内置于springboot的autoconfiguration中。参考tomcat的自动配置:SpringBoot如何实现Tomcat自动配置

mybatis-plus的配置没有被springboot包括。因此mybatis-stater中包含一个包mybatis-spring-boot-autoconfigure,这其中配置了需要自动配置的类。

因此我们也可以在自己的项目下新建 /META-INF/spring.factories ,并配置自动配置类。

@Import

@Import 用于导入配置类或需要前置加载的类。被导入的类会注册为Bean,可直接作为Bean被引用。它的 value 属性可以支持三种类型:

  • 被 @Configuration 修饰的配置类、或普通类(4.2版本之后可以)。
  • ImportSelector 接口的实现。
  • ImportBeanDefinitionRegistrar 接口的实现。

@Import 的配置

@Configuration
@Import(value = {TestA.class, TestB.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class ConfigurationTest {

}

导入一个普通类

package com.example.ssmpdemo.entity;

public class TestA {
    public void fun(){
        System.out.println("testA");
    }
}

导入一个配置类

package com.example.ssmpdemo.entity;

import org.springframework.context.annotation.Configuration;

@Configuration
public class TestB {
    public void fun(){
        System.out.println("testB");
    }
}

通过实现 ImportSelector 接口

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.example.ssmpdemo.entity.TestC"};
    }
}

通过重写 ImportBeanDefinitionRegistrar 的 registerBeanDefinitions 方法。

import com.example.ssmpdemo.entity.TestD;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition root = new RootBeanDefinition(TestD.class);
        registry.registerBeanDefinition("testD", root);
    }
}

@ConfigurationProperties

  • 支持常见的下划线、中划线和驼峰的转换。支持对象引导。比如:user.friend.name 代表的是user对象中的friend对象中的name
  • 需要有set()方法
  • 只添加 @ConfigurationProperties(prefix = "xxx") 并不会生效,需要配合 @Configuration 让容器识别到。
  • @EnableConfigurationProperties(value = ConfigData.class ) 会将value中指定的类注册为Bean,可直接用 @AutoWired 引用。

1.定义一个类用来记录所有字段,并使用@ConfigurationProperties(prefix = "xxx")将数据注入到ConfigData中。

package com.example.ssmpdemo.entity;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 用来记录Configuration的数据
 * @author wangc
 */
@Data
@ConfigurationProperties(value = "spring.datasource.druid")
public class ConfigData {
    private String driverClassName;
    private String url;
    private String username;
    private String password;
}


# 对应的yml文件
spring:
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:5506/ssmpdemo?serverTimezone=UTC
      username: root
      password: xxxx

2.使用@EnableConfigurationProperties(JDBCProperties.class) 将 ConfigData 注册为Bean,并提供给ConfigurationTest使用 。可将ConfigData作为参数注入到构造函数和普通函数中。

3.可以用以下方式引用被@ConfigurationProperties(value = "spring.datasource.druid")修饰的ConfigData

可以直接把 ConfigData 当成Bean使用

    /**
     * 可直接被注入
     */
    @Autowired
    private ConfigData configData;

可以用构造函数传入进来

@Data
@Configuration
@EnableConfigurationProperties(value = ConfigData.class )
public class ConfigurationTest {
    private ConfigData configData2;
    /**
     * 作为构造函数的参数注入
     * @param data
     */
    ConfigurationTest(ConfigData data){
        this.configData2 = data;
    }

也可以作为@Bean的方法函数的参数。只有当前类(ConfigurationTest)才可

    /**
     * 直接作为函数的参数
     * @param data
     * @return
     */
    @Bean(name = "configData2")
    HashMap<String, String> getBean(ConfigData data){
        return new HashMap<>(0);
    }

可以省略ConfigData直接将字段注入到返回结果中。

@Bean
@ConfigurationProperties(value = "spring.datasource.druid")
HashMap<String, String> getBean2(ConfigData data){
    // 会自动为hashMap赋值,或使用set方法为对象赋值
    return new HashMap<>();
}

EnableConfigurationProperties注解的内部如下,它导入了一个实现了 ImportBeanDefinitionRegistrar 接口的类。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        registerInfrastructureBeans(registry);
        ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
        // 获得@EnableConfigurationProperties的value指向的对象,并注册。
        getTypes(metadata).forEach(beanRegistrar::register);
    }

到此这篇关于一文带你了解SpringBoot中常用注解的原理和使用的文章就介绍到这了,更多相关SpringBoot注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现提取QSV文件视频内容

    Java实现提取QSV文件视频内容

    QSV是一种加密的视频文件格式。是爱奇艺公司研发的一种视频文件格式,这篇文章主要为大家介绍了如何利用Java实现提取QSV文件视频内容,感兴趣的可以了解一下
    2023-03-03
  • Java的Object类九个方法技巧

    Java的Object类九个方法技巧

    这篇文章主要介绍了Java的Object类九个方法技巧,Java的Object 类的完整路径是java.lang.Object ,是所有类的父类编译,下文相关资料,需要的朋友可以参考一下
    2022-04-04
  • springboot整合mqtt实现消息订阅和推送功能

    springboot整合mqtt实现消息订阅和推送功能

    mica-mqtt-client-spring-boot-starter是一个方便、高效、可靠的MQTT客户端启动器,适用于需要使用MQTT协议进行消息通信的Spring Boot应用程序,这篇文章主要介绍了springboot整合mqtt实现消息订阅和推送功能,需要的朋友可以参考下
    2024-02-02
  • 解析java中的condition

    解析java中的condition

    Condition 将 Object 监视器方法(wait()、notify()和notifyAll())分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待 set(wait-set)
    2021-06-06
  • SpringBoot + minio实现分片上传、秒传、续传功能

    SpringBoot + minio实现分片上传、秒传、续传功能

    MinIO是一个基于Go实现的高性能、兼容S3协议的对象存储,使用MinIO构建用于机器学习,分析和应用程序数据工作负载的高性能基础架构,这篇文章主要介绍了SpringBoot + minio实现分片上传、秒传、续传,需要的朋友可以参考下
    2023-06-06
  • 使用OpenFeign实现服务调用的方法详解

    使用OpenFeign实现服务调用的方法详解

    OpenFeign是运行在客户端的声明式服务调用的框架,通过声明接口的方式来达到对服务的调用,本文就来和大家讲讲如何使用OpenFeign实现服务调用吧
    2023-06-06
  • Java的Netty进阶之Future和Promise详解

    Java的Netty进阶之Future和Promise详解

    这篇文章主要介绍了Java的Netty进阶之Future和Promise详解,Netty 是基于 Java NIO 的异步事件驱动的网络应用框架,使用 Netty 可以快速开发网络应用,Netty 提供了高层次的抽象来简化 TCP 和 UDP 服务器的编程,但是你仍然可以使用底层的 API,需要的朋友可以参考下
    2023-11-11
  • java多线程编程技术详解和实例代码

    java多线程编程技术详解和实例代码

    这篇文章主要介绍了 java多线程编程技术详解和实例代码的相关资料,需要的朋友可以参考下
    2017-04-04
  • 详解MyBatis多数据源配置(读写分离)

    详解MyBatis多数据源配置(读写分离)

    这篇文章主要介绍了详解MyBatis多数据源配置(读写分离),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • Java 在PPT中添加混合图表过程详解

    Java 在PPT中添加混合图表过程详解

    这篇文章主要介绍了Java 在PPT中添加混合图表过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09

最新评论