spring.factories文件是Spring Boot框架提供的一个用于自动配置的核心文件。在我们引入一个Spring Boot的依赖时,它会自动扫描classpath下的spring.factories文件,并根据文件中的配置加载相应的自动配置类。

该文件的作用是通过定义key-value对的方式来指定自动配置类的全路径,键是自动配置类的类型,值是自动配置类的全路径。Spring Boot框架通过读取这个文件中的信息,来确定需要加载哪些自动配置类。这样就能够快速地自动配置相关的组件和功能,简化了Spring Boot的配置过程。

底层实现原理

Spring Boot框架利用了Spring Framework的SPI机制,实现了自定义扩展。在Spring Framework中,SPI即Service Provider Interface。SPI是一组约定,它主要是定义在接口中的一些约定,实现了这个接口的类就能够被框架所发现和使用。

在Spring Boot中,spring.factories文件就是为了实现SPI机制而设计的,它提供了一组key-value的映射,其中key是一个接口或抽象类的全限定名,value是实现这个接口或抽象类的类的全限定名。这样,当Spring Boot框架发现某个接口的实现类时,它就会自动加载对应的实现类,并按照一定的规则进行自动配置。这就是Spring Boot中的自动配置原理。

实现步骤

  1. 创建一个项目并引入相关的Spring Boot依赖。

  2. 在项目的classpath下创建META-INF/spring.factories文件。

  3. 在spring.factories文件中定义需要自动配置的类的全路径。

  4. 在Spring Boot启动时,根据配置的key自动加载对应的value,并进行自动配置。

底层工作原理

Spring Boot在启动时,会通过类加载器加载classpath下的所有类,同时会扫描classpath下的META-INF/spring.factories文件,并根据文件中配置的key-value对,去寻找对应的自动配置类。它会调用自动配置类中的@Configuration注解方法来构建Bean。自动配置类就是根据传入的信息来判断是否需要自动化配置的,如果是,则生成相应的Bean。

在加载自动配置类时,Spring Boot会根据一定的规则,对Bean进行排序,然后按照顺序依次进行配置。其中涉及到的一些技术包括:条件注解、Bean的初始化和销毁等。

源码解释

在Spring Boot框架中,自动配置的实现主要依赖于spring-boot-autoconfigure模块。该模块中包含了大量的自动配置类和相关的注解。

例如,比较常见的自动配置类是:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,在该类中,通过@Configuration注解和@ConditionalOnClass注解来判断是否需要自动配置。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "javax.sql.DataSource")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
  // ...
}

其中@ConditionalOnClass注解表示只有在classpath下存在DataSource和EmbeddedDatabaseType类时才进行自动配置,@ConditionalOnMissingBean注解表示在容器中不存在javax.sql.DataSource类型的Bean时,才进行自动配置。

总的来说,Spring Boot中的自动配置原理主要是基于SPI机制的,通过读取classpath下的spring.factories文件,来自动加载对应的自动配置类,并按照一定的规则进行配置。这种方式简化了Spring Boot的配置过程,提高了开发效率,也增强了框架的可扩展性。

1. spring.factories文件:

  • 该文件位于classpath下的META-INF/spring.factories路径上,用于定义自动配置类的全路径。
  • 底层源码实现:
    • Spring Boot在启动时会扫描classpath下的META-INF/spring.factories文件,然后根据其中定义的key来加载对应value的自动配置类。
    • 在底层源码中,Spring Boot通过调用SpringFactoriesLoader.loadFactoryNames方法,来加载spring.factories文件中定义的全路径。该方法的实现如下:
/**
* 通过指定的Class对象和ClassLoader对象,加载工厂名称列表
* @param factoryClass 指定的Class对象
* @param classLoader ClassLoader对象,可为null
* @return 工厂名称列表
*/
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    // 获取指定Class对象的名称
    String factoryClassName = factoryClass.getName();
    // 调用loadSpringFactories方法加载工厂名称列表,如果工厂名称列表为空则返回Collections.emptyList()
    return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

2. Spring Framework的SPI机制

  • 该机制主要是定义在接口中的一些约定,实现了这个接口的类就能够被框架所发现和使用。
  • 底层源码实现:
    • Spring Boot利用了Spring Framework中的SPI机制,通过读取META-INF/spring.factories文件中的key来加载对应的自动配置类。
    • 在底层源码中,通过调用spring-core模块中的SpringFactoriesLoader.loadSpringFactories方法来加载spring.factories文件。
// 定义一个常量字符串,表示Spring工厂配置文件的位置
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

// 从Spring工厂配置文件加载所有工厂类并返回一个映射表,其中key为工厂类名,value为一组工厂实例名
public 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()) {
            URL url = urls.nextElement();
            // 创建资源对象
            UrlResource resource = new UrlResource(url);
            // 加载资源中的属性
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            // 遍历属性,将工厂实例名与工厂类名映射到结果映射表中
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryClassName = ((String) entry.getKey()).trim(); // 工厂类名
                for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryClassName, factoryName.trim()); // 工厂实例名
                }
            }
        }
        // 将结果保存到缓存中并返回
        cache.put(classLoader, result);
        return result;
    } catch (IOException ex) {
        // 加载失败则抛出异常
        throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

3. org.springframework.boot.autoconfigure.AutoConfigurationPackage类

  • 该类用于自动扫描Spring Boot应用的主类所在的包及其子包中的所有Spring组件。
  • 底层源码实现:
  • 在Spring Boot启动时,会通过AutoConfigurationPackages类来自动扫描主类的包及其子包中所有的Spring组件。该类的实现代码如下:
/**
 * 自动配置包类,用于自动注册被 @AutoConfigurationPackage 标注的类到 Spring 容器中。
 */
public final class AutoConfigurationPackages {
    /**
     * 日志记录器。
     */
    private static final Log logger = LogFactory.getLog(AutoConfigurationPackages.class);

    /**
     * 发生启动失败时的错误信息。
     */
    private static final String STARTUP_FAILURE_MESSAGE = "Unable to initialize "
            + AutoConfigurationPackages.class.getSimpleName();

    /**
     * 表示没有指定自动配置包。
     */
    private static final Set<String> NO_PACKAGES = Collections.emptySet();

    /**
     * 用于存放线程本地的包名集合。
     */
    private static final ThreadLocal<Set<String>> packages = new ThreadLocal<>();

    /**
     * 私有构造函数,防止被实例化。
     */
    private AutoConfigurationPackages() {
    }

    /**
     * 注册自动配置包到 Spring 容器中。
     *
     * @param registry Spring 的 BeanDefinitionRegistry 对象,用于注册 Bean 定义
     * @param sources  启动类或配置类
     */
    public static void register(BeanDefinitionRegistry registry, Object... sources) {
        // 获取启动类或配置类的 Class 对象
        Class<?>[] startClasses = getStartClasses(sources);
        if (logger.isDebugEnabled()) {
            logger.debug("Searching specified packages: " +
                    StringUtils.arrayToCommaDelimitedString(startClasses));
        }
        // 将启动类或配置类所在包的包名加入 classNames 集合
        List<String> classNames = new ArrayList<>();
        for (Class<?> startClass : startClasses) {
            classNames.add(startClass.getPackage().getName());
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Using auto-configuration base packages: " +
                    StringUtils.collectionToCommaDelimitedString(classNames));
        }
    // 通过 PackageImportMetadataReadingVisitor 类获取被 @AutoConfigurationPackage 标注的所有类的包名,并将所有包注册到 Spring
    // 容器中
        PackageImportMetadata imports = PackageImportMetadataReadingVisitor
                .getImports(classNames.toArray(new String[0]));
        if (logger.isDebugEnabled()) {
            logger.debug("Importing " + imports.getNumberOfImportedPackages()
                    + " auto-configuration packages");
        }
        register(registry, imports);
    }

    /**
     * 注册自动配置包到 Spring 容器中。
     *
     * @param registry Spring 的 BeanDefinitionRegistry 对象,用于注册 Bean 定义
     * @param imports  PackageImportMetadata 对象,用于获取被 @AutoConfigurationPackage 标注的类的包名
     */
    public static void register(BeanDefinitionRegistry registry, PackageImportMetadata imports) {
        for (String packageName : imports.getImportedPackages()) {
            try {
                // 注册一个以 @AutoConfigurationPackage 标注的类名为 beanName 的 BeanDefinition
                registry.registerBeanDefinition(
                        generateName(packageName),
                        new ScannedGenericBeanDefinition(
                                ClassUtils.forName(packageName + ".package-info",
                                        AutoConfigurationPackages.class.getClassLoader())));
            } catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Failed to register bean definition for package " + packageName, ex);
            }
        }
    }

    /**
     * 生成包名对应的 beanName。
     *
     * @param packageName 包名
     * @return beanName
     */
    private static String generateName(String packageName) {
        return packageName + "." + AutoConfigurationPackage.class.getSimpleName();
    }

    /**
     * 获取线程本地的所有包名。
     *
     * @return 包名集合
     */
    public static Set<String> get() {
        Set<String> packageNames = packages.get();
        return (packageNames != null ? packageNames : NO_PACKAGES);
    }

    /**
     * 设置包名集合。
     *
     * @param packages 包名集合
     */
    static void set(List<String> packages) {
        set(new LinkedHashSet<>(packages));
    }

    /**
     * 设置包名集合。
     *
     * @param packages 包名集合
     */
    static void set(Set<String> packages) {
        AutoConfigurationPackages.packages.set(Collections.unmodifiableSet(packages));
    }

    /**
     * 移除线程本地的包名集合。
     */
    static void remove() {
        packages.remove();
    }

    /**
     * 通过扫描 Bean 定义获取所有被注册的自动配置包名。
     *
     * @param context Spring 应用上下文
     * @return 自动配置包名集合
     */
    public static synchronized Set<String> get(ApplicationContext context) {
        Set<String> packageNames = get();
        if (!packageNames.isEmpty()) {
            return packageNames;
        }
        long start = System.nanoTime();
        try {
            BeanDefinitionRegistry registry = getBeanDefinitionRegistry(context);
            Set<Object> candidates = new LinkedHashSet<>();
            for (String type : SCANNED_TYPES) {
                // 根据类型获取 Bean 名称集合
                candidates.addAll(Arrays.asList(context.getBeanNamesForType(
                        ClassUtils.resolveClassName(type, context.getClassLoader()), false, false)));
            }
            if (registry instanceof SingletonBeanRegistry) {
                SingletonBeanRegistry singletonBeanRegistry = (SingletonBeanRegistry) registry;
                for (Object candidate : candidates) {
                    String packageName = determinePackage(singletonBeanRegistry, candidate);
                    if (packageName != null) {
                        packageNames.add(packageName);
                    }
                }
            }
        } catch (Exception ex) {
            logger.warn(STARTUP_FAILURE_MESSAGE, ex);
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("Finished scanning in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start) + " ms");
            }
            if (packageNames.isEmpty()) {
                logger.debug("Could not find any auto-configuration packages");
            }
            set(packageNames);
        }
        return get();
    }

    /**
     * 获取 ApplicationContext 对应的 BeanDefinitionRegistry 对象。
     *
     * @param context Spring 应用上下文
     * @return BeanDefinitionRegistry 对象
     */
    private static BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
        if (context instanceof BeanDefinitionRegistry) {
            return (BeanDefinitionRegistry) context;
        }
        if (context instanceof AbstractApplicationContext) {
            return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();
        }
        throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
    }

    /**
     * 判断 Bean 对应的类所实现的接口中是否有被 @AutoConfigurationPackage 标注的接口,如果有,则返回该接口所
     * 在的包名。
     *
     * @param singletonBeanRegistry Spring 的 SingletonBeanRegistry 对象
     * @param bean                  Bean 对象
     * @return 包名
     */
    @Nullable
    private static String determinePackage(SingletonBeanRegistry singletonBeanRegistry, Object bean) {
        String[] interfaces = ClassUtils.getAllInterfacesForClass(bean.getClass(), singletonBeanRegistry.getBeanClassLoader());
        for (String ifc : interfaces) {
            if (ifc.startsWith("org.springframework.")) {
                continue;
            }
            try {
                Class<?> candidate = ClassUtils.forName(ifc + "$AutoConfiguration", singletonBeanRegistry.getBeanClassLoader());
                if (candidate.isAnnotationPresent(AutoConfigurationPackage.class)) {
                    return ((AutoConfigurationPackage) candidate.getAnnotation(AutoConfigurationPackage.class)).basePackage();
                }
            } catch (ClassNotFoundException ex) {
                // Ignore
            }
        }
        return null;
    }

    /**
     * 获取启动类或配置类的 Class 对象。
     *
     * @param sources 启动类或配置类
     * @return 启动类或配置类的 Class 对象数组
     */
    private static Class<?>[] getStartClasses(Object... sources) {
        List<Class<?>> startClasses = new ArrayList<>();
        for (Object source : sources) {
            Class<?> type = (source instanceof Class ? (Class<?>) source : source.getClass());
            AnnotationMetadata metadata = (type instanceof AnnotationMetadata ? (AnnotationMetadata) type :
                    StandardAnnotationMetadata.from(type));
            if (isEnabled(metadata)) {
                startClasses.add(type);
            }
        }
        return startClasses.toArray(new Class<?>[0]);
    }

    /**
     * 判断是否启用了 Spring Boot。
     *
     * @param metadata 注解元数据
     * @return 是否启用了 Spring Boot
     */
    private static boolean isEnabled(AnnotationMetadata metadata) {
        return metadata.isAnnotated(SpringBootApplication.class.getName());
    }

    /**
     * 用于扫描被 @AutoConfigurationPackage 标注的类,自动注册 BeanDefinition 的类。
     */
    private static final class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {

        private final AnnotationMetadata metadata;

        ScannedGenericBeanDefinition(AnnotationMetadata metadata) {
            this.metadata = metadata;
            setBeanClassName(metadata.getClassName());
        }

        @Override
        public AnnotationMetadata getMetadata() {
            return this.metadata;
        }
    }
}

4. org.springframework.boot.autoconfigure.SpringBootApplication注解

该注解是一个组合注解,包含了@EnableAutoConfiguration、@ComponentScan和@Configuration注解,用于定义Spring Boot应用的入口类。

  • @EnableAutoConfiguration注解:用于开启Spring Boot自动配置机制,根据classpath下的jar包和已有配置来判断应用需要哪些配置,并自动应用。

该注解是Spring Boot自动配置的核心注解,其底层实现原理是通过类路径下的META-INF/spring.factories文件来加载自动配置的类,并自动进行配置。具体实现原理如下:

  1. Spring Boot启动时会扫描类路径下的META-INF/spring.factories文件中定义的所有自动配置类。

  2. Spring Boot会将所有自动配置类按照顺序进行排序,然后依次进行配置。

  3. 自动配置类中通常会使用@Conditional注解来控制条件,判断是否需要进行该配置。

  4. Spring Boot会根据应用所添加的依赖和自定义配置来决定是否需要进行某个自动配置。

  • @ComponentScan注解:用于指定扫描组件包的路径。

该注解用于设置组件扫描的路径,Spring Boot会在这些路径下查找带有@Component、@Service、@Controller、@Repository等注解的类,并将其注入到Spring容器中。具体实现原理是使用了@ComponentScan类来扫描指定路径下的组件,并使用@Import注解将扫描到的组件Bean注册到Spring容器中。

  • @Configuration注解:表示该类是一个配置类,相当于一个Spring XML配置文件。

该注解用于标识一个类为配置类,作用类似于Spring XML配置文件。在Spring Boot中,通常使用Java代码来进行配置,使用@Configuration注解标识一个类为配置类,类中定义的@Bean注解用于注册Bean到Spring容器中。

5. org.springframework.boot.SpringApplication类

该类是Spring Boot应用的入口类,用于启动Spring Boot应用。

  • run方法:用于启动Spring Boot应用,自动加载配置文件并启动内嵌应用服务器。

SpringApplication类是Spring Boot应用的入口类,其run方法用于启动应用程序,并自动加载配置文件并启动内嵌的应用服务器。具体实现原理是使用SpringApplication类来加载应用程序所需的配置信息和启动类,并使用内嵌的Tomcat、Jetty或Undertow等Web服务器来启动应用程序。

6. org.springframework.boot.autoconfigure.EnableAutoConfiguration注解

该注解用于启用Spring Boot的自动配置机制。

  • @Import注解:用于导入一个或多个配置类,Spring Boot会自动加载这些配置类并进行配置。

@EnableAutoConfiguration注解是Spring Boot自动配置的核心注解之一,其底层实现原理是使用@Import注解来导入一个或多个自动配置类,Spring Boot会根据其顺序进行配置,并自动加载依赖的配置类。

7. org.springframework.context.annotation.Configuration注解

该注解用于标识一个类为配置类,相当于一个Spring XML配置文件。

  • @Bean注解:用于定义一个Bean,Spring Boot会自动进行实例化并加入到容器中。

@Configuration注解用于标识一个类为配置类,类中使用@Bean注解来定义Bean,并将其加入到Spring容器中。Spring Boot会自动扫描@Configuration注解的配置类,并使用@Bean注解来定义Bean,并将其加入到Spring容器中。具体实现原理是通过@Configuration注解来标识一个类为配置类,并使用@Bean注解来定义Bean,Spring Boot会根据@Bean注解的返回值类型自动进行实例化并加入到Spring容器中。

以下是一个简单的示例,使用@EnableAutoConfiguration注解实现自动配置:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

@EnableAutoConfiguration // 启用Spring Boot自动配置
@ComponentScan // 扫描组件
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

在上面的示例中,@EnableAutoConfiguration注解启用了Spring Boot的自动配置机制,@ComponentScan注解扫描了组件。运行这个应用程序,Spring Boot会根据自动配置规则自动配置应用程序。

8. org.springframework.context.annotation.Import注解

该注解用于将其他配置类导入到当前配置类中,Spring Boot会自动加载这些配置类并进行配置。具体步骤如下:

  1. 在配置类上添加@Import注解。
  2. 在@Import注解中传入待导入的配置类。
  3. Spring Boot会自动扫描并加载所有导入的配置类,完成相应的配置。

源码解析:

@Import注解的源码如下所示:

//@Target(ElementType.TYPE) 表示该注解可以用在类、接口、枚举等类型上
//@Retention(RetentionPolicy.RUNTIME) 表示该注解在运行时可用
//@Documented 表示该注解会被包含在文档中
public @interface Import {

    Class<?>[] value(); //定义了一个名为value的Class类型的数组参数,用于指定被导入的类

}

其中,@Target、@Retention和@Documented为元注解,表示该注解可以用于类上,运行时生效,可以被文档化。

@Import注解的作用是将其他配置类导入到当前配置类中,其参数value为一个数组,表示待导入的配置类。在使用过程中,可以使用以下两种方式进行导入:

  1. 直接传入待导入的配置类,如@Import(MyConfig.class)。
  2. 通过ImportSelector接口实现类进行动态导入,如@Import(MyImportSelector.class)。

9. org.springframework.context.annotation.Condition接口

该接口用于定义条件,Spring Boot会根据条件的判断结果来决定是否进行自动配置。具体步骤如下:

  1. 实现Condition接口,并实现matches方法。
  2. 在需要进行条件判断的注解上添加@Conditional注解,并传入实现了Condition接口的类。
  3. Spring Boot在加载配置时会自动扫描@Conditional注解,并根据matches方法的返回值来决定是否进行自动配置。

源码解析:

Condition接口的源码如下所示:

/**
* 条件接口,用于判断某个条件是否匹配
*/
public interface Condition {
    
    /**
    * 判断条件是否匹配
    * @param context 条件上下文
    * @param metadata 注解类型元数据
    * @return 如果条件匹配,则返回true,否则返回false
    */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

其中,ConditionContext为条件上下文信息,AnnotatedTypeMetadata为注解类型元数据信息。

Condition接口的作用是定义条件,其实现类需要实现matches方法来进行条件判断。matches方法有两个参数,分别为ConditionContext和AnnotatedTypeMetadata。

ConditionContext接口用于提供条件的上下文信息,其源码如下所示:

/* 
 * ConditionContext 接口定义了 Spring Boot 中某些条件注解的编写规范
 * 它继承了 EnvironmentCapable, BeanDefinitionRegistry, ResourceLoader 和 ClassLoader 接口
 */
public interface ConditionContext extends EnvironmentCapable, BeanDefinitionRegistry, ResourceLoader, ClassLoader {
    
    // 获取可配置的 ListableBeanFactory 对象
    ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
    
    // 获取 ClassLoader 对象
    ClassLoader getClassLoader();
    
    // 获取 Environment 对象
    Environment getEnvironment();
    
    // 获取 ResourceLoader 对象
    ResourceLoader getResourceLoader();
    
    // 获取 Registry 对象
    Registry getRegistry();
}

其中,EnvironmentCapable、BeanDefinitionRegistry、ResourceLoader和ClassLoader为ConditionContext的继承接口。ConditionContext主要提供了获取BeanFactory、ClassLoader、Environment、ResourceLoader等信息的方法。

AnnotatedTypeMetadata接口用于提供注解类型的元数据信息,其源码如下所示:

/**
 * AnnotatedTypeMetadata接口,提供获取注释类型和属性等相关方法
 */
public interface AnnotatedTypeMetadata {

    /**
     * 查询注释名称是否已注解
     *
     * @param annotationName 注释名称
     * @return 返回 true/false 表示是否注解了该名称
     */
    boolean isAnnotated(String annotationName);

    /**
     * 获取指定注释名称的所有属性和其值
     *
     * @param annotationName 注释名称
     * @return 返回 Map 表示该注释的所有属性和其值
     */
    Map<String, Object> getAnnotationAttributes(String annotationName);

    /**
     * 获取指定注释名称的所有属性和其值,支持设置类值是否转为字符串
     *
     * @param annotationName      注释名称
     * @param classValuesAsString 是否将类值转为字符串
     * @return 返回 Map 表示该注释的所有属性和其值
     */
    Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);

    /**
     * 获取指定注释名称的所有属性和其值,支持多值
     *
     * @param annotationName 注释名称
     * @return 返回 MultiValueMap 表示该注释的所有属性和其值,支持多值
     */
    MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);

    /**
     * 获取指定注释名称的所有属性和其值,支持多值,支持设置类值是否转为字符串
     *
     * @param annotationName      注释名称
     * @param classValuesAsString 是否将类值转为字符串
     * @return 返回 MultiValueMap 表示该注释的所有属性和其值,支持多值
     */
    MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);

    /**
     * 查询是否存在指定注释名称
     *
     * @param annotationName 注释名称
     * @return 返回 true/false 表示是否存在该注释名称
     */
    boolean hasAnnotation(String annotationName);

    /**
     * 获取所有注释类型名称
     *
     * @return 返回 Set 表示所有注释类型名称
     */
    Set<String> getAnnotationTypes();

    /**
     * 获取指定注释名称的所有元注释类型
     *
     * @param annotationName 注释名称
     * @return 返回 Set 表示该注释的所有元注释类型
     */
    Set<String> getMetaAnnotationTypes(String annotationName);

    /**
     * 查询是否有注解方法的指定注释名称
     *
     * @param annotationName 注释名称
     * @return 返回 true/false 表示是否有注解方法的该注释名称
     */
    boolean hasAnnotatedMethods(String annotationName);

    /**
     * 获取指定注释名称的所有注解方法
     *
     * @param annotationName 注释名称
     * @return 返回 Set 表示该注释名称的所有注解方法
     */
    Set<MethodMetadata> getAnnotatedMethods(String annotationName);

}

AnnotatedTypeMetadata主要提供了获取注解信息的方法,如获取注解的属性值、获取注解的类型等。

10. org.springframework.context.annotation.ConditionContext接口

该接口用于提供条件的上下文信息,Spring Boot会在启动时创建该接口的实现类,用来判断自动配置条件是否满足。具体步骤如下:

  1. 实现Condition接口,在matches方法中使用ConditionContext进行条件判断。
  2. 在需要进行条件判断的注解上添加@Conditional注解,并传入实现了Condition接口的类。
  3. Spring Boot在加载配置时会自动扫描@Conditional注解,并根据matches方法的返回值来决定是否进行自动配置。

源码解析:

ConditionContext接口的源码已在前文中进行了介绍,这里不再赘述。

11. org.springframework.context.annotation.Bean注解

该注解用于定义一个Bean,用于在Spring容器中定义一个Bean对象,Spring Boot会根据类路径、条件等信息进行自动配置。具体步骤如下:

  1. 在配置类中使用@Bean注解来定义Bean。
  2. 在自动配置时,Spring Boot会根据类路径、条件等信息加载并初始化Bean。
  3. 在需要使用Bean的地方,可以直接通过@Autowired注解进行注入。

源码解析:

@Bean注解的源码如下所示:

// 定义一个注解,表示这个注解可以用在方法上面
@Target({ElementType.METHOD})
// 表示这个注解在程序运行期间一直存在
@Retention(RetentionPolicy.RUNTIME)
// 表示这个注解会被包含在 JavaDoc 中
@Documented
public @interface Bean {
    // 定义了一个 value 字段,这个字段是一个数组,可以用别名 name 来表示
    @AliasFor("name")
    String[] value() default {};

    // 定义了一个 name 字段,这个字段是一个数组,可以用别名 value 来表示
    @AliasFor("value")
    String[] name() default {};

    // 定义了一个 initMethod 字段,默认为 true
    boolean initMethod() default true;

    // 定义了一个 destroyMethod 字段,默认为 true
    boolean destroyMethod() default true;
}

其中,@AliasFor表示别名注解,可以在value和name中任选其一进行配置。

@Bean注解的作用是定义一个Bean,在自动配置时,Spring Boot会根据类路径、条件等信息加载并初始化Bean。在@Bean注解所在的方法中,返回一个被@Bean注解修饰的对象,Spring Boot会将该对象注册到容器中,并根据需要进行初始化。

12. org.springframework.context.annotation.PropertySource注解

该注解用于定义一个属性源,用于加载指定的属性文件,Spring Boot会自动读取配置文件中的属性值,并注入到指定的Bean中。具体步骤如下:

  1. 在配置类中使用@PropertySource注解来指定属性文件的路径。
  2. 在需要使用属性值的地方,可以使用@Value注解进行注入。
  3. 在自动装配时,Spring Boot会自动加载该属性文件,并使用@Value注解为属性赋值。

源码解析:

@PropertySource注解的源码如下所示:

//声明该注解的作用对象为类级别
@Target(ElementType.TYPE)
//声明该注解的生命周期为运行时
@Retention(RetentionPolicy.RUNTIME)
//声明该注解会在javadoc中显示
@Documented
public @interface PropertySource {

    //声明value为String型的数组,用于读取配置文件的路径
    String[] value();

    //声明name为String型的数组,用于指定读取配置文件的路径,与value属性同义
    @AliasFor("value")
    String[] name() default {};

    //声明ignoreResourceNotFound为boolean型,默认为false,用于指定是否忽略未找到的配置文件
    boolean ignoreResourceNotFound() default false;

    //声明configuration为String型的数组,默认为空数组,用于指定配置文件所在的位置
    @AliasFor(annotation = Configuration.class, attribute = "value")
    String[] configuration() default {};

}

其中,@AliasFor表示别名注解,可以在value和name中任选其一进行配置。

@PropertySource注解的作用是指定属性文件的路径,在自动装配时,Spring Boot会自动加载该属性文件,并使用@Value注解为属性赋值。在@PropertySource注解中,需要指定属性文件的路径,如@PropertySource(“classpath:config.properties”)。Spring Boot会根据路径加载属性文件,并将其作为一个属性源进行注册。在需要使用属性值的地方,可以使用@Value注解进行注入。

13. org.springframework.boot.autoconfigure.condition.ConditionalOnClass注解

步骤:

  1. 在Spring Boot应用的配置类或Bean类上使用@ConditionalOnClass注解。
  2. 在注解中传入要判断是否存在的类作为value参数。如果指定的类存在于classpath中,则会进行自动配置。

底层源码讲解:
@ConditionalOnClass注解是一个条件注解,用于表示只有当classpath下存在指定的类时才进行自动配置。该注解的核心就是通过Spring框架的Condition接口的实现类ConditionOutcome来判断是否满足条件。如果指定的类存在于classpath中,则ConditionOutcome对象的match属性为true,否则为false。

相关类和方法:

  • org.springframework.boot.autoconfigure.condition.ConditionalOnClass
  • org.springframework.context.annotation.Condition
  • org.springframework.context.annotation.ConditionOutcome

实现步骤:

  1. 在Spring Boot应用的配置类或Bean类上使用@ConditionalOnClass注解。
  2. 将要判断的类作为value参数传入注解中。

示例代码:

@Configuration
@ConditionalOnClass(name = "com.example.MyClass")
public class MyConfiguration {
    // 配置内容...
}

14. org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean注解

步骤:

  1. 在Spring Boot应用的配置类或Bean类上使用@ConditionalOnMissingBean注解。
  2. 在注解中传入要判断是否存在的Bean的类型作为value参数。如果容器中不存在该类型的Bean,则会进行自动配置。

底层源码讲解:
@ConditionalOnMissingBean注解是一个条件注解,用于表示当容器中不存在指定类型的Bean时才进行自动配置。该注解的核心也是通过Condition接口的实现类ConditionOutcome来判断是否满足条件。如果容器中不存在指定类型的Bean,则ConditionOutcome对象的match属性为true,否则为false。

相关类和方法:

  • org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
  • org.springframework.context.annotation.Condition
  • org.springframework.context.annotation.ConditionOutcome

实现步骤:

  1. 在Spring Boot应用的配置类或Bean类上使用@ConditionalOnMissingBean注解。
  2. 将要判断的Bean的类型作为value参数传入注解中。

示例代码:

@Configuration
@ConditionalOnMissingBean(MyService.class)
public class MyConfiguration {
    // 配置内容...
}

@Configuration:表示当前类是一个配置类,用于定义Bean的创建和Bean之间依赖关系的维护。

@ConditionalOnMissingBean(MyService.class):表示当容器中不存在名为MyService的Bean时,才会创建此配置类对应的Bean。

public class MyConfiguration:定义了一个公共类MyConfiguration,该类是一个配置类,用于创建Bean。

// 配置内容…:在类中定义了一些配置内容用于创建Bean,具体内容需要根据实际情况而定。

15. org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration类

步骤:

  1. Spring Boot在启动时会扫描classpath下的@EnableAutoConfiguration注解标注的类,并执行其中的自动配置。
  2. DataSourceAutoConfiguration类是其中的一个,它会根据当前环境的配置情况来判断是否需要自动配置DataSource,并根据相应的配置信息进行自动配置。

底层源码讲解:
DataSourceAutoConfiguration类是Spring Boot自动配置DataSource的核心类,它基于条件注解和Bean注解实现。在这个类中,通过@ConditionalOnClass注解和@ConditionalOnMissingBean注解来判断是否需要自动配置DataSource。如果当前环境中存在javax.sql.DataSource类,则会进行自动配置。如果容器中已经存在DataSource类型的Bean,则不会进行自动配置。

相关类和方法:

  • org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  • org.springframework.boot.autoconfigure.condition.ConditionalOnClass
  • org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
  1. org.springframework.boot.autoconfigure.SpringBootApplication注解:

步骤:

  1. 在Spring Boot应用的主类上使用@SpringBootApplication注解。
  2. 该注解是一个组合注解,包含了@EnableAutoConfiguration、@ComponentScan和@Configuration注解。其中@EnableAutoConfiguration注解用于开启Spring Boot自动配置机制,@ComponentScan注解用于指定扫描组件包的路径,@Configuration注解表示这个类是一个配置类。

底层源码讲解:
@SpringBootApplication注解是一个组合注解,它包含了@EnableAutoConfiguration、@ComponentScan和@Configuration注解。它的核心是@EnableAutoConfiguration注解,用于开启Spring Boot自动配置机制。通过@EnableAutoConfiguration注解,Spring Boot会自动扫描classpath下的所有jar包,查找META-INF/spring.factories文件中的所有配置项,并将它们自动装载到Spring容器中。这些配置项就是Spring Boot的自动配置类,它们会根据当前环境的配置情况来自动配置相应的Bean。

相关类和方法:

  • org.springframework.boot.autoconfigure.SpringBootApplication
  • org.springframework.boot.autoconfigure.EnableAutoConfiguration
  • org.springframework.context.annotation.Configuration
  • org.springframework.context.annotation.ComponentScan

示例代码:

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

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐