springboot启动流程及原理,@SpringApplication注解分析

目录

Spring Boot、Spring MVC 和 Spring 的区别

一、springboot启动原理及相关流程概览

二、springboot的启动类入口

三、单单是SpringBootApplication接口用到了这些注解  

        1)@Configuration注解

   2)@ComponentScan注解

   3)@EnableAutoConfiguration注解

                a)AutoConfigurationPackage注解

                b)Import(AutoConfigurationImportSelector.class)注解

                c)自动配置幕后英雄:SpringFactoriesLoader详解

四、springboot启动流程概览图详解

五、深入探索SpringApplication执行流程

六、关于Bean的生命周期

七、BeanFactory 和ApplicationContext的区别

八、SpringMVC处理请求的大致流程

九、BEANFACTORY和FACTORYBEAN的区别与联系

十、Bean的循环依赖

十一、@Import注解介绍

十二、同一个类中调用 @Transaction注解的方法会有事务效果吗?


Spring Boot、Spring MVC 和 Spring 的区别
 

分别描述各自的特征:

Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等;但他们的基础都是Spring 的ioc和 aop,ioc 提供了依赖注入的容器, aop解决了面向切面编程,然后在此两者的基础上实现了其他延伸产品的高级功能。

Spring MVC提供了一种轻度耦合的方式来开发web应用;它是Spring的一个模块,是一个web框架;通过DispatcherServlet, ModelAndView 和 View Resolver,开发web应用变得很容易;解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。

Spring Boot实现了auto-configuration自动配置(另外三大神器actuator监控,cli命令行接口,starter依赖),降低了项目搭建的复杂度。它主要是为了解决使用Spring框架需要进行大量的配置太麻烦的问题,所以它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具;同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box)。

所以,用最简练的语言概括就是:

Spring 是一个“引擎”;

Spring MVC 是基于Spring的一个 MVC 框架;

Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。

一、springboot启动原理及相关流程概览

springboot是基于spring的新型的轻量级框架,最厉害的地方当属自动配置。那我们就可以根据启动流程和相关原理来看看,如何实现传奇的自动配置。

二、springboot的启动类入口

用过springboot的技术人员很显而易见的两者之间的差别就是视觉上很直观的:springboot有自己独立的启动类(独立程序)

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

从上面代码可以看出,Annotation定义(@SpringBootApplication)和类定义(SpringApplication.run)最为耀眼,所以要揭开SpringBoot的神秘面纱,我们要从这两位开始就可以了。


三、单单是SpringBootApplication接口用到了这些注解  

@Target(ElementType.TYPE) // 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明

@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三个生命周期)

@Documented // 表明这个注解应该被javadoc记录

@Inherited // 子类可以继承该注解

@SpringBootConfiguration // 继承了Configuration,表示当前是注解类

@EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助

@ComponentScan(excludeFilters = { // 扫描路径设置(具体使用待确认)

@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),

@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

public @interface SpringBootApplication {

...

}

在其中比较重要的有三个注解,分别是:

  1)@SpringBootConfiguration // 继承了Configuration,表示当前是注解类

  2)@EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助

  3)@ComponentScan(excludeFilters = { // 扫描路径设置(具体使用待确认)

   接下来对三个注解一一详解,增加对springbootApplication的理解:


1)@Configuration注解

按照原来xml配置文件的形式,在springboot中我们大多用配置类来解决配置问题

配置bean方式的不同: 

        a)xml配置文件的形式配置bean







        b)java configuration的配置形式配置bean

@Configuration

public class MockConfiguration{

    //bean定义

}

注入bean方式的不同:

        a)xml配置文件的形式注入bean



...

        b)java configuration的配置形式注入bean

@Configuration

public class MockConfiguration{

    @Bean

    public MockService mockService(){

        return new MockServiceImpl();

    }

}

任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。

表达bean之间依赖关系的不同:

        a)xml配置文件的形式表达依赖关系



  



        b)java configuration配置的形式表达依赖关系(重点)

如果一个bean A的定义依赖其他bean B,则直接调用对应的JavaConfig类中依赖bean B的

@Configuration

public class MockConfiguration{

  @Bean

  public MockService mockService(){

      return new MockServiceImpl(dependencyService());

  }

  @Bean

  public DependencyService dependencyService(){

      return new DependencyServiceImpl();

  }

}

  
2)@ComponentScan注解

组件扫描,自动扫描和装配Bean
要点:a)对应xml配置中的元素;

   b)ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义;(以启动类所在包为根目录扫描)

   c) 将这些bean定义加载到IoC容器中

我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。

注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages

   
3)@EnableAutoConfiguration注解

此注解顾名思义是可以自动配置,所以应该是springboot中最为重要的注解。

在spring框架中就提供了各种以@Enable开头的注解,例如: @EnableScheduling、@EnableCaching、@EnableMBeanExport等; @EnableAutoConfiguration的理念和做事方式其实一脉相承简单概括一下就是,借助@Import的支持,收集和注册特定场景相关的bean定义。  

  @EnableScheduling是通过@Import将Spring调度框架相关的bean定义都加载到IoC容器【定时任务、时间调度任务】
  @EnableMBeanExport是通过@Import将JMX相关的bean定义加载到IoC容器【监控JVM运行时状态】
  @EnableAutoConfiguration也是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器。

@EnableAutoConfiguration作为一个复合Annotation,其自身定义关键信息如下:

@SuppressWarnings("deprecation")

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@AutoConfigurationPackage【重点注解】

@Import(AutoConfigurationImportSelector.class)【重点注解】

public @interface EnableAutoConfiguration {

...

}

其中最重要的两个注解已经标注:

1. @AutoConfigurationPackage:该注解上有一个@Import({Registrar.class})注解,其中Registrar类的作用是将启动类所在的包下的所有子包组件扫描注入到spring容器中。 因此这就是为什么将controller、service等包放在启动类的同级目录下的原因

 2. @Import({AutoConfigurationImportSelector.class}):AutoConfigurationImportSelector类中有一个getCandidateConfigurations()方法,这个方法通过SpringFactoriesLoader.loadFactoryNames()查找位于META-INF/spring.factories文件中的所有自动配置类并加载这些类。

    当然还有其中比较重要的一个类就是:AutoConfigurationImportSelector.class


a)AutoConfigurationPackage注解

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@Import(AutoConfigurationPackages.Registrar.class)

public @interface AutoConfigurationPackage {
    ...
}

通过@Import(AutoConfigurationPackages.Registrar.class)

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

        @Override

        public void registerBeanDefinitions(AnnotationMetadata metadata,

                BeanDefinitionRegistry registry) {

            register(registry, new PackageImport(metadata).getPackageName());

        }
        ……
    }

它其实是注册了一个Bean的定义;

new PackageImport(metadata).getPackageName(),它其实返回了当前主程序类的同级以及子级的包组件(重点);

(重点)那这总体就是注册当前主程序类的同级以及子级的包中的符合条件的Bean的定义?

以上图为例,DemoApplication是和demo包同级,但是demo2这个类是DemoApplication的父级,和example包同级

也就是说,DemoApplication启动加载的Bean中,并不会加载demo2,这也就是为什么,我们要把DemoApplication放在项目的最高级中。


b)Import(AutoConfigurationImportSelector.class)注解

(重点)可以从图中看出  AutoConfigurationImportSelector 实现了 DeferredImportSelector 从 ImportSelector继承的方法:selectImports。

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {

        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }

        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader

                .loadMetadata(this.beanClassLoader);

        AnnotationAttributes attributes = getAttributes(annotationMetadata);

        List configurations = getCandidateConfigurations(annotationMetadata,

                attributes);
        configurations = removeDuplicates(configurations);

        Set exclusions = getExclusions(annotationMetadata, attributes);

        checkExcludedClasses(configurations, exclusions);

        configurations.removeAll(exclusions);

        configurations = filter(configurations, autoConfigurationMetadata);

        fireAutoConfigurationImportEvents(configurations, exclusions);

        return StringUtils.toStringArray(configurations);
    }

第9行List configurations = getCandidateConfigurations(annotationMetadata,attributes);其实是去加载各个组件jar下的   public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";外部文件。

该方法在springboot启动流程——bean实例化前被执行,返回要实例化的类信息列表;

如果获取到类信息,spring可以通过类加载器将类加载到jvm中,现在我们已经通过spring-boot的starter依赖方式依赖了我们需要的组件,那么这些组件的类信息在select方法中就可以被获取到。

protected List 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;

 }

其返回一个自动配置类的类名列表,方法调用了loadFactoryNames方法,查看该方法

public static List loadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) {

    String factoryClassName = factoryClass.getName();

    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());

 }

自动配置器会跟根据传入的factoryClass.getName()到项目系统路径下所有的spring.factories文件中找到相应的key,从而加载里面的类。

这个外部文件,有很多自动配置的类。如下:

(重点)其中,最关键的要属@Import(AutoConfigurationImportSelector.class),借助AutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件(spring.factories)的bean定义(如Java Config@Configuration配置)都加载到当前SpringBoot创建并使用的IoC容器。就像一只“八爪鱼”一样。 


c)自动配置幕后英雄:SpringFactoriesLoader详解

借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自动配置功效才得以大功告成!

SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories加载配置,加载工厂类。

SpringFactoriesLoader为Spring工厂加载器,该对象提供了loadFactoryNames方法,入参为factoryClass和classLoader即需要传入工厂类名称和对应的类加载器,方法会根据指定的classLoader,加载该类加器搜索路径下的指定文件,即spring.factories文件;

传入的工厂类为接口,而文件中对应的类则是接口的实现类,或最终作为实现类。

public abstract class SpringFactoriesLoader {
    
    //...
    public static  List loadFactories(Class factoryClass, ClassLoader classLoader) {
    ...
  }
    public static List loadFactoryNames(Class factoryClass, ClassLoader classLoader) {
    ....
  }
}

配合@EnableAutoConfiguration使用的话,它更多是提供一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类

上图就是从SpringBoot的autoconfigure依赖包中的META-INF/spring.factories配置文件中摘录的一段内容,可以很好地说明问题。

(重点)所以,@EnableAutoConfiguration自动配置的魔法其实就变成了:

从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。


四、springboot启动流程概览图详解

  1. 首先从main找到run()方法,在执行run()方法之前new一个SpringApplication对象
  2. 进入run()方法,创建应用监听器SpringApplicationRunListeners开始监听
  3. 然后加载SpringBoot配置环境(ConfigurableEnvironment),然后把配置环境(Environment)加入监听对象中
  4. 然后加载应用上下文(ConfigurableApplicationContext),当做run方法的返回对象
  5. 最后创建Spring容器,refreshContext(context),实现starter自动化配置和bean的实例化等工作。

每一个SpringBoot程序都有一个主入口,这个主入口就是main方法,而main方法中都会调用SpringBootApplication.run方法,一个快速了解SpringBootApplication启动过程的好方法就是在run方法中打一个断点,然后通过Debug的模式启动工程,逐步跟踪了解SpringBoot源码是如何完成环境准备和启动加载bean的。

查看SpringBootApplication.run方法的源码就可以发现SpringBoot启动的流程主要分为两大阶段:

初始化SpringApplication运行SpringApplication
运行SpringApplication的过程
其中运行SpringApplication的过程又可以细分为以下几个部分:

1)SpringApplicationRunListeners 引用启动监控模块

2)ConfigrableEnvironment配置环境模块和监听:包括创建配置环境、加载属性配置文件和配置监听

3)ConfigrableApplicationContext配置应用上下文:包括配置应用上下文对象、配置基本属性和刷新应用上下文

2 初始化SpringApplication
步骤1进行SpringApplication的初始化,配置基本的环境变量、资源、构造器、监听器,初始化阶段的主要作用是为运行SpringApplication实例对象启动环境变量准备以及进行必要的资源构造器的初始化动作,代码如下:

public SpringApplication(ResourceLoader resourceLoader, Object... sources){
    this.resourceLoader = resourceLoader;
    initialize(source);
}

@SupressWarnings({"unchecked","rowtypes"})
private void initialize(Object[] sources){
    if(sources != null && sources.length > 0){
        this.sources.addAll(Arrays.asList(sources));
    }
    this.WebEnvironment = deduceWebEnvironment;
    setInitiallizers((Collection) getSpringFactoriesInstances(ApplicationContextInitiallizer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}
SpringApplication方法的核心就是this.initialize(sources)初始化方法,SpringApplication通过调用该方法完成初始化工作。deduceWebEnvironment方法用来判断当前应用的环境,该方法通过获取两个类来判断当前环境是否是Web环境。而getSpringFactoriesInstances方法主要用来从spring.factories文件中找出Key为ApplicationContextInitiallizer的类并实例化,然后调用setInitiallizer方法设置到SpringApplication的initiallizer属性中,找到它所有应用的初始化器。接着调用setListener方法设置应用监听器,这个过程可以找到所有应用程序的监听器,然后找到应用启动主类名称。

3 运行SpringApplication
SpringBoot正式启动加载过程,包括启动流程监控模块、配置环境加载模块、ApplicationContext容器上下文环境加载模块。refreshContext方法刷新应用上下文并进行自动化配置模块加载,也就是上文提到的SpringFactoriesLoader根据指定classpath加载META-INF/spring.factories文件的配置,实现自动配置核心功能。运行SpringApplication的主要代码如下:

public ConfigurableApplicationContext run(String... args) {
    ConfigurableApplicationContext context = null;
    FailureAnalyzer analyzer = null;
    configureHeadlessProperty();
    // 步骤1
    SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
    try{
        // 步骤2
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        Banner printBanner = printBanner(environment);
        // 步骤3
        context = createApplicationContext();
        prepareContext(context, environment, listeners, applicationArguments, printBanner);
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        listeners.finished(context, null);
        // 省略
        return context;
    }
}
3.1 SpringApplciationRunListener应用启动监控模块

应用启动监控模块对应上述源码中的注释步骤1到步骤2之间的两行代码,它创建了应用的监听器SpringApplicationRunListeners并开始监听,监控模块通过调用getSpringFactoriesInstances私有协议从META-INF/spring.factories文件中取得SpringApplicationRunListener监听器实例。

当前事件监听器SpringApplicationRunListener中只有一个EventPublishingRunlistener广播事件监听器,它的starting方法会封装成SpringApplicatiionEvent事件广播出去,被SpringApplication中配置的listener监听。这一步骤执行完成后也会同时通知SpringBoot其他模块目前监听初始化已经完成,可以开始执行启动方案了。

3.2 ConfigurableEnviroment 配置环境模块和监听

对应上述源码注释中的步骤2到步骤3之间的几行代码,下面分解步骤说明:

(1) 创建配置环境,创建应用程序的环境信息。如果是Web程序,创建StandardServletEnvironment;否则创建StandardEnviroment;

(2) 加载属性配置文件,将配置文件加入监听器对象中(SpringApplicationRunListeners)。通过configPropertySource方法设置properties配置文件,通过执行configProfies方法设置profiles;

(3) 配置监听,发布environmentPrepared事件,及调用ApplicationListener#onApplicationEvent方法,通知SpringBoot应用的environment已经准备完成。

3.3  ConfigurableApplicationContext配置应用上下文

对应源码中的步骤3下面的几行代码,下面分解步骤说明:

(1)配置Spring容器应用上下文对象,它的作用是创建Run方法的返回对象ConfigurableApplicationContext(应用配置上下文),此类主要继承了ApplicationLifeCycle、Closeable接口,而ApplicationContext是Spring框架中负责Bean注入容器的主要载体,负责bean加载、配置管理、维护bean之间依赖关系及Bean生命周期管理。

(2)配置基本属性,对应prepareContext方法将listener、environment、banner、applicationArguments等重要组件与Spring容器上下文对象关联。借助SpringFactoriesLoader查找可用的ApplciationContextInitailizer, 它的initialize方法会对创建好的ApplicationContext进行初始化,然后它会调用SpringApplicationRunListener#contextPrepared方法,此时SpringBoot应用的ApplicationContext已经准备就绪,为刷新应用上下文准备好了容器。

(3)刷新应用上下文,对应源码中的refreshContext(context)方法将通过工程模式产生应用上下文中所需的bean。实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载、bean的实例化等核心工作。然后调用SpringApplicationRunListener#finish方法告诉SprignBoot应用程序,容器已经完成ApplicationContext装载。

SpringBoot应用程序的启动流程主要包括初始化SpringApplication和运行SpringApplication两个过程。其中初始化SpringApplication包括配置基本的环境变量、资源、构造器和监听器,为运行SpringApplciation实例对象作准备;而运行SpringApplication实例为应用程序正式启动加载过程,包括SpringApplicationRunListeners  引用启动监控模块、ConfigrableEnvironment配置环境模块和监听及ConfigrableApplicationContext配置应用上下文。当完成刷新应用的上下文和调用SpringApplicationRunListener#contextPrepared方法后表示SpringBoot应用程序已经启动完成。  

onContext配置应用上下文。当完成刷新应用的上下文和调用SpringApplicationRunListener#contextPrepared方法后表示SpringBoot应用程序已经启动完成。

五、深入探索SpringApplication执行流程

 

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered

EventPublishingRunListener实现了SpringApplicationRunListener接口;

实现了方法,下面是部分方法源码

    @Override
    public void starting() {

        this.initialMulticaster.multicastEvent(

                new ApplicationStartingEvent(this.application, this.args));

    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {

        this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(

                this.application, this.args, environment));

    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {

        this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(

                this.application, this.args, context));

    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {

     .....................


六、关于Bean的生命周期


springboot启动流程及原理,@SpringApplication注解分析_第1张图片

一个Bean的构造函数初始化时是最先执行的,这个时候,bean属性还没有被注入;

@PostConstruct注解的方法优先于InitializingBean的afterPropertiesSet执行,这时Bean的属性竟然被注入了;

spring很多组件的初始化都放在afterPropertiesSet做,想和spring一起启动,可以放在这里启动;

spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用;

实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点;但是init-method方式消除了对spring的依赖;

如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

Bean在实例化的过程中:

Constructor > @PostConstruct > InitializingBean > init-method


七、BeanFactory 和ApplicationContext的区别


BeanFactory和ApplicationContext都是接口,并且ApplicationContext间接继承了BeanFactory。

BeanFactory是Spring中最底层的接口,提供了最简单的容器的功能,只提供了实例化对象和获取对象的功能,而ApplicationContext是Spring的一个更高级的容器,提供了更多的有用的功能。  

ApplicationContext提供的额外的功能:获取Bean的详细信息(如定义、类型)、国际化的功能、统一加载资源的功能、强大的事件机制、对Web应用的支持等等。

加载方式的区别:BeanFactory采用的是延迟加载的形式来注入Bean;ApplicationContext则相反的,它是在Ioc启动时就一次性创建所有的Bean,好处是可以马上发现Spring配置文件中的错误,坏处是造成浪费。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,

        MessageSource, ApplicationEventPublisher, ResourcePatternResolver


八、SpringMVC处理请求的大致流程


1、用户发送请求至前端控制器DispatcherServlet

2、DispatcherServlet收到请求调用HandlerMapping处理器映射器。

3、处理器映射器根据请求url找到具体的处理器,生成处理器对象Handler及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

4、DispatcherServlet通过HandlerAdapter(让Handler实现更加灵活)处理器适配器调用处理器

5、执行处理器(Controller,也叫后端控制器)。

6、Controller执行完成返回ModelAndView(连接业务逻辑层和展示层的桥梁,持有一个ModelMap对象和一个View对象)。

7、HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet

8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器

9、ViewReslover解析后返回具体View

10、DispatcherServlet对View进行渲染视图(将ModelMap模型数据填充至视图中)。

11、DispatcherServlet响应用户


九、BEANFACTORY和FACTORYBEAN的区别与联系


两者都是接口;
BeanFactory主要是用来创建Bean和获得Bean的;
FactoryBean跟普通Bean不同,其返回的对象不是指定类的一个实例,而是该FactoryBean的getObject方法所返回的对象;
通过BeanFactory和beanName获取bean时,如果beanName不加&则获取到对应bean的实例;如果beanName加上&,则获取到FactoryBean本身的实例
FactoryBean 通常是用来创建比较复杂的bean(如创建mybatis的SqlSessionFactory很复杂),一般的bean 直接用xml配置即可,但如果创建一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,用xml配置比较困难,这时可以考虑用FactoryBean。
 


十、Bean的循环依赖

请点击查看详情

对于Spring中Bean的管理,下图一目了然: 

先调用构造函数进行实例化,然后填充属性,再接着进行其他附加操作和初始化,正是这样的生命周期,才有了Spring的解决循环依赖,这样的解决机制是根据Spring框架内定义的三级缓存来实现的,也就是说:三级缓存解决了Bean之间的循环依赖。我们从源码中来说明。

先来看Spring中Bean工厂是怎么获取Bean的(AbstractBeanFactory中):


一级一级向下寻找,找出了前面提到的三级缓存,也就是三个Map集合类:

singletonObjects:第一级缓存,里面放置的是已经实例化好的单例对象;

earlySingletonObjects:第二级缓存,里面存放的是提前曝光的单例对象;

singletonFactories:第三级缓存,里面存放的是将要被实例化的对象的对象工厂。

所以当一个Bean调用构造函数进行实例化后,即使set属性还未填充,就可以通过三级缓存向外暴露依赖的引用值进行set(所以循环依赖问题的解决也是基于Java的引用传递),这也说明了另外一点,基于构造函数的注入,如果有循环依赖,Spring是不能够解决的。

还要说明一点,Spring默认的Bean Scope是单例的,而三级缓存中都包含singleton,可见是对于单例Bean之间的循环依赖的解决,Spring是通过三级缓存来实现的。


十一、@Import注解介绍

请点击详情介绍

十二、同一个类中调用 @Transaction注解的方法会有事务效果吗?


没有,可以Autowired注入自己,然后再调用注入的类中的方法,即自己依赖自己,循环依赖;

这里在一个内部调用应该是相当于单纯的调用方法this.methodName(),并没有AOP代理。
 

你可能感兴趣的:(SpringBoot,笔记,java,spring,boot,java,spring)