目录
2.1、基于java代码的bean配置--@Configuration在@SpringbootApplication中的作用
2.4、bean发现(重点看)
一、起步依赖
二、自动装配
2.1、基于java代码的bean配置
@Configuration 和 @Bean 用来代替xml中的配置文件,创建Bean并将bean的控制权交给spring管理;
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${drivername}"/>
<property name="url" value="${url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${password}"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:com/chen/mapper/*.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
相当于用基于java代码的配置方式:
@Configuration
public class Conf {
@Bean
public DataSource myDataSource() {
return xxxBuilder.create().build();
}
@Bean
public SqlSessionFactor mySqlSessionFac(@Qualifier DataSource dataSource) {
SqlSessionFactor fac= new SqlSessionFactor ();
fac.setDataSource(dataSource);
return fac.getObject();
}
}
2.2、自动装配条件依赖
要完成自动配置是有依赖条件的,需要在类路径中存在SqlSessionFactory.class、SqlSessionFactoryBean.class这两个类,需要存在DataSource这个bean且这个bean完成自动注册。
@Configuration
//某个class位于类路径上,才会实例化这个Bean
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
//仅在当前上下文中存在某个bean时,才会实例化这个Bean
@ConditionalOnBean({DataSource.class})
@EnableConfigurationProperties({MybatisProperties.class})
//在某个bean完成自动配置后实例化这个bean
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration {
private final MybatisProperties properties;
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
//....
return factory.getObject();
}
}
2.3、bean参数获取
到此我们已经知道了bean的配置过程,但是还没有看到springboot是如何读取yml或者properites配置文件的的属性来创建数据源的?靠 @EnableConfigurationProperties 注解;
@ConfigurationProperties注解的作用是把yml或者properties配置文件转化为bean。
@EnableConfigurationProperties注解的作用是使@ConfigurationProperties注解生效。
如果只配置@ConfigurationProperties注解,在spring容器中是获取不到yml或者properties配置文件转化的bean的。
@ConfigurationProperties(
prefix = "spring.datasource"
)
public class MybatisProperties {
private ClassLoader classLoader;
private String name;
private boolean generateUniqueName;
private Class<? extends DataSource> type;
private String driverClassName;
private String url;
private String username;
private String password;
private String jndiName;
...
}
2.4、bean发现
springboot默认扫描启动类所在的包下的主类与子类的所有组件,但并没有包括依赖包的中的类,那么依赖包中的bean是如何被发现和加载的?靠 @SpringBootApplication 注解;
我们可以把 @SpringBootApplication
看作是 @Configuration
、@EnableAutoConfiguration
、@ComponentScan
注解的集合。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {
@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
),
@Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)
}
)
public @interface SpringBootApplication {
... ...
}
@Configuration:允许在上下文中注册额外的 bean 或导入其他配置类
@ComponentScan:注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean;
@EnableAutoConfiguration :启用 SpringBoot 的自动配置机制
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@Import导入需要自动配置的组件。
@AutoConfigurationPackage的作用就是自动配置的包; 进入@AutoConfigurationPackage,发现也是引入了 @Import({Registrar.class})注解,点进 Registrar.class 的代码如下:
Registrar:登记员
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry,
new String[]{(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()});
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
这两句代码的作用就是加载启动类所在的包下的主类与子类的所有组件注册到spring容器,这就是前文所说的springboot默认扫描启动类所在的包下的主类与子类的所有组件。
那问题又来了,要搜集并注册到spring容器的那些beans来自哪里?
进入 AutoConfigurationImportSelector类,
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
...
}
SpringFactoriesLoader.loadFactoryNames方法调用loadSpringFactories方法从所有的jar包中读取META-INF/spring.factories文件信息。
下面是spring-boot-autoconfigure这个jar中spring.factories文件部分内容,其中有一个key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值定义了需要自动配置的bean,通过读取这个配置获取一组@Configuration类。
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
oorg.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
每个xxxAutoConfiguration都是一个基于java的bean配置类。实际上,这些xxxAutoConfiguratio不是所有都会被加载,会根据xxxAutoConfiguration上的@ConditionalOnClass等条件判断是否加载。
总结:SpringBoot 在启动时会扫描外部引用 jar 包中的 META-INF/spring.factories
文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。
ps: bean加载
如果要让一个普通类交给Spring容器管理,通常有以下方法:
1、使用 @Configuration与@Bean 注解
2、使用@Controller @Service @Repository @Component 注解标注该类,然后启用@ComponentScan自动扫描
3、使用@Import 方法
springboot中使用了@Import 方法
@EnableAutoConfiguration注解中使用了@Import({AutoConfigurationImportSelector.class})注解,AutoConfigurationImportSelector实现了DeferredImportSelector接口,
DeferredImportSelector接口继承了ImportSelector接口,ImportSelector接口只有一个selectImports方法。