Spring框架大概讲一下

在Spring出现之前,企业级Web开发都是采用JavaEE标准EJB来实现的,但是EJB这种实现太重了,并不适合小型Web项目,所以Spring的提出者认为用Java Bean依赖注入的方式就能实现企业级Web开发,而Spring的出现也被成为是传统JavaEE的新起点。
Spring把普通的Java类看作是一个Java Bean,然后通过依赖注入的方式组装一个个Bean,并放入IOC容器中进行管理,再配合AOP切面的特性,使得其大大简化了企业级Web开发的成本,耦合度也更低。
所以在Spring出来之后就慢慢火热,随着他的发展,覆盖了Web应用的方方面面,对各种中间件等也提供了兼容,慢慢成为了一个支持企业级Web开发的全站式框架。
然后有利也有弊,Spring对其他主流框架、中间件都有支持,所以就带来个问题,引入各种框架时,需要加各种配置,慢慢的就变得很麻烦,所以在这个背景下,SpringBoot产生了。我理解的SpringBoot就是,简化了配置,提供了各种场景启动器,例如对不同框架整合的支持时,可以不用那些配置就方便的实现。

JavaEE

首先我们说下什么是J2EE和J2SE。

  • J2SE是Java的基础版本,其实就是JDK(集合、多线程、IO等API)、JVM这些,也就是Java语言的基础。
  • J2EE是Java Web应用的一套标准,是在J2SE的基础上构建的,这套标准就是制定了怎么去创建Web应用,具体来说,这套规定标准包括:Servlet(如何处理Web请求)、JPA(如何处理数据存取)、JMS(如何处理消息队列)等等。我认为J2EE实现的Web应用就是用了Servlet标准来处理Web请求,用JPA来做数据存取,然后还有像JMS来实现消息队列等等,通过这些标准来构建一个企业级的Web应用。

    Java中有个组织叫JCP,就是一群专家,然后他们会对各种标准提案JSR进行审核,如果通过那这个标准就是推荐需要遵守的,J2EE就是一系列JSR的合集,共同构成了Web开发的一套的标准。

JavaEE和Spring

Spring并没有完全超出J2EE,或者说Spring中有些部分是对J2EE标准的封装,以达到更好的实现,例如,SpringMVC其实本质还是Servlet。

这里提一下Servlet规范,Servlet和Servlet容器是相辅相成的,我们用到tomcat、JBoss这些都是实现了Servlet标准的Servlet容器,所以Servlet的接口代码都可以在这些平台上运行,所以从这个角度来说,J2EE和Spring都遵守了Servlet标准。

EJB

把封装了业务逻辑的Java类(相当于一个组件的概念)打包放在服务器上,然后通过RMI(远程方法调用)的方式调用这个封装了业务逻辑的功能块,通过这种方式来构建企业级Web服务。
但很明显,这种方式大大增加了网络开销,并且实现也更复杂。

Java Bean

Spring中用Java Bean替代了EJB,Java Bean其实就是一个普通的Java类,例如一个Service里面封装业务逻辑,这就是Java Bean,我们可以通过依赖注入的方式把他注入到别的类中,这样就可以替代EJB了。

IOC

控制反转的理解

先来说说IOC的含义,控制反转,意思就是原来的A类中使用B类,需要new一个B类的实例,而控制反转之后,在A中并不会直接创建B的实例,而是交给第三方(IOC容器)来实现,在Spring中这个第三方就是IOC容器,也就是说,B创建之后放到了IOC容器中,然后A中需要使用B实例的时候,从IOC容器中取,然后通过依赖注入的方式塞到A中使用。

依赖注入的几种方式

  1. 构造方法注入

    1. @Component
    2. public class MyBeanConstructor {
    3. private AnotherBean anotherBeanConstructor;
    4. @Autowired
    5. public MyBeanConstructor(AnotherBean anotherBeanConstructor){
    6. this.anotherBeanConstructor = anotherBeanConstructor;
    7. }
    8. }
  2. setter注入

    1. @Component
    2. public class MyBeanSet {
    3. private AnotherBean anotherBeanSet;
    4. @Autowired
    5. public void setAnotherBeanSet(AnotherBean anotherBeanSet) {
    6. this.anotherBeanSet = anotherBeanSet;
    7. }
    8. }
  3. 属性注入

平时开发都是用的这种

  1. @Component
  2. public class MyBeanProperty {
  3. @Autowired
  4. private AnotherBean anotherBeanProperty;
  5. }

IOC容器

BeanFactory和ApplicationContext

Spring中主要有两种IOC容器:BeanFactory和ApplicationContext。这两个容器的区别如下:

  1. BeanFactory是基础的容器,实现了最基础的功能,ApplicationContext扩展了BeanFactory,继承了BeanFactory,提供了更多的功能,例如AOP等。
  2. BeanFactory容器创建之后,只是完成了XML配置文件解析到内存的BeanDefinition容器的过程,在调用getBean()的时候才会去创建Bean;而ApplicationContext默认是容器启动之后就会创建所有配置的Bean,如果Bean设置了懒加载,那么会在后面调用getBean()的时候创建,这么做的目的可以在web服务启动的时候就检测了所有的配置,而不是运行过程中使用才出错。

    IOC容器的启动过程

    对于ApplicationContext容器来说,启动的过程可以分为两大部分:

  3. XML解析成内存中的BeanDefinition,并放入一个Map中;

  4. 把BeanDefinition解析成Bean,并放入三级缓存。

    BeanFactory容器启动只是完成了第一步,在调用getBean()的时候,创建Bean。

image.png

上图是ApplicationContext容器启动的过程,我认为比较重要是两个,一个是obtainFreshBeanFactory()这里会实现BeanDefinition的装载;一个是finishBeanFactoryInitialization()这里会实现Bean的创建,主要关注Bean的创建。

XML->BeanDefinition

  1. Resource资源定位,把资源解析为BeanDefinition;
  2. 把资源解析为BeanDefinition,并加载到内存;
  3. 向IOC容器中注册BeanDefinition(注册的时候会把BeanDefinition放入一个Map中,在方法DefaultListableBeanFactory.registerBeanDefinition()中)。

    BeanDefinition->Bean/Bean生命周期

    如上文所说,Bean的生命周期是从finishBeanFactoryInitialization()开始的,调用过程如下。

    1. AbstractApplicationContext.finishBeanFactoryInitialization()->
    2. beanFactory.preInstantiateSingletons()->
    3. AbstractBeanFactory.getBean()->
    4. AbstractAutowireCapableBeanFactory.doCreateBean()-> 流程的主干

    AbstractAutowireCapableBeanFactory.doCreateBean()是创建Bean的核心,整个Bean生命周期如下。doCreateBean()主要关注三个部分就可以了:

  4. 实例化Bean。createBeanInstance()方法

  5. 属性注入。populateBean()方法
  6. 处理Bean初始化之后的各种事件(就是设置属性值后面的各种回调方法)。initializeBean()方法

image.png

实例化Bean createBeanInstance

这里总的来说就是,基于BeanDefinition,通过forName()方法加载Class对象,然后通过newInstance()方法来创建的实例。 PS:就是通过反射来创建Bean

  1. AbstractAutowireCapableBeanFactory.createBeanInstance()-> doCreateBean中会调用过来
  2. AbstractAutowireCapableBeanFactory.instantiateBean()->
  3. SimpleInstantiationStrategy.instantiate()->
  4. BeanUtils.instantiateClass()-> 这里通过反射的方式实例化了Bean

属性注入 populateBean

属性注入是在AbstractAutowireCapableBeanFactory.populateBean()方法中的,这里面会对@Autowired注解等进行解析,最终调用applyPropertyValues()方法完成注入,本质是通过反射实现的

Bean初始化之后的各种事件 initializeBean

initializeBean()方法里面就是对各种回调方法的处理,Bean如果实现了这些接口,就会去执行,还有就是执行配置的init方法
如果Bean是单例的,那么就会交给IOC容器管理(放入一级缓存),并且容器还要负责Bean的销毁,如果Bean有配置销毁方法和接口,那么在销毁之前还会进行调用;如果Bean是多例的,那么直接交给创建线程就可以了。

如何解决循环依赖

Spring解决循环依赖问题主要是依靠三级缓存来实现的。具体来说是这样的:
在实例化Bean对象createBeanInstance()和属性注入populateBean()之间,有一个addSingletonFactory()的方法,这个方法会把刚刚创建的Bean实例放入三级缓存singletonFactories中,此时的Bean只完成了实例化,相当于只分配了内存和赋初值,属性还没有注入。但通过放入三级缓存的操作,提前暴露了这个Bean,缓存中持有了这个Bean的引用,为后面依赖的Bean注入提供了条件。
例如:当有两个Bean A和B相互依赖。我们先创建A,在实例化A之后,将A放入了三级缓存,然后为A注入属性的时候,发现需要B,B还未创建,这时候去创建B,B走到属性注入的时候,因为A已经放在了三级缓存,所以B是可以通过缓存来一级一级获取到A的,这样B中注入A就可以实现了,并且最后会把B放入一级缓存,然后在返回去继续为A注入属性,A就可以从一级缓存中找到B,这样就可以解决循环依赖了。
三级缓存分别是:单例Bean缓存、Bean的早期引用缓存、Bean工厂的缓存。单例Bean缓存很重要,因为后面从容器中获取Bean就是从这里获取的;Bean工厂的缓存也很重要,因为这是解决循环依赖的关键,提前暴露的Bean就是放在这里的。
二级缓存(Bean的早期引用缓存)好像不是很重要,但二级缓存能够提高加载的效率,避免每次都从Bean工厂缓存中获取

Spring只能解决setter注入或属性注入的单例Bean的循环依赖问题,不能解决构造器注入或多例Bean的循环依赖问题。不能解决会直接抛出异常。

Bean作用域

默认Bean是单例的,也可以设置成多例、request(一个请求一个Bean)、session(一个session一个Bean)、global session(全局的session)。

我们的service、controller这些都是单例的,也就是说所有的请求都调用这个实例对象,所以会存在线程安全问题,那这个时候,我们需要尽量避免使用实例变量,或者注意加锁这些处理。

@Autowird和@Resource注解

  1. @Autowird是Spring的注解,@Resource是JDK自带的注解,但是Spring也支持这个注解
  2. @Autowird是按照类型注入的,如果要想按照名称注入,@Autowird配合@Qualifier
  3. @Resource中使用参数name就是用名称注入,使用参数type就是用类型注入

    AOP

    OOP和AOP的区别

    OOP面向对象,更侧重于将业务中的实体和属性这些进行封装,也就是说可能,更符合对象的思想,把对象的东西都封装整合起来放在一个类中,但这种方式不好处理一些横向的问题,或者说一些对象都要具有的功能时,OOP可能就需要在所有的对象中都封装这个具体的功能。
    这个时候AOP就能更好地发挥作用了,AOP是面向切面的,更适合解决这种横向问题。

    AOP实现原理

    Spring中通过后置处理器来实现对Bean的封装代理,通过动态代理来实现AOP。
    对于接口实现的,用JDK动态代理,JDK动态代理本质是通过反射来实现的;不是接口实现这种的,就用CGLIB来实现代理,本质是通过读入代理类的class文件,然后修改字节码,生成代理子类来实现的。

    JDK动态代理只能对实现接口的类进行代理;CGLIB动态代理是通过生成子类并重写父类方法来实现代理的,所以代理类不能用final修饰。

MVC处理请求的过程

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)并返回给DispatcherServlet
  4. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
  5. 执行处理器(Controller,也叫后端控制器)
  6. Controller执行完成返回ModelAndView
  7. HandlerAdapter将controller执行结果(ModelAndView)返回给DispatchServlet
  8. DispatchServlet将ModelAndView传给ViewResolver视图解析器(如果加上@responsebody注解,则返回值不通过viewResolver,而是直接返回object)
  9. ViewResolver解析后返回具体View
  10. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
  11. DispatcherServlet响应用户