文章目录
@SpringBootApplication三个最重要的注解
组合注解@SpringBootApplication注解中三个最重要的注解:
@SpringBootConfiguration(底层实际是@Configuration)
@ComponentScan
@EnableAutoConfiguration 【最为重要,其用于自动装配】
@SpringBootConfiguration或@Configuration
@SpringBootConfiguration用于指定Springboot的启动类也是一个配置类,该配置类中的@Bean注解标注的方法也会被加载进Spring容器。
因为@SpringBootConfiguration底层也是@Configuration
@Configuration注解可以达到在Spring中使用xml配置文件的beans标签的作用。
@Bean就等同于xml配置文件中的bean标签
@SpringBootConfiguration和@Configuration的唯一区别:@SpringBootConfiguration在自动配置中会被SpringBoot识别
@ComponentScan
@ComponentScan作用是自动扫描并加载符合条件的组件(比如@Service、@Component和@Repository等),最终将它们的bean定义加载到IoC容器中
@EnableAutoConfiguration
SpringBoot中有许多@Enablexxxx开头的注解,@EnableAutoConfiguration注解与这些注解的实现方式是相同的,都是借助@Import注解,将所有符合自动配置条件的bean定义加载到IoC容器
- @EnableScheduling是通过@Import将Spring调度框架相关的bean定* 义都加载到IoC容器。
- @EnableZuulProxy是通过@Import将zuul相关的bean定义加载到IoC容器。
@Import
@Import注解的作用:导入指定配置对象到IOC容器
@Import导入配置有三种方式:
(1)导入普通类
@Import(value = {
UserBean.class})
public class UserConfig {
}
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserConfig.class);
UserBean user =(UserBean) applicationContext.getBean(UserBean.class.getName());
System.out.println(UserBean.class.getName());
}
}
public class UserBean {
public UserBean() {
System.out.println("========UserBean无参构造器==========");
}
@Bean
public User user(){
return new User();
}
}
(2)导入ImportBeanDefinitionRegistrar接口的实现类,该接口实现类中覆写的方法创建了普通类的BeanDefinition对象
@Import(value = {
CustomImportBeanDefinitionRegistrar.class})
public class UserConfig {
}
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(UserBean.class);
beanDefinitionRegistry.registerBeanDefinition(UserBean.class.getName(), rootBeanDefinition);
}
}
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserConfig.class);
UserBean user =(UserBean) applicationContext.getBean(UserBean.class.getName());
System.out.println(UserBean.class.getName());
}
}
public class UserBean {
public UserBean() {
System.out.println("========UserBean无参构造器==========");
}
@Bean
public User user(){
return new User();
}
}
(3)导入ImportSelector接口的实现类型,该接口实现类中覆写的方法中指定了该普通类的完全限定名【这种方法可以导入多个配置类】
@Import(value = {
UserImportSelector.class})
public class UserConfig {
}
public class UserImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 指定配置类的完全限定名,可以有多个,这里存在硬编码
return new String[]{
"top.onething.demo.config.UserBean"};
}
}
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserConfig.class);
UserBean user =(UserBean) applicationContext.getBean(UserBean.class.getName());
System.out.println(UserBean.class.getName());
}
}
public class UserBean {
public UserBean() {
System.out.println("========UserBean无参构造器==========");
}
@Bean
public User user(){
return new User();
}
}
导入ImportSelector接口的实现类这种方式,在上述代码中存在硬编码指定实现类路径的问题。为解决这个问题,SpringBoot默认把需要自动装配的配置类放到了classpath路径下META-INF目录的spring.factories文件中
SpringFactoryiesLoader
- SpringFactoriesLoader工厂加载机制是Spring内部提供的一个约定俗成的加载方式,只需要在模块的META-INF/spring.factories文件,这个Properties格式的文件中的key是接口、注解、或抽象类的全名,value是以逗号 “ , “ 分隔的实现类,使用SpringFactoriesLoader来实现相应的实现类注入Spirng容器中。
- SpringFactoryiesLoader会读取META-INF/spring.factories下的EnableAutoConfiguration的配置,紧接着在进行排除【去重,不同key下面可能有相同的类】与过滤【spring.factories中有redis、rabbitmq、kafka等需要的配置类,SpringFactoryiesLoader会全部加装,加装后根据pom文件的依赖过滤掉不需要的配置类】,进而得到需要装配的类
BeanDefinition
BeanDefinition:Bean的描述对象,包括Bean对象的字节码文件、是否懒加载、作用域等 BeanDefinition是接口,AbstractBeanDefinition其抽象实现类
BeanDefinition描述对象的三个重要属性
(1)class:
(2)autowireMode
#spring的自动装配的方式
//不自动注入(默认是这种模型)
AbstractBeanDefinition.AUTOWIRE_NO
//根据类型自动装配(但是这个类型是根据set***方法进行装配的)
AbstractBeanDefinition.AUTOWIRE_BY_TYPE
//根据名称自动装配
AbstractBeanDefinition.AUTOWIRE_BY_NAME
//根据构造函数自动装配
AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR
(3)constructorArumentValues
通过设置构造器参数来设置spring需要调用哪一个构造器,它会根据参数进行推断
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) defaultListableBeanFactory.getBeanDefinition("name");
//修改构造参数
ConstructorArgumentValues constructorArgumentValues1 = new ConstructorArgumentValues();
constructorArgumentValues1.addIndexedArgumentValue(0, "第1个参数");
constructorArgumentValues1.addIndexedArgumentValue(1, "第2个参数");
beanDefinition.setConstructorArgumentValues(constructorArgumentValues1);
//修改注入模型
beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_NAME);
获取Bean的描述对象----》BeanFactoryPostProcessor接口【Bean工厂的后置处理器,这就是修改Bean的描述对象】-----》getBean 根据Bean的描述对象创建Bean对象----->BeanPostProcessor接口【Bean对象后置处理器:这是修改的Bean实列对象】----->Bean实例化对象
-
应用启动后加载字节码文件,通过Spring的包扫描@Controller、@Service、@Dao、@Component、@Configuration、@Bean等注解标注的类或方法,生成他们各自的Bean描述对象,并这些Bean的描述对象存储到BeanDefinitionMap容器中,其中key为Bean的名称、value为Bean的描述对象
-
BeanFactory的实现类具有属性BeanDefinitionMap
从Spring容器中获取Bean对象
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
BeanDefinition bd = (BeanDefinition)this.beanDefinitionMap.get(beanName);
if (bd == null) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("No bean named '" + beanName + "' found in " + this);
}
throw new NoSuchBeanDefinitionException(beanName);
} else {
return bd;
}
}
SpringIO容器是一个单例缓存池,是Bean描述对象的集合
//根据配置类创建容器,容器中的元素即配置类生成的Bea对象
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(@Configuration注解标注的配置类.class);
//根据配置类所在包名创建容器,容器中的元素即配置类生成的Bea对象
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("@Configuration注解标注的配置类所在包名");
Object obj = applicationContext.getBean("bean名称");
总结
- SpringBoot是怎么自动装配的
自动装配是利用了SpringFactoriesLoader来加载META-INF/spring.factoires文件里所有配置的EnableAutoConfgruation,它会经过exclude和filter等操作,最终确定要装配的类 - 自动装配的什么对象
配置类的Bean定义对象,加载后放入IOC容器