自动配置原理

笔记参考B站黑马程序员视频:spring原理

1.自动配置原理

比如假设要导入第三方的配置,我们使用@Import注解

我们来模拟实现

package com.dreams.demo41;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.GenericApplicationContext;

import java.io.IOException;

public class Demo01 {

    @SuppressWarnings("all")
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(false);
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>");
        System.out.println(context.getBean(Bean1.class));
    }

    @Configuration
    @Import({AutoConfiguration1.class,AutoConfiguration2.class})
    static class Config {
    }


    @Configuration // 第三方的配置类
    static class AutoConfiguration1 {
        @Bean
        public Bean1 bean1() {
            return new Bean1("第三方");
        }
    }

    @Configuration // 第三方的配置类
    static class AutoConfiguration2 {
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {
        private String name;

        public Bean1() {
        }

        public Bean1(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Bean1{" +
                   "name='" + name + '\'' +
                   '}';
        }
    }
    static class Bean2 {

    }
}

 

通用的Bean要把他一起注入,使用@Import注解

@Configuration
@Import({AutoConfiguration1.class,AutoConfiguration2.class})
static class Config {
}

 

main方法的逻辑

这里因为要加载注解,所以要加入Bean工厂后处理器

@SuppressWarnings("all")
public static void main(String[] args) throws IOException {
    GenericApplicationContext context = new GenericApplicationContext();

    context.registerBean("config", Config.class);
    context.registerBean(ConfigurationClassPostProcessor.class);
    context.refresh();

    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    }
}

可以看到成功注入了

 

还有方法就是代码读入

selectImports 方法根据传入的 AnnotationMetadata 参数,返回需要导入的配置类的全限定类名数组。
在这里,返回了 AutoConfiguration1.class 和 AutoConfiguration2.class 的全限定类名数组,这两个类将被动态导入到Config配置类中。

@Configuration // 本项目的配置类
@Import(MyImportSelector.class)
static class Config {
}

static class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{AutoConfiguration1.class.getName(),AutoConfiguration2.class.getName()};
    }
}

 

读取配置文件的方法

META-INF/spring.factories,内部类使用$分割

com.dreams.demo41.Demo01$MyImportSelector=\
com.dreams.demo41.Demo01.AutoConfiguration1,\
com.dreams.demo41.Demo01.AutoConfiguration2

代码:

SpringFactoriesLoader.loadFactoryNames会读取所有META-INF下的spring.factories,包括自己配置的,导入的依赖中的META-INF下的spring.factories。

@Configuration // 本项目的配置类
@Import(MyImportSelector.class)
static class Config {
}

static class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
        return names.toArray(new String[0]);
    }
}

 

如果重复定义Bean

@Configuration // 本项目的配置类
@Import(MyImportSelector.class)
static class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1("本项目");
    }
}

前面注册的会被后面的覆盖

默认可以修改,在main方法里改为false就不能覆盖,直接报错,spring默认为false

context.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(false);

可以换成DeferredImportSelector实现就是晚加载,这样@Impose就不会先加载了

同时@ConditionalOnMissingBean表示如果不存在该Bean才加载,否则不加载。

static class MyImportSelector implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
        return names.toArray(new String[0]);
    }
}

@Configuration // 第三方的配置类
static class AutoConfiguration1 {
    @Bean
    @ConditionalOnMissingBean
    public Bean1 bean1() {
        return new Bean1("第三方");
    }
}

这样就不会报错

 

2.AopAutoConfiguration

我们直接读取AopAutoConfiguration,看看他加入了多少Bean

package com.dreams.demo41;

import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.type.AnnotationMetadata;

public class TestAopAuto {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        // 加入常用的处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
        context.registerBean(Config.class);
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    @Configuration
    @Import(MyImportSelector.class)
    static class Config {

    }

    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{AopAutoConfiguration.class.getName()};
        }
    }
}

这四个就是AopAutoConfiguration自动配置的Bean

我们查看源码看看他是如何配置进去的

一开始由@Configuration标志位为配置类

@ConditionalOnProperty:条件装配注解,表示仅当满足指定的属性条件时,该配置类才会生效。

  • prefix = “spring.aop”:属性的前缀是 spring.aop。
  • name = “auto”:属性名是 auto。
  • havingValue = “true”:属性值必须为 true。
  • matchIfMissing = true:如果缺少该属性时,默认条件匹配为 true。

也就是要求当 spring.aop.auto属性的值为 false 时,该配置类才会生效。

如果属性缺失,则默认匹配(matchIfMissing = true),而matchIfMissing就是说只要缺是上面这个属性就匹配。

可以看到加入了

我们设置以下这个键值

使用PropertySource加入–spring.aop.auto=false

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    StandardEnvironment env = new StandardEnvironment();
    env.getPropertySources().addLast(new SimpleCommandLinePropertySource("--spring.aop.auto=false"));
    context.setEnvironment(env);
    // 加入常用的处理器
    AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
    context.registerBean(Config.class);
    context.refresh();
    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    }
}

这样就不会加入了

回到源码可以看到,类里面还有两个内部类

@ConditionalOnClass 指定当类路径中存在指定的类(在这里是 Advice.class)时,才会创建并注册这个Bean。也就是只有当类路径上存在 Advice 类时,才会启用当前的配置类。

@ConditionalOnMissingClass 指定当类路径中不存在指定的类(在这里是 org.aspectj.weaver.Advice)时,才会创建并注册这个Bean。也就是只有当类路径上不存在 org.aspectj.weaver.Advice 类时,才会启用当前的配置类。

类似if-else了

我们的代码肯定会加入第一个,因为Advice是AOP的核心类,spring默认是加入了核心类。

 

第一个内部类里面又嵌套了两个内部类

@ConditionalOnProperty 指定当指定的配置属性满足特定条件时,才会创建并注册这个Bean。它检查名为 spring.aop.proxy-target-class 的属性是否存在,并且其值为 “false”。只有当这个条件满足时,才会启用当前的配置类。我们没有配置所以默认进入第二个。

@EnableAspectJAutoProxy(proxyTargetClass = true): 启用基于AspectJ的自动代理功能,并指定使用基于类的CGLIB代理。

这个注解里面使用了@Import,注入了AspectJAutoProxyRegistrar

从源码一步一步进去

AnnotationAwareAspectJAutoProxyCreator 是一个 Bean后处理器,主要就是创建代理。

然后他就加入了。

 

3.DataSource 自动配置

同样在上面的类基础上,将AOP自动配置类改为DataSource自动配置、MyBatis 自动配置、事务自动配置

static class MyImportSelector implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{
                DataSourceAutoConfiguration.class.getName(),
                MybatisAutoConfiguration.class.getName(),
                DataSourceTransactionManagerAutoConfiguration.class.getName(),
                TransactionAutoConfiguration.class.getName()
        };
    }
}

然后main方法获取输出

package com.dreams.demo41;

import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.type.AnnotationMetadata;

public class TestDataSourceAuto {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        StandardEnvironment env = new StandardEnvironment();
        env.getPropertySources().addLast(new SimpleCommandLinePropertySource(
                "--spring.datasource.url=jdbc:mysql://localhost:3306/test",
                "--spring.datasource.username=root",
                "--spring.datasource.password=123456"
        ));
        context.setEnvironment(env);
        AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
        context.registerBean(Config.class);

        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            String resourceDescription = context.getBeanDefinition(name).getResourceDescription();
            if (resourceDescription != null)
                System.out.println(name + " 来源:" + resourceDescription);
        }
    }

    @Configuration
    @Import(MyImportSelector.class)
    static class Config {

    }

    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{
                    DataSourceAutoConfiguration.class.getName(),
                    MybatisAutoConfiguration.class.getName(),
                    DataSourceTransactionManagerAutoConfiguration.class.getName(),
                    TransactionAutoConfiguration.class.getName()
            };
        }
    }
}

这里我们只输出来源不为null的,这样才是DataSource自动配置、MyBatis 自动配置、事务自动配置来的,为null是spring自带的

 

DataSource 自动配置的来源是org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class

进入DataSourceAutoConfiguration类源码

@Conditional(EmbeddedDatabaseCondition.class)判断是否是嵌入式数据库环境来决定是否启用这个配置类。嵌入式数据库环境是小型的,我们不成立,所以默认进入第二个内部类

@Conditional(PooledDataSourceCondition.class)判断当前是否是基于连接池数据源配置。spring与mybatis和jdbc整合时提供了。

所以这些就能加载这些配置类

进去可以看到这些配置,不过我们没有加入依赖所以爆红。

导入了mybatis依赖,就有了Hikari

可以看到上面传入了DataSourceProperties。还有一个重要的来源是DataSourceProperties,不过因为它的来源为null,所以上面没有打印出来。

@EnableConfigurationProperties(DataSourceProperties.class) 告诉 Spring Boot 在加载应用程序上下文时,将 DataSourceProperties 类型的配置属性绑定到 Environment 中。

这个就是用来绑定键值信息的,他绑定的信息都是以spring.datasource打头的

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    StandardEnvironment env = new StandardEnvironment();
    env.getPropertySources().addLast(new SimpleCommandLinePropertySource(
            "--spring.datasource.url=jdbc:mysql://localhost:3306/test",
            "--spring.datasource.username=root",
            "--spring.datasource.password=123456"
    ));
    context.setEnvironment(env);
    AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
    context.registerBean(Config.class);

    context.refresh();
    for (String name : context.getBeanDefinitionNames()) {
        String resourceDescription = context.getBeanDefinition(name).getResourceDescription();
        if (resourceDescription != null)
            System.out.println(name + " 来源:" + resourceDescription);
    }

    DataSourceProperties dataSourceProperties = context.getBean(DataSourceProperties.class);
    System.out.println(dataSourceProperties.getUrl());
    System.out.println(dataSourceProperties.getPassword());
    System.out.println(dataSourceProperties.getUsername());
}

通过dataSourceProperties.get方法就可以获取像–spring.datasource.username=root这种以spring.datasource开头的参数。而那些dataSource配置传入这个才能读取到数据库密码等等。

 

 

4.MyBatis 自动配置

与MyBatis 自动配置相关的是sqlSessionFactory 和sqlSessionTemplate

sqlSessionFactory 来源org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class
sqlSessionTemplate 来源org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class

打开 MybatisAutoConfiguration源码

@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}):当类路径中存在 SqlSessionFactory 和 SqlSessionFactoryBean 类时,该配置类才会生效。这意味着只有当 MyBatis 的必要类在类路径中时,才会进行 MyBatis 的配置。我们加入了mybatis依赖,所以一定会有。

@ConditionalOnSingleCandidate(DataSource.class):当容器中存在唯一的 DataSource 类型的 Bean 时,该配置类才会生效。这通常用于确保只有一个数据源被配置,以便 MyBatis 可以正确地进行数据库访问。

@EnableConfigurationProperties({MybatisProperties.class}):启用 MybatisProperties 类的配置属性绑定到 Environment,源码里要求是mybatis开头的属性

回到MybatisAutoConfiguration源码

@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class}):表示这个配置类应该在 DataSourceAutoConfiguration 和 MybatisLanguageDriverAutoConfiguration 类之后自动配置。这确保了在配置 MyBatis 之前,必要的数据源配置和语言驱动配置已经完成。

下面就是这两个配置了

可以看到这里加入了我们之前说的@ConditionalOnMissingBean注解

对于SqlSession,mybatis已经提供了实现

sqlSessionTemplate同样实现了sqlSession,提供了线程安全的、基于 Spring 的 MyBatis 集成方案。这的区别就是他提供了一个与当前线程绑定的SqlSession

在处理@Mapper注解的MapperFactoryBean后处理器里被调用。

可以看到是sqlSessionTemplate

回到 MybatisAutoConfiguration,可以看到还有一个配置

@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class }):当容器中不存在 MapperFactoryBean 类型和 MapperScannerConfigurer 类型的 Bean 时,该配置类才会生效。这意味着如果用户已经手动配置了这些 Bean,Spring 将不会自动配置。这种条件通常用于避免自动配置与手动配置冲突,确保用户的自定义配置优先。

然后就是执行@Import,加入AutoConfiguredMapperScannerRegistrar

他实现了ImportBeanDefinitionRegistrar接口,所以就可以使用编程的方式去补充BeanDefinition。这里补充的是Mapper的。

AutoConfiguredMapperScannerRegistrar 负责自动扫描项目中的 Mapper 接口,并注册这些接口的实现类作为 Spring 的 Bean。

所以我们只要提供mapper实在包给它,使用AutoConfigurationPackages.register方法,就能够扫描出@mapper注解配置的类,

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    StandardEnvironment env = new StandardEnvironment();
    env.getPropertySources().addLast(new SimpleCommandLinePropertySource(
            "--spring.datasource.url=jdbc:mysql://localhost:3306/test",
            "--spring.datasource.username=root",
            "--spring.datasource.password=123456"
    ));
    context.setEnvironment(env);
    AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
    context.registerBean(Config.class);

    String packageName = TestDataSourceAuto.class.getPackage().getName();
    System.out.println("当前包名:" + packageName);
    AutoConfigurationPackages.register(context.getDefaultListableBeanFactory(),
            packageName);

    context.refresh();
    for (String name : context.getBeanDefinitionNames()) {
        String resourceDescription = context.getBeanDefinition(name).getResourceDescription();
        if (resourceDescription != null)
            System.out.println(name + " 来源:" + resourceDescription);
    }
}

在当前包下提供几个@Mapper注解的类

源码中在@SpringBootApplication注解中用到

@SpringBootApplication注解中的@EnableAutoConfiguration

@EnableAutoConfiguration中的@AutoConfigurationPackage,它内部就是使用了AutoConfigurationPackages.register方法

 

5.事务自动配置

对应源码在DataSourceTransactionManagerAutoConfiguration中的内部类JdbcTransactionManagerConfiguration

@ConditionalOnMissingBean(TransactionManager.class) 是在当前容器中不存在 TransactionManager 类型的 Bean 时,就会生效

 

还有三个重要的Bean

来源在ProxyTransactionManagementConfiguration

基于切点切面和通知实现

还有transactionTemplate就是可以通过编程控制事务。

还有一个platformTransactionManagerCustomizers是对上面TransactionManager的扩展

 

 

5.MVC自动配置

与之相关的是:

  • ServletWebServerFactoryAutoConfiguration配置内嵌tomcat工厂的
  • DispatcherServletAutoConfiguration配置DispatcherServlet的
  • WebMvcAutoConfiguration自动配置了 Spring MVC 的基本设置,包括视图解析器、资源处理器、消息转换器等。
  • ErrorMvcAutoConfiguration自动配置 Spring Boot 的错误处理机制。
package com.dreams.demo41;

import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.context.annotation.Import;
import org.springframework.core.type.AnnotationMetadata;

public class TestMvcAuto {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();
        context.registerBean(Config.class);
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            String source = context.getBeanDefinition(name).getResourceDescription();
            if (source != null) {
                System.out.println(name + " 来源:" + source);
            }
        }
        context.close();

    }

    @Configuration
    @Import(MyImportSelector.class)
    static class Config {

    }

    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{
                    ServletWebServerFactoryAutoConfiguration.class.getName(),
                    DispatcherServletAutoConfiguration.class.getName(),
                    WebMvcAutoConfiguration.class.getName(),
                    ErrorMvcAutoConfiguration.class.getName()
            };
        }
    }
}

 

6.springboot自动配置

springboot的这部分逻辑在注解@EnableAutoConfiguration中

打开AutoConfigurationImportSelector的selectImports方法,进入源码

可以看到我们熟悉的读取方法

最后返回的是EnableAutoConfiguration

 

7.条件匹配原理

类似源码那样,可以使用注解来条件判断

模拟实现条件匹配:

使用@Conditional注解,传入判断类,它实现了Condition接口,根据是否存在DruidDataSource类来决定是否加载配置类。

matches 方法是 Condition 接口中定义的方法,用于确定是否符合条件以决定是否加载配置类或bean。使用ClassUtils.isPresent方法判断是否存在某类。

package com.dreams.demo42;

import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;

import java.io.IOException;

public class Demo01 {

    @SuppressWarnings("all")
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    @Configuration // 本项目的配置类
    @Import(MyImportSelector.class)
    static class Config {
    }

    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()};
        }
    }

    static class MyCondition1 implements Condition { // 存在 Druid 依赖
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource", null);
        }
    }

    static class MyCondition2 implements Condition { // 不存在 Druid 依赖
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return !ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource", null);
        }
    }

    @Configuration // 第三方的配置类
    @Conditional(MyCondition1.class)
    static class AutoConfiguration1 {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
    }

    @Configuration // 第三方的配置类
    @Conditional(MyCondition2.class)
    static class AutoConfiguration2 {
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {

    }

    static class Bean2 {

    }
}

 

因为我们加了依赖,所以Bean1被注入,而Bean2没有

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.8</version>
</dependency>

 

对上面的注解还可以优化。

类似spring源码,我们可以自定义注解@ConditionalOnClass

在 matches 方法中,通过 metadata 获取 ConditionalOnClass 注解的属性,判断给定的类名是否存在于类路径中。

AutoConfiguration1 和 AutoConfiguration2 分别使用了 @Configuration 注解,并通过 @ConditionalOnClass 条件注解来控制是否加载。

package com.dreams.demo42;

import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;

public class Demo02 {

    @SuppressWarnings("all")
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    @Configuration // 本项目的配置类
    @Import(MyImportSelector.class)
    static class Config {
    }

    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()};
        }
    }

    static class MyCondition implements Condition { // 存在 Druid 依赖
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());
            String className = attributes.get("className").toString();
            boolean exists = (boolean) attributes.get("exists");
            boolean present = ClassUtils.isPresent(className, null);
            return exists ? present : !present;
        }
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Conditional(MyCondition.class)
    @interface ConditionalOnClass {
        boolean exists(); // true 判断存在 false 判断不存在
        String className(); // 要判断的类名
    }

    @Configuration // 第三方的配置类
    @ConditionalOnClass(className = "com.alibaba.druid.pool.DruidDataSource", exists = false)
    static class AutoConfiguration1 {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
    }

    @Configuration // 第三方的配置类
    @ConditionalOnClass(className = "com.alibaba.druid.pool.DruidDataSource", exists = true)
    static class AutoConfiguration2 {
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {

    }

    static class Bean2 {

    }
}

 

 

8.参考

笔记参考B站黑马程序员视频:spring原理

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇