笔记参考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原理


