Fork me on GitHub

Spring

什么是Spring?

Spring是一个IOC容器,用来管理bean,使用依赖注入实现控制反转,可以很好的与其他框架集成(如struts,hibernate),提供AOP机制弥补OOP的代码重复问题,更方便将程序中交叉业务逻辑抽取为切面,自动注入给方法执行(如日志、异常、安全、事务)。是一种轻量级J2EE框架,旨在提高开发人员的开发效率以及系统的可维护性。

我们一般说的Spring框架就是Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。这些模块是核心容器、数据访问/集成、Web、AOP(面向切面编程)、工具、消息和测试模块。比如Core Container中的Core组件是Spring所有组件的核心,Beans组件和Context组件是实现IOC和DI的基础,AOP组件用来实现面向切面编程。

Spring官网(https://spring.io/)列出的Spring的6个特征:

核心技术:依赖注入(DI),AOP,事件(Events),资源,i18n,验证,数据绑定,类型转换,SpEL。

测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。

数据访问:事务,DAO支持,JDBC,ORM,编组XML。

Web支持:Spring MVC和Spring WebFlux Web框架。

集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。

语言:Kotlin,Groovy,动态语言。

列举一些重要的Spring模块?

下图对应的是Spring 4.x的版本,目前最新的5.x版本中Web模块的Portlet组件已经被废弃掉,同时增加了用于异步响应式处理的WebFlux组件。

Spring Core:基础,可以说Spring其他所有的功能都依赖于该类库。主要提供IOC和DI功能。

Spring Aspects:该模块为与AspectJ的集成提供支持。

Spring AOP:提供面向方面的编程实现。

Spring JDBC:Java数据库连接。

Spring JMS:Java消息服务。

Spring ORM:用于支持Hibernate等ORM工具。

Spring Web:为创建Web应用程序提供支持。

Spring Test:提供了对JUnit和TestNG测试的支持。

谈谈自己对于Spring IOC和AOP的理解

IOC

IOC(Inversion Of Controll,控制反转)是一种设计思想,就是将原本在程序中手动创建对象和对象之间的调用过程的控制权,交由给Spring框架来管理。IOC在其他语言中也有应用,并非Spring特有。IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象。

将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入。(ioc容器对象为DefaultListableBeanFactory)这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。

IOC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。在实际项目中一个Service类可能由几百甚至上千个类作为它的底层,假如我们需要实例化这个Service,可能要每次都搞清楚这个Service所有底层类的构造函数,这可能会把人逼疯。如果利用IOC的话,你只需要配置好,然后在需要的地方引用就行了,大大增加了项目的可维护性且降低了开发难度同时也降低了耦合度。

IOC的底层原理:XML解析,工厂模式,反射

Spring提供IOC容器实现两种方式:(两个接口),
(1) BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用,加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
(2) ApplicationContext: BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
*加载配置文件时候就会把在配置文件对象进行创建

Spring时代我们一般通过XML文件来配置Bean,后来开发人员觉得用XML文件来配置不太好,于是Sprng Boot注解配置就慢慢开始流行起来。

上图是Spring IOC的初始化过程,IOC的源码阅读:https://javadoop.com/post/spring-ioc。

AOP

AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。

Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理。

当然也可以使用AspectJ,Spring AOP中已经集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。使用AOP之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样可以大大简化代码量。我们需要增加新功能也方便,提高了系统的扩展性。日志功能、事务管理和权限管理等场景都用到了AOP。

Spring AOP和AspectJ AOP有什么区别?

Spring AOP是属于运行时增强,而AspectJ是编译时增强。Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)。

Spring AOP已经集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。

如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择AspectJ,它比SpringAOP快很多。

Spring中的bean的作用域有哪些?

1.singleton:Spring中默认的作用域,单实例,保证一个容器中只有一个实例,。

2.prototype:每次getBean()请求都会创建一个新的bean实例。(注意singleton在加载配置文件时就已经创建对象,而prototype是在getBean的时候才创建对象)

3.request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。

4.session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。

5.application:bean被定义在ServletContext的生命周期中复用一个单例对象。

6.websocket:bean被定义在websocket的生命周期中复用一个单例对象。

6.global-session:全局session作用域,仅仅在基于Portlet的Web应用中才有意义,Spring5中已经没有了。Portlet是能够生成语义代码(例如HTML)片段的小型Java Web插件。它们基于Portlet容器,可以像Servlet一样处理HTTP请求。但是与Servlet不同,每个Portlet都有不同的会话。

Spring中的单例bean的线程安全问题了解吗?

大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例bean存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。

有两种常见的解决方案:

1.在bean对象中尽量避免定义可变的成员变量(不太现实),也就是无状态的类。

2.在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal中(推荐的一种方式)。

Spring中的bean生命周期?

1.Bean容器找到配置文件中Spring Bean的定义。

2.Bean容器利用Java Reflection API创建一个Bean的实例。

3.如果涉及到一些属性值,利用set()方法设置一些属性值。

4.如果Bean实现了BeanNameAware接口,调用setBeanName()方法,传入Bean的名字。

5.如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。

6.如果Bean实现了BeanFactoryAware接口,调用setBeanClassFacotory()方法,传入ClassLoader对象的实例。

7.与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。

8.如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法。

9.如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法。

10.如果Bean在配置文件中的定义包含init-method属性,执行指定的方法。

11.如果有和加载这个Bean的Spring容器相关的BeanPostProcess对象,执行postProcessAfterInitialization()方法。

12.当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy()方法。

13.当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法。

Spring中bean的自动装配?

1.byName 根据id/name

 

2.byType 根据类名

3.constructor 根据构造参数

4.no 人工指定(默认)

5.@autowired自动装配bean,可在字段、setter方法、构造器上使用

6.组件扫描的方式,如@ComponentScan

说说自己对于Spring MVC的了解?

谈到这个问题,我们不得不提提之前Model1和Model2这两个没有Spring MVC的时代。

Model1时代:很多学Java比较晚的后端程序员可能并没有接触过Model1模式下的JavaWeb应用开发。在Model1模式下,整个Web应用几乎全部用JSP页面组成,只用少量的JavaBean来处理数据库连接,访问等操作。这个模式下JSP即是控制层又是表现层。显而易见,这种模式存在很多问题。比如将控制逻辑和表现逻辑混杂在一起,导致代码重用率极低;又比如前端和后端相互依赖,难以进行测试并且开发效率极低。

Model2时代:学过Servlet并做过相关Demo的朋友应该了解Java Bean(Model)+JSP(View)+Servlet(Controller)这种开发模式,这就是早期的Java Web MVC开发模式。Model是系统中涉及的数据,也就是dao和bean;View是用来展示模型中的数据,只是用来展示;Controller是将用户请求都发送给Servlet做处理,返回数据给JSP并展示给用户。

Model2模式下还存在很多问题,Model2的抽象和封装程度还远远不够,使用Model2进行开发时不可避免地会重复造轮子,这就大大降低了程序的可维护性和可复用性。于是很多Java Web开发相关的MVC框架应运而生,比如Struts2,但是由于Struts2比较笨重,随着Spring轻量级开发框架的流行,Spring生态圈出现了Spring MVC框架。Spring MVC是当前最优秀的MVC框架,相比于Struts2,Spring MVC使用更加简单和方便,开发效率更高,并且Spring MVC运行速度更快。

MVC是一种设计模式,Spring MVC是一款很优秀的MVC框架。Spring MVC可以帮助我们进行更简洁的Web层的开发,并且它天生与Spring框架集成。Spring MVC下我们一般把后端项目分为Service层(处理业务)、Dao层(数据库操作)、Entity层(实体类)、Controller层(控制层,返回数据给前台页面)。

Spring MVC的简单原理图如下:

Spring MVC的工作原理了解嘛?

流程说明:

1.客户端(浏览器)发送请求,直接请求到DispatcherServlet。

2.DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。

3.解析到对应的Handler(也就是我们平常说的Controller控制器)。

4.HandlerAdapter会根据Handler来调用真正的处理器来处理请求和执行相对应的业务逻辑。

5.处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是逻辑上的View。

6.ViewResolver会根据逻辑View去查找实际的View。

7.DispatcherServlet把返回的Model传给View(视图渲染)。

8.把View返回给请求者(浏览器)。

Spring框架中用到了哪些设计模式

1.工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。

2.代理设计模式:Spring AOP功能的实现,还有动态代理

3.单例设计模式:Spring中的bean默认都是单例的。

4.模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到了模板模式。还有在核心方法refresh中postProcessBeanFactory

5.包装器设计模式:动态数据源。

6.观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。还有listener,event,multicast

7.适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。Adapter

8.策略模式:XmlBeanDefinitionReader,PropertiesBeanDefinitionReader

9.装饰圈模式:BeanWrapper

10.责任链模式:使用aop的时候会生成一个拦截器

12.委托者模式:delegate

。。。

@Component和@Bean的区别是什么

1.作用对象不同。@Component注解作用于类,而@Bean注解作用于方法。

2.@Component注解通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用@ComponentScan注解定义要扫描的路径)。@Bean注解通常是在标有该注解的方法中定义产生这个bean,告诉Spring这是某个类的实例,当我需要用它的时候还给我。

3.@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean注解来注册bean。比如当引用第三方库的类需要装配到Spring容器的时候,就只能通过@Bean注解来实现。

@Bean注解的使用示例:

复制代码
@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}
复制代码

上面的代码相当于下面的XML配置:

<beans>
    <bean id="transferService" class="com.yanggb.TransferServiceImpl"/>
</beans>

下面这个例子是无法通过@Component注解实现的:

复制代码
@Bean
public OneService getService(status) {
    case (status)  {
        when 1:
                return new serviceImpl1();
        when 2:
                return new serviceImpl2();
        when 3:
                return new serviceImpl3();
    }
}
复制代码

将一个类声明为Spring的bean的注解有哪些?

我们一般使用@Autowired注解去自动装配bean。而想要把一个类标识为可以用@Autowired注解自动装配的bean,可以采用以下的注解实现:

1.@Component注解。通用的注解,可标注任意类为Spring组件。如果一个Bean不知道属于哪一个层,可以使用@Component注解标注。

2.@Repository注解。对应持久层,即Dao层,主要用于数据库相关操作。

3.@Service注解。对应服务层,即Service层,主要涉及一些复杂的逻辑,需要用到Dao层(注入)。

4.@Controller注解。对应Spring MVC的控制层,即Controller层,主要用于接受用户请求并调用Service层的方法返回数据给前端页面。

Spring事务管理的方式有几种?

1.编程式事务:在代码中硬编码(不推荐使用)。

2.声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务(@Transactional)。

Spring事务中的隔离级别有哪几种?

在TransactionDefinition接口中定义了五个表示隔离级别的常量:

ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。

ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。

ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生

ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。

ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

Spring事务中有哪几种事务传播行为?

在TransactionDefinition接口中定义了八个表示事务传播行为的常量。

支持当前事务的情况:

PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。

不支持当前事务的情况:

PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。

PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。

PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。

其他情况:

PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED。

Spring事务什么时候会失效?

spring事务的原理是AOP,进行了切面增强,那么失效的根本原因就是AOP不起作用,常见的情况有以下几种

1.发生了自调用,类里面使用this调用本类的方法,此时这个this对象不是spring代理类,而是它对象本身。

2方法不是public修饰的,@Transactional只能作用于public方法上,否则事务失效,若非要使用在非public方法上,可以开启AspectJ代理模式。

3.数据库不支持事务(如mysql中的Myisam引擎)

4.没有被spring管理(如Service类上没有添加@Service,不会被扫描)

5.异常被吃掉,事务不会回滚(如进行了catch,没有抛出异常),或者抛出的异常没有被定义,默认定义的异常为RuntimeException和Error

Spring的事务是如何回滚的?
spring的事务是由aop来实现的,首先要生成具体的代理对象,然后按照aop的整套流程来执行具体的操作逻辑,
正常情况下要通过通知来完成核心功能,但是事务不是通过通知实现的,而是通过一个TransactionInterceptor来实现的,
然后调用invoke来实现具体的逻辑。
1.先做准备工作,解析各个方法上事务相关的属性,根据具体的属性来判断是否开始新事务
2.当需要开启的时候,获取数据库连接,关闭自动提交功能,开启事务
3.执行具体的sql逻辑操作
4.在操作过程中,如果执行失败了,那么会通过completeTransactionAfterThrowing看来完成事务的回滚操作,回滚的具体逻辑是通过doRollBack方法来实现的,实现的时候也是要先获取链接对象,通
过连接对象来回滚。
5.如果执行过程中,没有任何意外情况的发生,那么通过commitTransactionAfterReturning来完成事务的提交操作,提交的具体逻辑是通过doCommit方法来实现的,实现的时候也要获取链接,通过链接对象来提交
6.当事务执行完毕之后需要清除相关的事务信息cleanupTransactionInfo

spring是如何解决循环依赖问题的

三级缓存

一级缓存对象:singletonObjects,

二级缓存对象:earlySingletonObjects,

三级缓存对象:singletonFactories,

在创建对象的时候主要有6个方法:

getBean doGetBean createBean doCreteBean createBeanInstance populateBean

 

1、三个缓存对象,在获取数据的时候,是按照什么顺序来获取的?
先获取一级缓存,没有在获取二级缓存,没有再获取三级缓存,所以当前面的缓存中存在了对象那么后面就需要把缓存对象给清空
2、如果只有一级缓存,能解决循环依赖问题吗?
不能,如果只有一级缓存,那么成品对象和半成品对象会放到一起,这个是没办法区分了,所以需要两个缓存来分别存放不同状态的对象,一级缓存放成品,二级缓存放半成品
3、如果只有二个缓存,能否解决循环依赖问题?
在刚刚的整个流程中,三级缓存一共出现了几次? getSingleton,doCreateBean
如果对象的创建过程中不包含aop,那么二级缓存就可以解决循环依赖问题,但是包含aop的操作,循环依赖问题是解决不了的

报错信息如下:

Exception in thread "main"org.springframework.beans.factory.BeanCurrentlyInCreationException:

Error creating beanwith name 'a': Bean with name 'a' has been injected into other beans [b] in its raw version aspart of a circular reference, but has eventually been wrapped.

This means that said otherbeans do not use the final version of the bean.(主要是这句话,这意味着所述的其他bean不使用bean的最终版本,也就是成品对象)

This is often the result of over-eager typematching - consider using 'getBeanNamesForType' with the 'allowEagerInit'flag turned off,for example.

4、为什么添加了aop的操作之后就需要添加三级缓存来解决这个问题?

三级缓存加了什么操作?

添加了一个getEarlyBeanReference的方法

在创建代理对象的时候,是否需要生成原始对象?需要

当创建完成原始对象之后,后续有需要创建代理对象,那么对象在引用的时候应该使用哪一个?
换句话说,就是一个beanName对应有两个对象,(原始对象和代理对象)
因为在整个容器中,有且仅能有一个同名的对象,当需要生成代理对象的时候,就要把代理对象覆盖原始对象
程序是怎么知道在什么时候要进行代理对象的创建的呢?
需要一个类似于回调的接口判断,当需要第一次对外暴露使用的时候,来判断当前对象是否需要去创建代理对象,getEarlyBeanReference方法中的if判断,如果需要代理就返回代理对象,如果没有代理就返回原始对象

Spring容器的refresh()【创建、刷新】完整总结

1、prepareRefresh()刷新前的预处理;
    0)、this.closed.set(false),this.active.set(true)  设置一些标记位
    1)、initPropertySources()初始化一些属性设置;(交由子类去实现,比如web容器中的 AbstractRefreshableWebApplicationContext 就去初始化了servlet的一些init参数等等)
    2)、getEnvironment().validateRequiredProperties();检验属性的合法等
    3)、earlyApplicationEvents= new LinkedHashSet<ApplicationEvent>();初始化容器,保存一些早期的事件;
    
2、obtainFreshBeanFactory();获取BeanFactory;
    1)、refreshBeanFactory();抽象方法,子类【AbstractRefreshableApplicationContext】唯一实现的:
            ①、若已经存在beanFactory了,那就做一些清理工作(销毁单例Bean、关闭工厂)
            ②、创建了一个this.beanFactory = new DefaultListableBeanFactory();并且设置id
            ③、把旧的工厂的属性赋值给新创建的工厂:customizeBeanFactory(beanFactory)
            ④、loadBeanDefinitions(beanFactory):加载Bean定义。抽象方法,由子类去决定从哪儿去把Bean定义加载进来,实现有比如:
                    XmlWebApplicationContext:专为web设计的从xml文件里加载Bean定义(借助XmlBeanDefinitionReader)
                    ClassPathXmlApplicationContext/FileSystemXmlApplicationContext:均由父类AbstractXmlApplicationContext去实现这个方法的,也是借助XmlBeanDefinitionReader
                    AnnotationConfigWebApplicationContext:基于注解驱动的容器。(也是当下最流行、最重要的一个实现,前面一篇博文对此有重点分析),借助了AnnotatedBeanDefinitionReader.register()方法加载Bean定义
                        (这里面需要注意的是:.register()只是把当前这一个Class对象registry.registerBeanDefinition()了,至于内部的@Bean、@ComponentScan扫描到的,都不是在此处注册的)
                        
                    有必要说一句:AnnotationConfigApplicationContext是在非web环境下的容器。它虽然没有实现实现loadBeanDefinitions()抽象方法,是因为它在new对象的时候,已经调用了.register()完成配置Bean定义信息的注册了
    2)、getBeanFactory();返回刚才GenericApplicationContext创建的BeanFactory对象;
    3)、将创建的BeanFactory【DefaultListableBeanFactory】返回;
    =======到这一步截止,BeanFactory已经创建好了(只不过都还是默认配置而已),配置Bean的定义信息也注册好了=======
    
3、prepareBeanFactory(beanFactory);BeanFactory的预准备工作(对BeanFactory进行一些设置);
    1)、设置BeanFactory的类加载器、StandardBeanExpressionResolver、ResourceEditorRegistrar
    2)、添加感知后置处理器BeanPostProcessor【ApplicationContextAwareProcessor】,并设置一些忽略EnvironmentAware、EmbeddedValueResolverAware、xxxxx(因为这个处理器都一把抓了)
    3)、注册【可以解析的(表示虽然不在容器里,但还是可以直接 @Auwowired)】自动装配;我们能直接在任何组件中自动注入(@Autowired):
            BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
    5)、添加BeanPostProcessor【ApplicationListenerDetector】 检测注入进来的Bean是否是监听器
    6)、Detect a LoadTimeWeaver and prepare for weaving, if found.添加编译时的AspectJ支持:LoadTimeWeaverAwareProcessor
        (添加的支持的条件是:beanFactory.containsBean("loadTimeWeaver"))
    7)、给BeanFactory中注册一些能用的组件;
        environment-->【ConfigurableEnvironment】、
        systemProperties-->【Map<String, Object>】、
        systemEnvironment-->【Map<String, Object>】

4、postProcessBeanFactory(beanFactory);BeanFactory准备工作完成后进行的后置处理工作;(由子类完成)
    一般web容器都会对应的实现此方法,比如 AbstractRefreshableWebApplicationContext:
        1)、添加感知BeanPostProcessor【ServletContextAwareProcessor】,支持到了ServletContextAware、ServletConfigAware
        2)、注册scopse:beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());当然还有SCOPE_SESSION、SCOPE_APPLICATION
        3)、向上线一样,注册【可以解析的】自动注入依赖:ServletRequest/ServletResponse/HttpSession/WebRequest
            (备注:此处放进容器的都是xxxObjectFactory类型,所以这是为何@Autowired没有线程安全问题的重要一步)
        4)、registerEnvironmentBeans:注册环境相关的Bean(使用的registerSingleton,是直接以单例Bean放到容器里面了)
            servletContext-->【ServletContext】
            servletConfig-->【ServletConfig】
            contextParameters-->【Map<String, String>】 保存有所有的init初始化参数(getInitParameter)
            contextAttributes-->【Map<String, Object>】 servletContext的所有属性(ServletContext#getAttribute(String))
            
========以上是BeanFactory的创建及预准备工作,至此准备工作完成了,那么接下来就得利用工厂干点正事了========

5、invokeBeanFactoryPostProcessors(beanFactory);执行BeanFactoryPostProcessor的方法;
    BeanFactoryPostProcessor:BeanFactory的后置处理器。此处调用,现在就表示在BeanFactory标准初始化之后执行的;
    两个接口:BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor(子接口)
    1)、执行BeanFactoryPostProcessor们的方法;
    
        ===先执行BeanDefinitionRegistryPostProcessor===
        1)、获取所有的BeanDefinitionRegistryPostProcessor;(当然会最先执行我们手动set进去的Processor,但是这个一般都不会有)
        2)、先执行实现了PriorityOrdered优先级接口的BeanDefinitionRegistryPostProcessor、
            postProcessor.postProcessBeanDefinitionRegistry(registry)
        3)、在执行实现了Ordered顺序接口的BeanDefinitionRegistryPostProcessor
        4)、最后执行没有实现任何优先级或者是顺序接口的BeanDefinitionRegistryPostProcessors
        (小细节:都会调用getBean(“name”,BeanDefinitionRegistryPostProcessor.class)方法,所以都会先实例化,才去执行的)
        
        **这里面需要特别的介绍一个处理器:`ConfigurationClassPostProcessor`,它是一个BeanDefinitionRegistryPostProcessor**
        **它会解析完成所有的@Configuration配置类,然后所有@Bean、@ComponentScan等等Bean定义都会搜集进来了,所以这一步是非常的重要的**
    
        ===再执行BeanFactoryPostProcessor的方法(顺序逻辑同上,略)===
    2)、再次检测一次添加对AspectJ的支持。为何还要检测呢?through an @Bean method registered by ConfigurationClassPostProcessor,这样我们注入了一个切面Bean,就符合条件了嘛
        
6、registerBeanPostProcessors(beanFactory);注册BeanPostProcessor(Bean的后置处理器)【 intercept bean creation】
        **不同接口类型的BeanPostProcessor;在Bean创建前后的执行时机是不一样的**
        BeanPostProcessor:BeanPostProcessor是一个工厂钩子,允许Spring框架在新创建Bean实例时对其进行定制化修改,比如填充Bean、创建代理、解析Bean内部的注解等等。。。
        DestructionAwareBeanPostProcessor:Bean销毁时候
        InstantiationAwareBeanPostProcessor:Bean初始化的时候
        SmartInstantiationAwareBeanPostProcessor:初始化增强版本:增加了一个对Bean类型预测的回调(一般是Spring内部使用,调用者还是使用InstantiationAwareBeanPostProcessor就好)
        MergedBeanDefinitionPostProcessor:合并处理Bean定义的时候的回调【该类型的处理器保存在名为internalPostProcessors的List中】、
        
        1)、获取所有的 BeanPostProcessor;后置处理器都默认可以通过PriorityOrdered、Ordered接口来执行优先级
        2)、先注册PriorityOrdered优先级接口的BeanPostProcessor;
            把每一个BeanPostProcessor;添加到BeanFactory中
            beanFactory.addBeanPostProcessor(postProcessor);
        3)、再注册Ordered接口的、最后注册没有实现任何优先级接口的、最终注册MergedBeanDefinitionPostProcessor
            (此处细节:BeanPostProcessor本身也是一个Bean,其注册之前一定先实例化,而且是分批实例化和注册。
            另外还有一个非常非常重要的一点就是阶段顺序问题:
            我们可以把BeanPostProcessor的实例化与注册分为四个阶段:
                    第一阶段applicationContext内置阶段、
                    第二阶段priorityOrdered阶段、
                    第三阶段Ordered阶段、
                    第四阶段nonOrdered阶段
            因为是分批注册,所以我们同阶段是不能拦截到同阶段的BeanPostProcessor的实例化的。举例子:
            PriorityOrdered的只能被内置阶段的比如:ApplicationContextAwareProcessor(可以注入啦)/ApplicationListenerDetector(可以接受事件啦)这种拦截
            而:Ordered就可以被   内置的、PriorityOrdered都拦截到了
            。。。 以此类推。。。
            
            所以我们的BeanPostProcessor是可以@Autowired 比如Service、Dao来做一些事的。单思,但是一定要【注意避免BeanPostProcessor启动时的“误伤”陷阱】,什么意思?大概解释一下如下:
                可能由于你的Processor依赖于某个@Bean,从而让它提前实例化了,然后就很可能错过了后面一些BeanPostProcessor的处理,造成“误伤”
                (SpringBoot中使用Shiro、Spring-Cache的时候,使用不当会出现这样的问题)
    
        6)、这一步非常有意思:moving it to the end of the processor chain。它的又注册了一次,作用是把这个探测器移动到处理器的底部,最后一个(显然,最后一个是为了不要放过任何Bean)
            (小细节:可能有小伙伴疑问,这里也是new出来,这这样容器内不就有两个探测器对象了吗?气其实不然,ApplicationListenerDetector它重写了hashCode方法,且只和应用applicationContext有关)
            return ObjectUtils.nullSafeHashCode(this.applicationContext);所以对它执行remove的时候,会被当作同一个对象处理,能把老的移除成功添加新的的

7、initMessageSource();初始化MessageSource组件(做国际化功能;消息绑定,消息解析)
        1)、看容器中是否有id为messageSource的,类型是MessageSource的组件
            如果有赋值给messageSource,如果没有自己创建一个DelegatingMessageSource;
                MessageSource:取出国际化配置文件中的某个key的值;能按照区域信息获取;
        2)、把创建好的MessageSource注册在容器中,以后获取国际化配置文件的值的时候,可以自动注入MessageSource
        beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource)
        
8、initApplicationEventMulticaster();初始化事件派发器;
        1)、从BeanFactory中获取applicationEventMulticaster的ApplicationEventMulticaster;
        2)、如果上一步没有配置;创建一个SimpleApplicationEventMulticaster,将创建的ApplicationEventMulticaster添加到BeanFactory中
        
9、onRefresh();留给子容器(子类) 容器刷新的时候做些事
        AbstractRefreshableWebApplicationContext:this.themeSource = UiApplicationContextUtils.initThemeSource(this);
        
10、registerListeners();把容器中将所有项目里面的ApplicationListener注册进来;
        1、拿到容器里所有的Bean定义的名字,类型为ApplicationListener,然后添加进来
            getApplicationEventMulticaster().addApplicationListener(listener);
        2、派发之前步骤产生的事件(早期事件)
        (细节:此处只是把Bean的名字放进去,Bean还没有实例化哦~~~~)

11、finishBeanFactoryInitialization(beanFactory);初始化所有剩下的单实例bean;这应该是最核心的一步了
    1)、为容器初始化ConversionService(容器若没有就不用初始化了,依然采用getBean()初始化的) 提供转换服务
    2)、若没有设置值解析器,那就注册一个默认的值解析器(lambda表示的匿名处理)
    3)、实例化LoadTimeWeaverAware(若存在)
    4)、清空临时类加载器:beanFactory.setTempClassLoader(null)
    5)、缓存(快照)下当前所有的Bean定义信息 beanFactory.freezeConfiguration();
    ==== 更精确的是说是根据Bean的定义信息:beanDefinitionNames来实例化、初始化剩余的Bean ====
    6)、beanFactory.preInstantiateSingletons();初始化后剩下的单实例bean(过程这里就不详说了)
    
12、finishRefresh();完成BeanFactory的初始化创建工作;IOC容器就创建完成
        0)、clearResourceCaches(); (Spring5.0才有)
        1)、initLifecycleProcessor();初始化和生命周期有关的后置处理器;从容器中找是否有lifecycleProcessor的组件【LifecycleProcessor】;如果没有new DefaultLifecycleProcessor();
            关于Lifecycle接口的使用,也专门讲解过,这里不聊了
        2)、getLifecycleProcessor().onRefresh();  相当于上面刚注册,下面就调用了
        3)、publishEvent(new ContextRefreshedEvent(this));发布容器刷新完成事件;
        4)、liveBeansView.registerApplicationContext(this); 和MBean相关,略
    
    ======总结===========
    1)、Spring容器在启动的时候,先会保存所有注册进来的Bean的定义信息(可以有N种方式);
        1)、xml注册bean;<bean>
        2)、注解注册Bean;@Service、@Component、@Bean、xxx
    2)、Spring容器会合适的时机创建这些Bean
        1)、用到这个bean的时候;利用getBean创建bean;创建好以后保存在容器中;
        2)、统一创建剩下所有的bean的时候;finishBeanFactoryInitialization();
    3)、后置处理器;BeanPostProcessor
        1)、每一个bean创建完成,都会使用各种后置处理器进行处理;来增强bean的功能;
            AutowiredAnnotationBeanPostProcessor:处理自动注入
            AnnotationAwareAspectJAutoProxyCreator:来做AOP功能;
            xxx....
            增强的功能注解:
            AsyncAnnotationBeanPostProcessor
            ....
    4)、事件驱动模型;
        ApplicationListener;事件监听;
        ApplicationEventMulticaster;事件派发:
posted @ 2021-07-06 23:14  来一杯面向对象的茶  阅读(170)  评论(0编辑  收藏  举报
1